StoryBook memo

🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪
StoryBook Memo
🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪

Intro to Storybook

npx storybook@latest init
インストールが完了したら、
.storybook
と src/stories
のディレクトリが作成され、
package.json に storybook と build-storybook のコマンドが追加される
"scripts": {
・・・
+ "storybook": "start-storybook -p 6006",
+ "build-storybook": "build-storybook"
},
npm run storybook
ブラウザが立ち上がった。簡単。

サンプルである stories
フォルダにある、Button.stories.tsx
を参考にストーリーの書き方をメモ。
-
args
(略称:アーグ) -> Reactのprops、Vueのprops、Angularの@Input、その他同様の概念の総称をStorybookではargsと呼ぶ。 - ストーリーの定義は、ES6モジュールをベースとした
CSF(Component Story Format)
に従って書かれる。

storyってなんだ?
あるコンポーネントの例を特定の状態でレンダリングするコードスニペットのこと。
CSF
に則ったストーリーの書き方
CSF(Component Story Format)
Storybook v5.2 にて導入された新しいストーリーのフォーマット。
ES6 モジュールに基づいた記法のため、Storybook を越えて移植可能であるもの。
ES6モジュールが存在する場所ならどこでも簡単に利用できるため、
Jest や Testing Library とも組み合わせることが簡単にできる。
export default { title: 'Counter' };
export const enabled = () => (
<Counter text="Enabled" />
);
export const disabled = () => (
<Counter disabled text="Disabled" />
);
import { render, fireEvent } from '@testing-library/react';
import { enabled, disabled } from './Button.stories';
describe('counter interactivity', () => {
it('should increment when enabled', () => {
const comp = render(enabled());
fireEvent.click(comp.getByText('Enabled: 0'));
expect(comp.getByText('Enabled: 1')).toBeTruthy();
});
it('should do nothing when disabled', () => {
const comp = render(enabled());
fireEvent.click(comp.getByText('Disabled: 0'));
expect(comp.getByText('Disabled: 0')).toBeTruthy();
});
});
また、Storybook v6.4以降はCSF3.0に準じた記述方法に変更されている
(以下はCSF 2.0に準じた記述方法)
01. Default export
Storybookがストーリーを一覧表示する方法と、アドオンが使用する情報(メタデータ)を export default
する
export default {
// ブラウザで表示するタイトル
// スラッシュで区切ることで階層を表現することができる。
title: 'Example/Button',
// .storybook\main.jsの"stories"設定とマッチするファイルを探します。
component: Button,
// ArgTypesはArgsの振る舞いを指定するためのStorybookのファーストクラスの機能です。
// Argの型を指定することで、Argが取り得る値を制約し、明示的に設定されていない(つまり、必須ではない)Argに関する情報を提供します。
argTypes: {
backgroundColor: { control: 'color' },
},
} as ComponentMeta<typeof Button>;
02. Defining stories
名前付きエクスポートを使用して、コンポーネントのストーリーを定義
エクスポートの名前はすべて大文字で始めることを推奨。
export const Primary: ComponentStory<typeof Button> = () => (
<Button backgroundColor="#ff0" label="Button" />
);
export const Secondary: ComponentStory<typeof Button> = () => (
<Button backgroundColor="#ff0" label="😄👍😍💯" />
);
export const Tertiary: ComponentStory<typeof Button> = () => (
<Button backgroundColor="#ff0" label="📚📕📈🤓" />
);
03. args
の使用
コンポーネントのストーリーのための引数(Args、Reactで言うProps)を導入。
templateとしてbindする事でPropsに応じたcomponentを表示する。
const Template: ComponentStory<typeof Button> = (args) => <Button {...args} />;
export const Primary = Template.bind({});
Primary.args = { backgroundColor: '#ff0', label: 'Button' };
export const Secondary = Template.bind({});
Secondary.args = { ...Primary.args, label: '😄👍😍💯' };
export const Tertiary = Template.bind({});
また引数はインポートして他のコンポーネントのストーリーを書くときに再利用することができるため複合コンポーネント作成やテストの際に便利。
import { ButtonGroup } from '../ButtonGroup';
// ButtonストーリーのArgs(Props)をインポートする
import * as ButtonStories from './Button.stories';
export default {
title: 'ButtonGroup',
component: ButtonGroup,
};
const Template: ComponentStory<typeof Button> = (args) => <ButtonGroup {...args} />;
export const Pair = Template.bind({});
Pair.args = {
buttons: [
{ ...ButtonStories.Primary.args },
{ ...ButtonStories.Secondary.args }
],
orientation: 'horizontal',
};

04.parameters
ストーリーに関する静的な名前付きメタデータのセットで、通常、Storybookの機能やアドオンの動作を制御するために使用する。
Story parameters
CSFエクスポートのparameters
キーを使って、1つのストーリーにパラメータを設定することができる
export const Primary = Template.bind({});
Primary.args = {
primary: true,
label: 'Button',
};
Primary.parameters = {
backgrounds: {
values: [
{ name: 'red', value: '#f00' },
{ name: 'green', value: '#0f0' },
{ name: 'blue', value: '#00f' },
],
},
};
Component parameters
デフォルトのCSFエクスポートで parameters
キーを使用して、コンポーネントのすべてのストーリーのパラメータを設定することができます。
export default {
title: 'Button',
component: Button,
parameters: {
backgrounds: {
values: [
{ name: 'red', value: '#f00' },
{ name: 'green', value: '#0f0' },
{ name: 'blue', value: '#00f' },
],
},
},
};
Global parameters
.storybook/preview.js
ファイル のパラメータエクスポートを介して、すべてのストーリーのパラメータを設定することができる。
// .storybook/preview.js
export const parameters = {
backgrounds: {
values: [
{ name: 'red', value: '#f00' },
{ name: 'green', value: '#0f0' },
],
},
};
パラメータ継承のルール
- より具体的なパラメータが優先される(ストーリーパラメータ > コンポーネントパラメータ > グローバルパラメータ)
- パラメータはマージされるため、キーは上書きされる。(削除はされない)

05.Decorators
decoratorsを記述することで、storyがレンダリングされる際に任意の要素でwrapすることができる。
-
ストーリーがテスト対象のコンポーネントの「純粋な」レンダリングであることを確認し、余分な HTML やコンポーネントが「純粋な」レンダリングを汚染しないようにするために有用である。
-
パラメータと同様に、デコレータもグローバル、コンポーネントレベル、および単一のストーリーに対して定義することができる。
-
ストーリーに関連するすべてのデコレーターは、ストーリーがレンダリングされると、
Global decorators > Component decorators > Story decorators
の順序で実行されます。

Storybookにおけるテスト手法

インタラクションテスト
Visual Regression Test(画像回帰テスト)
@axe-core/playwright による a11y テスト

Visual Regression Test(VRT: 画像回帰テスト)
導入するモチベ
- VRT を導入するモチベーションは、改修による予期せぬ UI のデザイン崩れ(いわゆるデグレ)を素早く見つける
- VRT で大部分の UI を守りながら、E2E テストや手動テストによってとくに重要な小量の UI を守る
♣️reg-suit + Storycap
reg-suit -> 画像の差分検出を行う CLI (reg-suit 自体には画像を保存する機能はない)
Storycap -> Storybook の内容を画像として保存できる Storybook Addon
<導入事例>
♣️Chromatic・Playwright

interaction-testing(インタラクションテスト)
インタラクションテストは、UIにおけるデータの取得・状態変化などの機能的な側面を検証することができる。(ユーザーの操作に合わせてアプリケーションが予期した通りに動くかをテスト)
公式Docsより参照
インタラクションテストの手順
- コンポーネントの初期状態を設定するためのストーリーを書く
- play関数を使用してユーザーの行動をシミュレート
- テストランナーを使用して、コンポーネントが正しくレンダリングされ、play機能とのインタラクションテストがパスすることを確認 (コマンドラインや CI環境からテスト実行を自動化も可能)
play
関数 : ストーリーのレンダリング終了後に実行される小さなコードスニペットです。ユーザーのワークフローをテストするために使用することができます。(テストは、StorybookにインストされたバージョンのJestと Testing Libraryを使用して書かれています。)
Storybook/addon-interactions : Storybookのテストを視覚化し、ブラウザベースのデバッグに便利なプレイバックインターフェイスを提供します。
storybook/test-runner : Jestと Playwrightを搭載したスタンドアローンのユーティリティで、インタラクションテストをすべて実行し、壊れたストーリーをキャッチします。