Closed13

StoryBook memo

ピン留めされたアイテム
ya7ya7

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

ya7ya7

Intro to Storybook

ya7ya7
npx storybook@latest init 

インストールが完了したら、
.storybooksrc/stories のディレクトリが作成され、
package.json に storybook と build-storybook のコマンドが追加される

package.json
  "scripts": {
 ・・・
+     "storybook": "start-storybook -p 6006",
+     "build-storybook": "build-storybook"
  },
npm run storybook

ブラウザが立ち上がった。簡単。

ya7ya7

サンプルである storiesフォルダにある、Button.stories.tsx を参考にストーリーの書き方をメモ。

  • args(略称:アーグ) -> Reactのprops、Vueのprops、Angularの@Input、その他同様の概念の総称をStorybookではargsと呼ぶ。
  • ストーリーの定義は、ES6モジュールをベースとした CSF(Component Story Format) に従って書かれる。

https://storybook.js.org/docs/react/writing-stories/introduction#default-export

ya7ya7

storyってなんだ?

あるコンポーネントの例を特定の状態でレンダリングするコードスニペットのこと。

CSFに則ったストーリーの書き方

CSF(Component Story Format)
Storybook v5.2 にて導入された新しいストーリーのフォーマット。
ES6 モジュールに基づいた記法のため、Storybook を越えて移植可能であるもの。

https://storybook.js.org/blog/component-story-format/

ES6モジュールが存在する場所ならどこでも簡単に利用できるため、
Jest や Testing Library とも組み合わせることが簡単にできる。

Button.stories.tsx
export default { title: 'Counter' };

export const enabled = () => (
  <Counter text="Enabled" />
);

export const disabled = () => (
  <Counter disabled text="Disabled" />
);
Button.spec.tsx
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に準じた記述方法に変更されている

https://storybook.js.org/blog/component-story-format-3-0/

(以下はCSF 2.0に準じた記述方法)

01. Default export

Storybookがストーリーを一覧表示する方法と、アドオンが使用する情報(メタデータ)を export default する

Button.stories,tsx
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

名前付きエクスポートを使用して、コンポーネントのストーリーを定義
エクスポートの名前はすべて大文字で始めることを推奨。

Button.stories,tsx
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を表示する。

Button.stories.tsx
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({});

また引数はインポートして他のコンポーネントのストーリーを書くときに再利用することができるため複合コンポーネント作成やテストの際に便利。

ButtonGroup.stories.tsx
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',
};
ya7ya7

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' },
    ],
  },
};

パラメータ継承のルール

  • より具体的なパラメータが優先される(ストーリーパラメータ > コンポーネントパラメータ > グローバルパラメータ)
  • パラメータはマージされるため、キーは上書きされる。(削除はされない)
ya7ya7

05.Decorators

decoratorsを記述することで、storyがレンダリングされる際に任意の要素でwrapすることができる。

  • ストーリーがテスト対象のコンポーネントの「純粋な」レンダリングであることを確認し、余分な HTML やコンポーネントが「純粋な」レンダリングを汚染しないようにするために有用である。

  • パラメータと同様に、デコレータもグローバル、コンポーネントレベル、および単一のストーリーに対して定義することができる

  • ストーリーに関連するすべてのデコレーターは、ストーリーがレンダリングされると、Global decorators > Component decorators > Story decoratorsの順序で実行されます。

ya7ya7

Storybookにおけるテスト手法

ya7ya7

インタラクションテスト

Visual Regression Test(画像回帰テスト)

@axe-core/playwright による a11y テスト

ya7ya7

Visual Regression Test(VRT: 画像回帰テスト)

導入するモチベ

  • VRT を導入するモチベーションは、改修による予期せぬ UI のデザイン崩れ(いわゆるデグレ)を素早く見つける
  • VRT で大部分の UI を守りながら、E2E テストや手動テストによってとくに重要な小量の UI を守る

♣️reg-suit + Storycap

reg-suit -> 画像の差分検出を行う CLI (reg-suit 自体には画像を保存する機能はない)

Storycap -> Storybook の内容を画像として保存できる Storybook Addon

https://github.com/reg-viz/storycap#managed-mode

<導入事例>

https://buildersbox.corp-sansan.com/entry/2021/03/18/110000

https://blog.recruit.co.jp/rmp/front-end/visual-regression-testing/

♣️Chromatic・Playwright

ya7ya7

interaction-testing(インタラクションテスト)

インタラクションテストは、UIにおけるデータの取得・状態変化などの機能的な側面を検証することができる。(ユーザーの操作に合わせてアプリケーションが予期した通りに動くかをテスト)

公式Docsより参照
https://storybook.js.org/docs/react/writing-tests/interaction-testing

インタラクションテストの手順

  1. コンポーネントの初期状態を設定するためのストーリーを書く
  2. play関数を使用してユーザーの行動をシミュレート
  3. テストランナーを使用して、コンポーネントが正しくレンダリングされ、play機能とのインタラクションテストがパスすることを確認 (コマンドラインや CI環境からテスト実行を自動化も可能)

play関数 : ストーリーのレンダリング終了後に実行される小さなコードスニペットです。ユーザーのワークフローをテストするために使用することができます。(テストは、StorybookにインストされたバージョンのJestと Testing Libraryを使用して書かれています。)

Storybook/addon-interactions : Storybookのテストを視覚化し、ブラウザベースのデバッグに便利なプレイバックインターフェイスを提供します。

storybook/test-runner : Jestと Playwrightを搭載したスタンドアローンのユーティリティで、インタラクションテストをすべて実行し、壊れたストーリーをキャッチします。

このスクラップは2024/12/18にクローズされました