ことりずかんのカードは、状態によって見た目が変わります。
- お気に入り済み(★)と、まだ(☆)
- メモがある鳥と、ない鳥(「せつめいはじゅんび中」)
- レアの印が付く鳥と、付かない鳥
- 名前が長い鳥(レイアウトが崩れないか)
これを画面から確かめようとすると面倒です。メモが空の鳥を出すためにJSONを書きかえ、確認したら戻す。名前が長いときの確認は、たいてい忘れます。
Storybookは、部品を状態ごとに並べておくカタログです。
入れる
npx storybook@latest init質問に答えると、プロジェクトを見て(Vite+React+TypeScriptだと判定して)設定まで書いてくれます。追加されるのはこのあたりです。
.storybook/
├── main.ts どのファイルをストーリーとして読むか、アドオンの設定
└── preview.tsx 全ストーリー共通の設定(CSSの読みこみなど)
src/stories/ サンプル(消してよい)package.jsonにも2つ増えます。
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"Tailwindを使っているなら、preview.tsxの先頭でCSSを読ませます。これを忘れるとStorybookの中だけスタイルが当たりません。
import '../src/index.css'ストーリーを書く
BirdCardのストーリーです。部品と同じフォルダに置くのが今の主流です。
import type { Meta, StoryObj } from '@storybook/react-vite'
import { MemoryRouter } from 'react-router-dom'
import { fn } from 'storybook/test'
import BirdCard from './BirdCard.tsx'
const meta = {
title: 'ことりずかん/BirdCard',
component: BirdCard,
decorators: [
(Story) => (
<MemoryRouter>
<div className="max-w-md bg-sky-50 p-6">
<Story />
</div>
</MemoryRouter>
),
],
args: {
bird: { id: 4, name: 'フクロウ', emoji: '🦉', area: 'もり', rare: 3, memo: 'よるにおきてる' },
isFavorite: false,
onToggleFavorite: fn(),
},
} satisfies Meta<typeof BirdCard>
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
name: '通常',
}
export const Favorite: Story = {
name: 'お気に入り済み',
args: { isFavorite: true },
}
export const NoMemo: Story = {
name: 'メモなし',
args: {
bird: { id: 2, name: 'スズメ', emoji: '🐦', area: 'こうえん', rare: 1, memo: '' },
},
}
export const LongName: Story = {
name: '名前が長い',
args: {
bird: { id: 1, name: 'キタノモリノエナガ', emoji: '🐤', area: 'もり', rare: 2, memo: 'わざと長い名前' },
},
}読み方はこうです。
| 書くもの | 意味 |
|---|---|
export default meta | この部品の共通設定。ここのargsが全ストーリーの初期値 |
export const Default | 1つのストーリー=1つの状態。nameがサイドバーに出る(省くと変数名がそのまま出ます) |
args | 渡すprops。metaのargsに上書きされる形で足りない分だけ書く |
decorators | ストーリーを包む枠。<Link>を使う部品にはRouterが要る |
動かす
npm run storybookhttp://localhost:6006が開きます。左のサイドバーにことりずかん/BirdCardが出て、その下に4つの状態が並びます。
見どころは下のパネルです。
| パネル | できること |
|---|---|
| Controls | propsをその場で書きかえられる。isFavoriteをFalse/Trueで切りかえるなど |
| Actions | fn()を渡したpropsがいつ呼ばれたかが出る |
| Accessibility | 自動でアクセシビリティ検査。BirdCardはViolations 0 / Passes 13 / Inconclusive 1でした |
Controlsが効くのは、argsでpropsを渡しているストーリーだけです(部品の中に値を直書きすると効きません)。これは「propsで受け取る」設計を自然に促す仕組みでもあります。
どこで効くのか
正直に言うと、1人で小さいものを作っている間は要りません。効くのはこういう場面です。
| 場面 | 効き方 |
|---|---|
| 状態の組み合わせが多い部品 | エラー・空・読みこみ中を画面から作らずに見られる |
| デザイナーやディレクターに見せる | build-storybookで静的サイトとして配れる。実装環境が要らない |
| 共通部品を作る | ボタンやモーダルの全パターンが1か所にそろう |
| レビュー | 「この状態を見たい」にURLで答えられる |
| リファクタ | 部品を書きかえたあと、状態を一巡して見比べられる |
逆に、画面をそのまま見れば分かる部品にストーリーを書くのは手間だけです。書くのは「画面から再現しにくい状態がある部品」に絞ります。
npm run build-storybook # storybook-static/ に書き出される書き出したものはただの静的サイトなので、公開の回と同じやり方でそのまま置けます。
Reactテストを書く(Vitest + Testing Library)テストの回。Storybookのストーリーは、そのままテストの入力としても使えます公式ドキュメントも見てみる
Storybook外部サイトStorybook: Frontend workshop for UI developmentStorybook公式の「ストーリーを書く」。metaとargsの関係、decoratorsの使い方はここが正確です(英語)このレッスンのまとめ
- Storybookは部品を状態ごとに並べたカタログ。画面から作りにくい状態を固定できる
npx storybook@latest initで設定まで入る。生成されたサンプルは消してよい- Tailwindを使うなら
preview.tsxでCSSを読ませる - 1ファイル=1部品。
export default meta+ストーリーごとのexport argsが渡すprops。関数のpropsにはfn()を渡すとActionsに記録される<Link>やContextを使う部品は、decoratorsでProviderに包む- Accessibilityパネルで自動検査が付いてくる(
Violations 0を目指す) - 効くのは状態が多い部品・人に見せるとき。全部品に書く必要はない
次は、Tailwind以外のスタイリングです。案件で当たったときに読めるようにします。