Open10

Storybook

Kazuki MatsudaKazuki Matsuda

useArgs

https://storybook.js.org/docs/writing-stories/args#setting-args-from-within-a-story

UI と Contols を同期する

import { StoryObj, Meta } from '@storybook/react';
import { useArgs } from '@storybook/preview-api';
import { Checkbox } from './checkbox';

const meta: Meta<typeof Checkbox> = {
  title: 'Inputs/Checkbox',
  component: Checkbox,
};
export default meta;

type Story = StoryObj<typeof Checkbox>;

export const Example: Story = {
  args: {
    isChecked: false,
    label: 'Try Me!',
  },
  /**
   * 👇 To avoid linting issues, it is recommended to use a function with a capitalized name.
   * If you are not concerned with linting, you may use an arrow function.
   */
  render: function Render(args) {
    const [{ isChecked }, updateArgs] = useArgs();

    function onChange() {
      updateArgs({ isChecked: !isChecked });
    }

    return <Checkbox {...args} onChange={onChange} isChecked={isChecked} />;
  },
};
Kazuki MatsudaKazuki Matsuda

Stories

  • Args : コンポーネントの Props の設定方法
  • Parameters : ストーリーのメタデータ (背景色の設定など)
  • Decorators : ストーリーを任意のコンポーネントでラップできる
  • Play functions : ストーリーがレンダリングされた後に実行できるコード
  • Loaders : ストーリーがレンダリング前に実行される非同期関数
  • Tags : タグ (テスト対象かどうかなどを設定できる)
  • Naming components and hierarchy : ストーリーの階層と並び替え
  • Mocking data and modules
    • Modules : コンポーネントが使用するモジュールのモック
    • Network requests : MSW を使用したネットワークリクエストのモック
    • Providers : Decorators を使用した Provider のモック
  • Building pages and screens : ページをストーリーで表示する方法
  • Stories for multiple components : ButtonGroup みたいな複数コンポーネントからなるストーリーの書き方
  • Writing stories in TypeScript : TypeScript を使用したストーリーの書き方
Kazuki MatsudaKazuki Matsuda

Testing

  • Component tests
    • Jest + React Testing Library のテストを Storybook 上で実行する
    • play 機能を使う
  • Visual tests
    • いわゆる VRT
    • Chromatic を使う
    • Chromatic を使えない場合は storycap + reg-suit か Playwright あたりを使う
  • Accessibility tests
    • eslint-plugin-jsx-a11y ではチェックできないコントラストなどをチェックできる
  • Snapshot tests
  • Test runner
    • 内部は Jest + Playwright
  • Vitest plugin : TODO
  • Test coverage : カバレッジの計測方法
  • Import stories in tests
    • Unit tests : ストーリーを単体テストを使う方法
    • End-to-end tests : ストーリーを Cypress と Playwright で使う方法

コンポーネントがレンダリングされる前にコードを実行する

import MockDate from 'mockdate';

// ...rest of story file

export const ChristmasUI: Story = {
  async play({ mount }) {
    MockDate.set('2024-12-25');
    // 👇 Render the component with the mocked date
    await mount();
    // ...rest of test
  },
};

コンポーネントテストと Jest + テストライブラリのみを使用する場合の違いは何ですか?

コンポーネント テストは、Jest とテスト ライブラリを Storybook に統合します。最大の利点は、テストしているコンポーネントを実際のブラウザーで表示できることです。これにより、コマンド ラインで (偽の) DOM のダンプを取得したり、JSDOM がブラウザー機能をモックする方法の制限に達したりすることなく、視覚的にデバッグできます。また、ストーリーとテストを複数のファイルに分散させるよりも、1 つのファイルにまとめておく方が便利です。

ビジュアルテストとスナップショットテストの違いは何ですか?

スナップショット テストでは、各ストーリーのレンダリングされたマークアップを既知のベースラインと比較します。つまり、テストでは HTML の塊を比較するのであって、ユーザーが実際に見ているものを比較するのではありません。その結果、コードの変更によって必ずしもコンポーネントの視覚的な変更がもたらされるわけではないため、誤検知が増加する可能性があります。

テストランナー

デフォルトでは、テストランナーは、ポート でローカルに提供されている Storybook に対して実行していると想定します6006。デプロイされた Storybook に対して実行するターゲット URL を定義する場合は、次のフラグを使用できます--url。

npm run test-storybook -- --url https://the-storybook-url-here.com

Chromatic と Test runner の違いは何ですか?

テストランナーは、ローカルまたは CI で実行でき、あらゆる種類のテストを実行するように構成または拡張できる汎用テスト ツールです。

Chromaticは、テスト ランナーをセットアップせずにビジュアルテストとコンポーネント テスト(および近々アクセシビリティ テスト)を実行するクラウドベースのサービスです。また、Git プロバイダーと同期し、プライベート プロジェクトのアクセス制御を管理します。

Kazuki MatsudaKazuki Matsuda

Docs

  • Autodocs : Storybook が自動生成するコンポーネントのドキュメントとカスタマイズ方法
  • MDX : MDX を使用したドキュメントの作成方法
  • Doc blocks : ArgTypes、Canvas、ColorPalette のなどのドキュメントブロックについて
  • Preview and build docs : ドキュメントのプレビューとビルド方法
    • ドキュメントだけ公開したい場合に使う?
Kazuki MatsudaKazuki Matsuda

Sharing

  • Publish : デプロイ方法
  • Embed : ストーリーやドキュメントを iframe で別ページに埋め込む方法
  • Design integrations : Figma に Storybook を埋め込む方法と Storybook に Figma を埋め込む方法
  • Composition : 複数の Storybook を連結する方法
  • Package Composition
npm run build-storybook
npx http-server ./path/to/build # ローカルで確認
Kazuki MatsudaKazuki Matsuda

Essential addons

  • Actions : イベントハンドラーの引数に渡されるデータを表示する
  • Backgrounds : ストーリーの背景色を変える
  • Controls : Storybook で Props を変更して動作確認できる
  • Highlight : 特定の要素をハイライトする
    • このアドオンを直接使うシーンはあまりなさそう
    • storybook-addon-a11y の内部で使われている
      • A11y 警告のある要素をハイライトする
  • Interactions : play 機能を Storybook で視覚化できる
  • Measure & outline
    • Measure : ブラウザ開発者ツールの Storybook 版
    • outline : 要素にアウトラインをつける
  • Tollbars & globals : ツールバーをカスタマイズ
  • Viewport : ストーリーがレンダリングされる iframe のサイズを調整

Actions

設定方法は 2 つ

  1. @storybook/test の fn を使う
// Replace your-framework with the name of your framework
import type { Meta } from '@storybook/your-framework';
import { fn } from '@storybook/test';

import { Button } from './Button';

const meta: Meta<typeof Button> = {
  component: Button,
  // 👇 Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked
  args: { onClick: fn() },
};

export default meta;
  1. 自動検出
// Replace your-framework with the name of your framework
import type { Meta } from '@storybook/your-framework';

import { withActions } from '@storybook/addon-actions/decorator';

import { Button } from './Button';

const meta: Meta<typeof Button> = {
  component: Button,
  parameters: {
    actions: {
      handles: ['mouseover', 'click .btn'],
    },
  },
  decorators: [withActions],
};

export default meta;

Controls

条件付きコントロール

// Replace your-framework with the name of your framework
import type { Meta } from '@storybook/your-framework';

import { Button } from './Button';

const meta: Meta<typeof Button> = {
  component: Button,
  argTypes: {
    label: { control: 'text' }, // Always shows the control
    advanced: { control: 'boolean' },
    // Only enabled if advanced is true
    margin: { control: 'number', if: { arg: 'advanced' } },
    padding: { control: 'number', if: { arg: 'advanced' } },
    cornerRadius: { control: 'number', if: { arg: 'advanced' } },
  },
};

export default meta;

Highlight

使い方

import type { Meta, StoryObj } from '@storybook/react';
import { useChannel } from '@storybook/preview-api';
import { HIGHLIGHT } from '@storybook/addon-highlight';

import { MyComponent } from './MyComponent';

const meta: Meta<typeof MyComponent> = {
  component: MyComponent,
};

export default meta;
type Story = StoryObj<typeof MyComponent>;

export const Highlighted: Story = {
  decorators: [
    (storyFn) => {
      const emit = useChannel({});
      emit(HIGHLIGHT, {
        elements: ['h2', 'a', '.storybook-button'],
      });
      return storyFn();
    },
  ],
};

Tollbars & globals

Storybook のグローバルは、ストーリーのレンダリングに対する「グローバルな」(つまり、ストーリー固有ではない)入力を表します。ストーリーに固有のものではないため、argsストーリー関数の引数には渡されません(ただし、context.globals としてアクセスすることはできます)。代わりに、通常はすべてのストーリーに適用されるデコレータで使用されます。

グローバルが変更されると、ストーリーが再レンダリングされ、デコレータが新しい値で再実行されます。グローバルを変更する最も簡単な方法は、グローバル用のツールバー項目を作成することです。

Viewport

デフォルトの MINIMAL_VIEWPORTS 以外も使いたい時

// Replace your-renderer with the renderer you are using (e.g., react, vue3, angular, etc.)
import { Preview } from '@storybook/your-renderer';
import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport';

const preview: Preview = {
  parameters: {
    viewport: {
      viewports: INITIAL_VIEWPORTS,
      defaultViewport: 'ipad',
    },
  },
};

export default preview;
Kazuki MatsudaKazuki Matsuda

Addons

storybook add / remove コマンド知らなかった

npx storybook@latest add @storybook/addon-a11y
npx storybook@latest remove @storybook/addon-a11y
Kazuki MatsudaKazuki Matsuda

Configure

  • Styling and CSS : グローバル CSS や Web フォントの追加方法
    • .storybook/preview.ts
    • .storybook/preview-head.html
  • Upgrading Storybook : Storybook のバージョンアップ方法
    • 基本的にアドオンなどを個別でバージョンアップするのではなく npx storybook@latest upgrade がいい
    • npx storybook@latest doctor でヘルスチェック
  • Telemetry : Storybook が収集するテレメトリーについて
    • 収集されないようにすることもできる
    • STORYBOOK_TELEMETRY_DEBUG=1 npm run storybook で収集される情報を見れる
  • Integration
    • Frameworks
      • TODO
    • Feature support for frameworks
      • 各機能のフレームワークサポート状況
    • Compilers
      • SWC、Babel について
    • TypeScript
      • .storybook/main.ts の typescript オプションについて
    • Images and assets
      • ストーリーでのアセットの使用方法
      • 各ファイルで import or .storybook/main.ts の staticDirs で設定
  • Story rendering : 全てのストーリーで処理を実行する方法
    • .storybook/preview.ts
    • .storybook/preview-head.html
    • .storybook/preview-body.html
  • Story layout : ストーリーのレイアウト
    • centeredfullscreenpadded がある
  • User Interface
    • Features and behavior
      • サイドバーやアドオンパネルのスタイルの変更方法
    • Theming
      • Storybook をダークモードにする方法
      • カスタムテーマの作成方法
    • Sidebar & URLS
      • サイドバーのグループ化方法
      • 各ストーリーの URL に使用される ID の決め方
    • Storybook Addons
      • アドオンの UI について
      • ツールバーとアドオンパネル
  • Environment variables : 環境変数の設定方法
    • STORYBOOK_THEME=red STORYBOOK_DATA_KEY=12345 npm run storybook