Storybookの使い方

StorybookはReactなどのUIコンポーネントをリスト表示して動作確認するツールです。addonを利用して入力値ごとの挙動を確認することも可能です。

インストール

npmでstorybookを追加したうえで、storybook initコマンドを実行することで関連パッケージが一式入ります。

$ npm install --save-dev storybook
$ npx storybook init

Storybookに後から機能追加していく際、各種configを編集することになります。
Storybookは、TypescriptやJSXのトランスパイルにBabelを、バンドルにはWebpackを利用しています。メインのプロジェクトでtscやrollupなどのビルドツールを採用している場合にもstorybook initがwebpack/babelなどを追加セットアップします。
また、storybook用のconfigはプロジェクトのconfigではなく.storybook/を参照します。そのため、プロジェクトのツールチェインに関わらずStorybookをセットアップできます。ただ、パッケージは肥大化することと、Storybookのconfigはwebpack/babelを設定する必要がある点はトレードオフです。

.storybook/main.js

Storybookの基礎的なconfigは.storybook/main.jsです。基本的にはstories属性のpathが適切であれば動作します。初期状態ではstorybook initが生成するサンプルのストーリー(ギャラリー表示するコンポーネント)を指しており、このディレクトリを採用しても問題ありません。pathさえ適切であれば別のディレクトリも設定可能です。

また、webpackのレイヤで追加設定したい場合、main.jsの webpackFinal属性にwebpack.config.jsのパーツを書くという構成になっており、拡張の際には注意が必要です。
これは、Storybookが内包しているwebpackのconfigをオーバーレイするための構成ですが、猛烈に分かりにくいポイントです。

なお、config.jsやaddons.jsなどはv7で削除予定の古いconfigフォーマットで、 移行の警告が表示されるため情報源に注意します。

Preact向けBabel拡張

StorybookはReact向けにセットアップするため、JSXや基本的なTypescriptについては初期状態で対応済になっています。
JSXライブラリとしてPreactを利用している場合、h()の解決が追加で必要になります。これはBabelのレイヤで対応するため、.storybook/main.jsに以下のような Babel設定を追加することで吸収できました。

module.exports = {
    babel: async (options) => ({
        ...options,
        presets: [
            '@babel/preset-env',
            ['@babel/preset-typescript', { jsxPragma: 'h' }, 'preact-preset']
        ],
        plugins: [
            ['@babel/transform-react-jsx', { pragma: 'h' }, 'preact-plugin']
        ]
    })
}

なお、configで追加指定するBabelのプラグインはnpm install --savedev @babel/plugin-transform-react-jsxのように手動でインストールしておきます。
要するに、Babelの機能追加はBabelのレイヤで対応するという当たり前のことですが、StorybookのドキュメントはWebpack/Babelに言及せずに書かれているため、気づきにくいポイントと言えます。

またこの例はPreactですが、Typescriptのクラスプロパティを使いたい場合なども同様にBabelのプラグイン追加&config定義します。

Storyの記述と実行

ギャラリーの表示内容は、生成されたサンプルを参考に、各プロジェクトのコンポーネントを表示するストーリーを記述します。表示対象のコンポーネントは、各ストーリー冒頭のimport文でpathベースで参照するだけで、追加の設定は不要です。
ストーリーについては実行環境による差がほとんどないため、 公式ドキュメントが参考になります。

ドキュメント

スタティックなドキュメントについては、MDX形式で記述する方法があります。
各ストーリーへのコメント程度で足りる場合には、以下のようにCSFのexport defaultのパラメータとして指定する方法もあります。

export default {
    title: 'Some story',
    parameters: {
        docs: {
            description: {
                component: "ストーリーのトップブロックに表示されます。markdownで装飾可能"
            }
        }
    }
};

また、コンポーネントのpropsを表形式で表示する機能もあります。Reactの場合は、以下のようにexport defaultにコンポーネントを指定すると表示できるようです。

export default {
    title: 'Some story',
    component: SomeComopnent
};

Preactでは自動でリスト抽出できませんが、 argsの形式でStoryを書くとArgsTableを表示できます。
なお、Storybookの意図としてはコンポーネントのソースからJSDocのPropTypesを参照して自動でdescriptionなどを生成する機能があるのですが、現状安定していません。
手動で指定することも可能ですが、コードエディタに活用できるわけでもないため、JSDocのような定義をストーリー側に書きたいケースはあまりなさそうです。

Storybookのドキュメント機能はコンポーネントと同時に掲載できる点はメリットなのですが、JSDocからの抽出対象が狭いため、完全に統合することが難しい状況です。ストーリーを記述する範囲でコメントを付加する程度にとどまり、APIドキュメントは別途JSDocで管理することになりそうです。

ローカルサーバー

npx start-storybookコマンドでローカルサーバーが起動します。起動時にアドレスがコンソール出力され、ブラウザからアクセス可能です。/index.htmlは明示的に指定する必要があるでしょう。

起動前にビルドが失敗した場合は、修正して再実行します。初回セットアップ時にはconfigが完備していないケースもあるため、サンプルのストーリーを一度起動して動作確認まで終えておいた方が良いでしょう。

スタティックビルド

npx build-storybook -o dist/コマンドで一式ビルドも可能です。-oオプションのディレクトリにhtml, javascriptが出力されます。
ブラウザで直接index.htmlファイルを表示可能なほか、別途HTTPサーバーで配信できます。

なお、ビルド失敗時にはエラー出力しますが、build-storybookのログは判別不能なため、start-storybookでビルドが通る状態を完備しておく必要があるでしょう。

まとめ

StorybookはUIコンポーネントに変数を入力して表示させる手法で品質確認するツールです。SPAなどの場合、バックエンドAPIほか特殊な環境を必要とするケースが多くなりがちですが、設計しだいでUIのみテスト可能にできます。

眺めることによる確認のほか、異常値を入力することによる堅牢性テストなどもStorybookのメリットと言えます。

セットアップについては、限られた特定条件では設定不要で利用開始できますが、基本的にはWebpackとBabelについて理解したうえでStorybookのconfigレイヤを把握することになるでしょう。

中馬崇尋
Chuma Takahiro