Storybook
https://gyazo.com/13c6a9198bdcb3af88bc0b69bce2cbc9
概要
テストを行うことも可能
実行速度と本番環境の再現度の観点から見て、jsdom と E2E の中間に位置する インタラクションテスト
セットアップ
インストール
code:sh
$ npx storybook init
インストールが完了後、package.json が更新されていることが確認できる
依存関係
code:package.json
{
"devDependencies": {
...
"storybook": "^8.2.9",
"@chromatic-com/storybook": "^1.8.0",
"@storybook/addon-essentials": "^8.2.9",
"@storybook/addon-interactions": "^8.2.9",
"@storybook/addon-links": "^8.2.9",
"@storybook/addon-onboarding": "^8.2.9",
"@storybook/blocks": "^8.2.9",
"@storybook/react": "^8.2.9",
"@storybook/react-vite": "^8.2.9",
"@storybook/test": "^8.2.9",
...
}
}
スクリプト
code:package.json
{
...
"script": {
...
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
}
}
storybook: ローカル Storybook サーバを起動する
build-storybook: ドキュメントを HTML・CSS・JavaScript にコンパイルして、デプロイできる状態にする
.storybook/ と src/stories/ ディレクトリも作成される
.storybook/: Storybook の設定
src/stories/: Storybook ドキュメント(Story)
Story
Storybook のコアとなるコンセプト
A story captures the rendered state of a UI component.
(Story は、UI コンポーネントのレンダリングされた状態をキャプチャしたものです。)
Developers write multiple stories per component that describe all the “interesting” states a component can support.
(開発者は、コンポーネントがサポートするすべての「興味深い」状態を説明するため、1 つのコンポーネントに対して複数の Story を記述する。)
e.g.
code:Carousel.stories.tsx
import type { Meta, StoryObj } from "@storybook/react";
import Carousel from "../Carousel";
import slides from "../examples/slides";
const meta = {
title: "Example/Carousel",
component: Carousel,
} satisfies Meta<typeof Carousel>;
export default meta;
type Story = StoryObj<typeof Carousel>;
export const Basic: Story = {
args: {
slides,
},
};
アドオン
Storybook は様々なアドオンを用いてドキュメントをよりリッチにできる
npx storybook init 実行時点で以下のアドオンがセットされている
code:.storybook/main.ts
const config: StorybookConfig = {
...
addons: [
"@storybook/addon-onboarding",
"@storybook/addon-links",
"@storybook/addon-essentials",
"@chromatic-com/storybook",
"@storybook/addon-interactions",
],
...
};
よく使われるアドオンをまとめたもので、ほとんどのプロジェクトに適したデフォルトの設定が組み込まれている
よく使われるアドオン
Actions タブから、イベントハンドラの引数で受け取ったデータをログが確認できる
https://scrapbox.io/files/66daa84d83280c0022aa6aa6.png
有効にするには、@storybook/test の fn スパイ 関数を、Story の Meta の args として渡す必要がある code:Carousel.stories.tsx
const meta = {
title: "Example/Carousel",
component: Carousel,
args: { onSlideIndexChange: fn() },
} satisfies Meta<typeof Carousel>;
v8 から少し挙動が変わったみたい radish-miyazaki.icon
前は on で始まる Props は自動で有効になってた
Controls タグを開くと各プロパティの現在の値が表示され、リアルタイムに変更できる
https://scrapbox.io/files/66daa8acaf9a14001db6091f.png
プロパティの型からコントロールの種類が自動で推論される
Controls アドオンの注意点
Story には、Storybook 自体の内部状態(args)と Component の内部状態の 2 つの状態が存在する
Controls タブからプロパティを変更すると args が更新され、Component の内部状態が上書きされる
これにより、状態を変更するインタラクティブな操作が動かなくなる
https://scrapbox.io/files/66dab238442de2001c207398.mov
解決法
Storybook のデフォルトのレンダリングロジックを上書きする render 関数を作成する
code:Carousel.stories.tsx
import { useArgs } from "@storybook/preview-api";
export const Basic: Story = {
args: {
slides,
},
render: function RenderCarousel(args) {
const updateArgs = useArgs();
return (
<Carousel
{...args}
onSlideIndexChange={(newSlideIndex) => {
updateArgs({ slideIndex: newSlideIndex });
args.onSlideIndexChange?.(newSlideIndex);
}}
/>
);
},
};
前準備
package.json の build-storybook スクリプトに -o dist/storybook という引数を追加する
code:package.json
{
"scripts": {
...
"build-storybook": "storybook build -o dist/storybook"
}
}
.github/workflows/static.yml を作成する
code:.github/workflows/static.yml
name: Build and Publish Storybook to GitHub Pages
on:
push:
branches:
- "main"
permissions:
contents: read
pages: write
id-token: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- id: build-publish
uses: bitovi/github-actions-storybook-to-github-pages@v1.0.3
Settings タブ → Pages メニューを選択し、Build and deployment の項の Source を GItHub Actions に変更する https://scrapbox.io/files/66dacb737dc45c001d98c38a.png