NotificationBar

処理中
ドラッグして幅を変更
ドラッグして高さを変更
import { Story } from '@storybook/react'
import React, { ComponentProps, useState } from 'react'
import styled, { css } from 'styled-components'
import { Button } from '../Button'
import { CheckBox } from '../CheckBox'
import { Cluster, Stack } from '../Layout'
import { LineClamp } from '../LineClamp'
import { RadioButton } from '../RadioButton'
import { Text } from '../Text'
import { TextLink as shrTextLink } from '../TextLink'
import { NotificationBar, messageTypes } from './NotificationBar'
export default {
title: 'States(状態)/NotificationBar',
component: NotificationBar,
parameters: {
withTheming: true,
},
}
const TextLink = styled(shrTextLink)`
color: inherit;
&:hover {
color: inherit;
}
`
const Actions = () => (
<>
<Button size="s">編集</Button>
<TextLink href="#">
<Text size="S">ヘルプ</Text>
</TextLink>
</>
)
export const all: Story = () => {
const onClose = () => console.log('close')
return (
<Wrapper>
<Stack gap={0.5}>
<dt>メッセージの種類</dt>
<Stack gap={0.25} as="dd">
<NotificationBar type="info" message="バックグラウンド処理中です。" onClose={onClose} />
<NotificationBar
type="success"
message="タスクの削除に成功しました。"
onClose={onClose}
/>
<NotificationBar
type="warning"
message="評価開始中にタスクの削除を実行すると、情報の整合性が取れなくなる可能性があります。"
onClose={onClose}
/>
<NotificationBar type="error" message="タスクの削除に失敗しました。" onClose={onClose} />
</Stack>
</Stack>
<Stack gap={0.5}>
<dt>強調</dt>
<Stack gap={0.25} as="dd">
<NotificationBar
type="success"
bold
message="タスクの削除に成功しました。"
onClose={onClose}
/>
<NotificationBar
type="warning"
bold
message="評価開始中にタスクの削除を実行すると、情報の整合性が取れなくなる可能性があります。"
onClose={onClose}
/>
<NotificationBar
type="error"
bold
message="タスクの削除に失敗しました。"
onClose={onClose}
/>
</Stack>
</Stack>
<Stack gap={0.5}>
<dt>その他</dt>
<Stack gap={0.25} as="dd">
<NotificationBar
type="info"
message="ボタンやリンクを渡せます。表じゃなくなるのでborder-collapseプロパティー等に影響があるけど、子のtd要素等にはその影響は波及しないのであまり問題もなさそう。"
onClose={onClose}
>
<Actions />
</NotificationBar>
<NotificationBar type="warning" message="onClose を省略すると、閉じるボタンが消えます" />
<NotificationBar
type="success"
message={
<LineClamp maxLines={1} withTooltip>
非推奨ですが、LineClamp
を使用して省略もできます。そのでっかい領域によるヘッダー(のナビゲーション)へのアクセス性の低下をフォローするために多くのウェブサイトはヘッダーを固定しちゃうわけなんだけど、それでは同時にコンテンツの閲覧性に影響を与えてしまう。
</LineClamp>
}
onClose={onClose}
/>
</Stack>
</Stack>
</Wrapper>
)
}
const Wrapper = styled(Stack).attrs({ as: 'dl', gap: 1.5 })`
${({ theme: { color, spacingByChar } }) => css`
margin-block: unset;
background-color: ${color.BACKGROUND};
padding: ${spacingByChar(1.5)};
dd {
margin-inline-start: unset;
}
`}
`
export const Demo: Story = () => {
const [visible, setVisible] = useState(false)
const [animate, setAnimate] = useState(true)
const [messageType, setMessageType] = useState<(typeof messageTypes)[number]>('success')
const [bold, setBold] = useState(true)
return (
<DemoWrapper>
{visible && (
<NotificationBar
type={messageType}
bold={bold}
message="NotificationBar が表示されました"
animate={animate}
/>
)}
<Center>
<Stack>
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label>
<CheckBox name="bold" onChange={() => setBold(!bold)} checked={bold}>
bold
</CheckBox>
</label>
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label>
<CheckBox name="animate" onChange={() => setAnimate(!animate)} checked={animate}>
animate
</CheckBox>
</label>
<Stack as="fieldset">
<Text as="legend">メッセージの種類</Text>
<Cluster gap={0.75}>
{messageTypes.map((type) => (
<label key={type}>
<RadioButton
name="message_type"
value={type}
checked={messageType === type}
onChange={({ currentTarget: { value } }) =>
setMessageType(value as ComponentProps<typeof NotificationBar>['type'])
}
>
{type}
</RadioButton>
</label>
))}
</Cluster>
</Stack>
<Button onClick={() => setVisible(!visible)}>
NotificationBar を{visible ? '隠す' : '表示'}
</Button>
</Stack>
</Center>
</DemoWrapper>
)
}
Demo.parameters = {
layout: 'fullscreen',
withTheming: true,
}
const DemoWrapper = styled.div`
${({ theme: { color } }) => css`
position: relative;
overflow: auto;
background-color: ${color.BACKGROUND};
`}
`
const Center = styled.div`
display: flex;
align-items: center;
justify-content: center;
height: 50vh;
`
処理中

使いどころ

NotificationBarは、フィードバックやメッセージを伝えるために使います。

  • 操作後に、ダイアログ内や画面全体が切り替わるとき
  • ダイアログで操作し、元の画面に戻るとき
  • 特別で重要な情報やお知らせを表示するとき

種類

メッセージで伝えたいことや、メッセージの重要度に合わせて4種類を定義しています。

状態

強調

ユーザーへのアテンションを高めたい場合は、bold={true}にすることを検討します。

アニメーション

メッセージが表示されたことを強調したい場合は、animate={true}にすることでスライドインのアニメーション効果を追加できます。

レイアウト

非推奨ですがLineClampコンポーネントも使えます。

その他

ボタンやテキストリンクを表示したり、常時表示したいときは「閉じる」ボタンを非表示にできます。

使い方

操作のフィードバック

主に「操作後に、ダイアログ内や画面全体が切り替わるとき」や「ダイアログで操作し、元の画面に戻るとき」などに使います。

  • ユーザーにメッセージが表示されたことを強調するために、アニメーションを有効にします。
  • ユーザーの明示的な操作で非表示にします。時間の経過で閉じることは非推奨されません(※1)。

ダイアログからコレクションにオブジェクトを新規追加した後のメッセージ

コレクションにオブジェクトを追加した際に、フィードバックを表示する典型的な例です。
以下の例では操作が成功したためtype="success"にしていますが、エラーの場合はerrorを指定してメッセージを返します。

スクロール追従するNotificationBar

操作するボタンなどと、NotificationBarによるメッセージの距離が離れている場合は、positon: stickyでスクロールに追従させ、メッセージに気づきやすくすることを推奨します。 以下の例では、スクロールに関わらずNotificationBarを画面上部に固定表示しています。

特別な情報やお知らせ

主に「特別で重要な情報やお知らせを出したいとき」に使うケースです。

  • 基本的に、bold={false}で使うことを推奨します。破壊的な変更や必ず見てほしい重要情報など必要に応じて強調してください。
  • 特別な情報やお知らせで設置するコンポーネントと表示位置は、情報構造に合わせて設計します。NotificationBarでの代表的な使い方は以下を参照してください。

アプリケーション全体にメッセージを表示する

アプリケーション全体にメッセージを表示している例です。
アプリケーション内で画面を切り替えてもメッセージが表示され続けることを表すため、Headerよりも上に配置します。また、必ず見てもらいたいメッセージなのでbold={true}にしています。

NotificationBarを使ってアプリケーション全体へメッセージを表示している例

ページにメッセージを表示する

ページ内にメッセージを表示している例です。
「ダッシュボード」の配下のみに関わる情報であることを表すために、AppNaviの下にNotificationBarを配置します。

NotificationBarを使ってページ内にメッセージを表示している例

Props

type必須
"error""warning""info""success"

メッセージの種類

bold
falsetrue

強調するかどうか

animate
falsetrue

スライドインするかどうか

message必須
stringnumberfalsetrueReactElement<any, string | JSXElementConstructor<any>>ReactFragmentReactPortal

メッセージ

onClose
() => void

閉じるボタン押下時に発火させる関数

children
stringnumberfalsetrueReactElement<any, string | JSXElementConstructor<any>>ReactFragmentReactPortal

アクション群

role
"alert""status"

role 属性

参考文献

※1. Web Content Accessibility Guidelines - 2.2.1 タイミング調整可能を理解する