Goalist Developers Blog

バックエンドエンジニアがフロントエンドを書く② 〜storybookをカスタマイズ〜

現プロジェクトでバックエンドを実装しているJPです。
前回の記事の続きで、storybookの基本的な部分をカスタマイズしていきたいと思います。

筆者の経験:CLIツールなどもあまり触ったことがない、フロントエンドの初心者
環境   :macOS Sierra
前回の記事:

developers.goalist.co.jp

今回やることは以下の通りです。

前回の作業のおさらい

storybookの起動

前回作成したプロジェクトにおいて、storybookを起動します。

$ cd my-app
$ yarn run storybook

そして、http://localhost:9009/にアクセスすると、以下のような画面が表示されます。

f:id:j-itoh:20170615200240p:plain

自動生成されたコンポーネント

storybook画面左の「Buttons」を押してみます。

f:id:j-itoh:20170727111832p:plain

「Buttons」というカテゴリーには「with text」と「with some emoji」の2つのコンポーネントが自動で生成されています。

コンポーネントの追加処理をカテゴリーごとに分ける

どういうこと?

作成したコンポーネントをstorybookに表示させるには、そのコンポーネントをどのカテゴリーに表示させるかをstorybookに読み込ませる必要があります。
デフォルトでは表示情報の定義が1つのファイルにまとめて書いてありますが、それをカテゴリーごとに分割したいと思います。

なぜ?

コンポーネントの表示情報の定義の読み込み先を探します。

以下の「my-app/.storybook/config.js」に書いてある模様。

f:id:j-itoh:20170726182259p:plain

my-app/.storybook/config.js

import { configure } from '@storybook/react';

function loadStories() {
  require('../src/stories');
}

configure(loadStories, module);

どうやらloadStories()で読み込んでいる様子。
パスは「my-app/src/stories」フォルダを指しています。
そのフォルダを確認すると、以下のような「my-app/src/stories/index.js」ファイルがありました。

my-app/src/stories/index.js

import React from 'react';

import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links';

import Button from './Button';
import Welcome from './Welcome';

storiesOf('Welcome', module).add('to Storybook', () => <Welcome showApp={linkTo('Button')} />);

storiesOf('Button', module)
  .add('with text', () => <Button onClick={action('clicked')}>Hello Button</Button>)
  .add('with some emoji', () => <Button onClick={action('clicked')}>😀 😎 👍 💯</Button>);

上記ではstoriesOf()で「Welcome」と「Button」というカテゴリーを追加し、add()でそれぞれのカテゴリーにコンポーネントを追加しています。

ですが、コンポーネントの数が増えたら、以下のようなデメリットが発生します。

  • 「my-app/src/stories/index.js」のコード量が増える
  • コンポーネントに複雑なプロパティーを渡す場合、プロパティーオブジェクトの記述も「my-app/src/stories/index.js」に書かなければならない
  • 「my-app/src/stories」以下にコンポーネントのjsファイルが溢れる

これらの理由により、この追加処理を1つのファイルにまとめて書くのではなく、カテゴリーごとに分けて記述するように変更しようと思います。

手順

フォルダ構成を以下のように変更する

f:id:j-itoh:20170727121614p:plain

src以下のフォルダやファイルの説明は以下の通りです。

  • components:カテゴリーを配置するフォルダ
  • Buttons:ボタンカテゴリーを表すフォルダ
  • Button.js:ボタンのコンポーネント(「my-app/src/stories/Button.js」と同じファイル)
  • HogeButton.js:他のボタンコンポーネント
  • stories.js:ボタンカテゴリーのコンポーネントの追加処理を書くファイル(「my-app/src/stories/index.js」と同じファイル)

「my-app/.storybook/config.js」を編集する

「my-app/.storybook/config.js」はカテゴリーやコンポーネントの追加処理が書いてあるファイルを指定するところでした。
そのため、新しく作成した「components/{カテゴリー}/stories.js」から読み込むように内容を変更します。

import { configure } from "@storybook/react";

const req = require.context("../src/components", true, /stories.js$/); // ①

function loadStories() {
  req.keys().forEach(filename => req(filename)); // ②
}

configure(loadStories, module);

loadStories()の中で対象ファイルをrequire()する必要がありますが、カテゴリーの数だけrequire()するのは大変なので、正規表現で「components/{カテゴリー}」以下の「stories.js」を取得し(①)、ループでrequire()しています(②)。

これで完了です。 これまでと同じようにstorybookにカテゴリーとコンポーネントが表示されていると思います。
(「Welcome」カテゴリーは今回移植していません。)

まとめ

storybook上の表示情報がカテゴリーごとに分けて記述できると、見やすくなるし、複数人での開発もやりやすいと感じました。
今回はここまでしか書けませんでしたが、どんどんstorybookをカスタマイズしていく予定です。