🦁

StoryBookを仕様書として使う

2024/07/08に公開

StoryBookとは

コンポーネントの開発・テスト・管理を行うためのOSSツールです。コンポーネントのコードや見た目を一元化して共有・参照することができます。また、アクセシビリティテストやビジュアルテストなどを行うこともできます。
今回はStoryBookをUIの仕様書として使う手順をまとめます。

前提知識

Docs

UIコンポーネントのドキュメントです。コンポーネントの引数やバリエーション(story)、またその説明を一覧することができます。

story

UIコンポーネントの特定の状態を表現したものです。具体的には、コンポーネントが特定の引数を受け取ったときの振る舞いや見た目を記述します。

使い方

1. AutoDocsの設定

StoryBookにはDocsを自動生成してくれる機能があります。
preview.tstags: ['autodocs']を設定することで、各コンポーネントに自動生成設定を施す必要がなくなります。

preview.ts
import type { Preview } from '@storybook/your-renderer';

const preview: Preview = {
   ...
+  tags: ['autodocs'],
};

export default preview;

2. StoryBookのためのコードを記述

Docs

簡単なドキュメントはStoryBookが自動作成してくれます。しかし、コンポーネントやプロパティの詳細な説明書きはないため、ドキュメンテーションコメント(/** コメント */)を用いて説明を追加していきます。

Button.tsx
import { FC } from 'react';
import './button.css';

export interface ButtonProps {
  /**
   * ページ上で主要な役割をするボタンかどうか
   */
  primary?: boolean;
  /**
   * ボタンの背景色
   */
  backgroundColor?: string;
  /**
   * ボタンの大きさ
   */
  size?: 'small' | 'medium' | 'large';
  /**
   * ボタンの使用の可否
   */
  disable?: boolean;
  /**
   * ボタンの内容
   */
  label: string;
  /**
   * click ハンドラを設定
   */
  onClick?: () => void;
}

/**
 *  ユーザにアクションを実行してもらうためのコンポーネント
 */
export const Button: FC<ButtonProps> = ({
  primary = false,
  size = 'medium',
  disable = false,
  backgroundColor,
  label,
  ...props
}) => {
  const mode = primary
    ? 'storybook-button--primary'
    : 'storybook-button--secondary';
  const disableClass = disable ? 'storybook-button--disable' : '';

  return (
    <button
      type="button"
      className={[
        'storybook-button',
        `storybook-button--${size}`,
        mode,
        disableClass,
      ].join(' ')}
      style={{ backgroundColor }}
      {...props}
    >
      {label}
    </button>
  );
};

このように説明書きを追加することで、以下のような説明付きのドキュメントが作成されます。
StoryBookのDocsの例。Buttonコンポーネントの例を出している。

story

取りうる状態を、[コンポーネント名].stories.tsに記述します。
Docs同様、ドキュメンテーションコメントを用いて説明を追加することができます。

Button.stories.ts
import type { Meta, StoryObj } from '@storybook/react';
import { fn } from '@storybook/test';
import { Button } from './Button';

const meta = {
  // コンポーネントのタイトル
  title: 'Example/Button',

  // 適用するコンポーネント(importしているコンポーネント)
  component: Button,

  // 引数の振る舞い
  // これを指定することで、StoryBookのUI上で引数の値を動的に調整できるようになる。
  // 自動推論されるので省略可能
  argTypes: {
    backgroundColor: {
      control: 'color',
    },
  },

  // 関数をスパイする
  args: { onClick: fn() },
} satisfies Meta<typeof Button>;

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

// 以下にstoryを記述する
// ここで記述した通りにStoryBookのUI上でコンポーネントが表示される

/**
 * プライマリーカラーのボタン
 */
export const Primary: Story = {
  args: {
    primary: true,
    label: 'Button',
  },
};

/**
 * セカンダリーカラーのボタン
 */
export const Secondary: Story = {
  args: {
    label: 'Button',
  },
};

/**
 * 大きなサイズのボタン
 */
export const Large: Story = {
  args: {
    size: 'large',
    label: 'Button',
  },
};

/**
 * 小さなサイズのボタン
 */
export const Small: Story = {
  args: {
    size: 'small',
    label: 'Button',
  },
};

/**
 * 使用不可のボタン
 */
export const Disabled: Story = {
  args: {
    primary: true,
    size: 'medium',
    label: 'Button',
    disable: true,
  },
};

3. StoryBookを起動

npm run storybook

4. 画面の立ち上がりの確認

このような画面が表示されます。
StoryBook起動時に表示される画面。Primaryボタンのタブを開いている。
詳細な仕様書を作ることができました!

storyファイルの場所

コンポーネントファイルと同じディレクトリに配置することがよいかもしれません。
https://storybook.js.org/docs/writing-stories#where-to-put-stories

Accessibilityテスト

コンポーネントを登録した際には、Accesibilityテストを通過しているか確認するのが望ましいです。
Accesibilityテストのタブ。コントラスト比が不十分というエラーが出ている。

参考

https://storybook.js.org/tutorials/intro-to-storybook/react/en/get-started/
https://note.com/japan_d2/n/nc4fc0f52794d
https://zenn.dev/sakito/articles/7a7c2e0800cf69#ドキュメント
https://storybook.js.org/docs/writing-stories#where-to-put-stories

Discussion