RoadMovie

write down memos or something I found about tech things

StorybookをReact, Typescriptな環境に導入する

StorybookをReact, Typescript, Atomic Designな環境に導入する手順を紹介します。

Why Storybook?

Frontendの開発を行っていると、再利用性を高めたいという意識が湧いてくると思うのですが、それをチーム間で共有したり、デザイナーさんと認識を合わせるのが容易でなかったりします。また、開発者も「このコンポーネントどこでどう使ってたっけ?」と言うように、視認性を持ちながら検索性も高くするというのがなかなか難しかったりします。

そこで、Storybook ( https://storybook.js.org ) の出番です。

How to install Storybook in my environment?

まず、先にも記述しましたが、今回は、下記の要件のアプリへの導入を紹介します。

  • React.js
  • yarn
    • npmな方は適宜読み替えてください
  • Typescript
    • 特に使ってなければ問題ないのですが、Typescript特有のハマリポイントの紹介もしています
  • SCSS (CSS)
    • SCSSを利用しているコンポーネントの使用にもいくつかハマリポイントがあったのでそこも紹介しています。
  • Atomic Design
    • こちらもこれで運用していなくても全く問題なく読めますが、例として考えやすいのでこちらをベースにしています。

公式ドキュメントはこちらです。
https://storybook.js.org/docs/guides/guide-react/

また、Storybookには数多くのaddonがあり、それらを利用することでより便利にすることが可能です。この記事の中でもある程度メジャーそうなaddonをインストールしながら進めていきます。



まずは下記のコードをFrontend root(package.jsonなどを置いている場所)な場所で実行しましょう。

$ npx -p @storybook/cli sb init

これでかなりの部分をいい感じに自動で用意してくれます。下記のコマンドを打てばひとまずstorybookを確認することができます。(デフォルトで、Welcome.jsとButton.jsが用意されているはず)

$ yarn storybook

f:id:mr7myself:20200205132749p:plain

いい感じですね。 さて、ここからが本題です。サンプルコードは .js で書かれていますが、 .ts あるいは .tsx で作りたいですよね。そうするといくつか追加で設定が必要になってきます。

その前に、軽くstorybookのディレクトリ構造などに触れておきたいのですが、一番はじめのコマンドで作られたのを見るとわかるように、storybookは下記の2つのディレクトリをベースに作っていきます。

stories
├── atoms/
└── molecules/
.storybook
├── config.js
├── main.js
├── manager.js
└── preset.js

.storybook

(各ファイルはあとから出てくるので今わからなくても問題ないです)

  • config.js
    • 全体に反映させたい設定などを行います
  • main.js
    • webpack.config.jsのstorybook用という感じのファイルです。loaderの設定などを行います
  • manager.js
    • addonの初期設定を行います
  • preset.js
    • こちらはtypescript用に一行設定が必要なファイルです

stories

storybookでは、ひとつひとつのファイルをstoryと呼び、ファイル名.stories.拡張子 という形で保存すると、storybookに反映されます。このディレクトリ構造は一旦atomic designに沿って、atoms, moleculesというような形にしてあります。



次に進む前に、前述したaddonをいくつか入れておきましょう。

$ yarn add -D @storybook/addon-knobs @storybook/addon-viewport @storybook/addon-storysource react-docgen-typescript-loader @storybook/addon-info @storybook/addon-console

typescript用のライブラリも入れておきます。

$ yarn add -D @types/storybook__react @types/storybook__addon-info @types/storybook__addon-actions @types/storybook__addon-knobs

ちなみに、どのライブラリだったか忘れたのですが、2020/02/05現在でcore-jsのv2に依存しているのがありまして(stableで最新がv3なのでイケてないのですが)、core-js v2を入れなければなりません。実コードでv3使ってる方は、addonを外すようにしましょう。core-js v2の入れ方は下記のとおりです。

$ yarn add -D core-js@2

さて、各ライブラリが何をやっているのかは別途検索していただければと思いますが、簡単に紹介しておくと、

  • addon-info
    • componentが受け取るpropsの情報や、componentの使い方をviewに表示してくれる
  • addon-knobs
    • propsの内容をviewから変更してデザインの変化などを確認できる

このあたりは便利です。



では、早速typescriptに合わせて設定を行っていきましょう。まずは、簡単なところから。 .storybook/preset.js に下記のように書いて保存しましょう。

module.exports = ['@storybook/preset-typescript']

次に、.storybook/main.js を、下記のように書いて保存します。addonの設定なども既に記述しています。

const path = require('path')

module.exports = {
  stories: ['../stories/**/*.stories.tsx'],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-knobs/register',
    '@storybook/addon-actions/register',
    '@storybook/addon-viewport/register',
    {
      name: '@storybook/preset-typescript',
      options: {
        tsLoaderOptions: {
          configFile: path.resolve(__dirname, './tsconfig.json')
        }
      }
    }
  ],
  webpackFinal: async config => {
    config.module.rules.push({
      test: /\.(ts|tsx)$/,
      use: [
        {
          loader: require.resolve('babel-loader')
        },
        // Optional
        {
          loader: require.resolve('react-docgen-typescript-loader')
        }
      ]
    })
    config.module.rules.push({
      test: /\.scss$/,
      use: ['style-loader', 'css-loader?modules', 'sass-loader'],
      include: path.resolve(__dirname, '../')
    })
    config.resolve.extensions.push('.ts', '.tsx')
    return config
  }
}

webpack.config.jsと同じように、typescriptとscssのloaderの設定をここで行います。addonの登録もここで行います。

これで一通りの設定はおしまいです。例として、サンプルのWelcome.jsとButton.jsを Welcome.tsxButton.tsx に変更して、 yarn storybook でstorybookを立ち上げてみましょう。うまくいきましたか?

Advanced (さらに便利に)

グローバルなaddonの初期設定

Storybookのパネルが下に表示されると思うのですが、邪魔なので右側にしたい。といった場合に、 .storybook/manager.js にその設定を書いておけば、常に右側になります。

import { addons } from '@storybook/addons'

addons.setConfig({
  panelPosition: 'right'
})

すべてのstoriesにaddonを適用

addon-infoaddon-knobs は便利なので、すべてのstoriesに適用させておきたくなります。 .storybook/config.js に下記のように書いて保存しましょう。

import { addDecorator } from '@storybook/react'
import { withInfo } from '@storybook/addon-info'
import { withKnobs } from '@storybook/addon-knobs'

import '../css/app.css'

addDecorator(withInfo({ inline: true }))
addDecorator(withKnobs)

ついでに、実コードのcssをインポートする方法も載せておきました。こうしたstoriesへのグローバルな設定はここで行なえます。

静的ファイルがstorybookでうまく読み込めない

storybookの立ち上げコマンドを工夫すればできるようになります。package.json にかかれている実行コマンドを下記のように修正しましょう。

"storybook": "start-storybook -p 6006 -s ./staticファイルのディレクトリ名",


これでかなり便利にStorybookを使えるようになったと思います。最後にサンプルとして上記の設定を行った上でのコードを載せておきます。

import React from 'react'
import { action } from '@storybook/addon-actions'
import { storiesOf } from '@storybook/react'
import { text, boolean } from '@storybook/addon-knobs'

import Button from '@/components/atoms/Button' // storyに載せたいコンポーネントをインポート

storiesOf('atoms', module).add('Button', () => (
  <Button
    title={text('Title', 'テキスト')}
    disabled={boolean('Disabled', false)}
    onClick={action('clicked')}
  />
))

storybook sample view
storybook

以上です。Storybookはatomic designを推進するのにもかなり相性良さそうですね!