Storybookを導入する

August 7th, 2020

手順

storybook cliのインストール

まずはStorybookのCLIをインストールしてコマンドを使えるようにします。

yarn global add @storybook/cli

storybookの初期化

プロジェクトのルートで以下のコマンドを実行して初期化を行います。成功すると.storybook/main.jsが作られます。

sb init

main.jsの修正

初期化で作られた.storybook/main.jsを修正します。公式サイトの内容を無心でコピペすれば多分OKです。

.storybook/main.js
module.exports = {
  stories: ['../stories/**/*.stories.js'],
  addons: ['@storybook/addon-actions', '@storybook/addon-links'],
  webpackFinal: async config => {
    // Transpile Gatsby module because Gatsby includes un-transpiled ES6 code.
    config.module.rules[0].exclude = [/node_modules\/(?!(gatsby)\/)/]

    // use installed babel-loader which is v8.0-beta (which is meant to work with @babel/core@7)
    config.module.rules[0].use[0].loader = require.resolve("babel-loader")

    // use @babel/preset-react for JSX and env (instead of staged presets)
    config.module.rules[0].use[0].options.presets = [
      require.resolve("@babel/preset-react"),
      require.resolve("@babel/preset-env"),
    ]

    config.module.rules[0].use[0].options.plugins = [
      // use @babel/plugin-proposal-class-properties for class arrow functions
      require.resolve("@babel/plugin-proposal-class-properties"),
      // use babel-plugin-remove-graphql-queries to remove static queries from components when rendering in storybook
      require.resolve("babel-plugin-remove-graphql-queries"),
    ]

    // Prefer Gatsby ES6 entrypoint (module) over commonjs (main) entrypoint
    config.resolve.mainFields = ["browser", "module", "main"];

    return config;
  },
};

preview.jsの作成

main.jsと同じ階層にpreview.jsを作ります。中身は公式の内容をコピペすればOKです。

.storybook/preview.js
import { action } from "@storybook/addon-actions"

// Gatsby's Link overrides:
// Gatsby Link calls the `enqueue` & `hovering` methods on the global variable ___loader.
// This global object isn't set in storybook context, requiring you to override it to empty functions (no-op),
// so Gatsby Link doesn't throw any errors.
global.___loader = {
  enqueue: () => {},
  hovering: () => {},
}

// Navigating through a gatsby app using gatsby-link or any other gatsby component will use the `___navigate` method.
// In Storybook it makes more sense to log an action than doing an actual navigate. Checkout the actions addon docs for more info: https://github.com/storybookjs/storybook/tree/master/addons/actions.

window.___navigate = pathname => {
  action("NavigateTo:")(pathname)
}

TypeScriptをサポートする設定

TypeScriptをサポートするためにmain.jsを修正します。2行目のstoriesの配列にtsxをファイルタイプとして追加します。追加後は以下のようになります。

.storybook/main.js
js
stories: ['../stories/**/*.stories.js',"../stories/**/*.stories.tsx"],

27行目のreturn config;の直上に以下の内容をコピペします。

.storybook/main.js
js
config.module.rules.push({
  test: /\.(ts|tsx)$/,
  loader: require.resolve("babel-loader"),
  options: {
    presets: [["react-app", { flow: false, typescript: true }]],
    plugins: [
      require.resolve("@babel/plugin-proposal-class-properties"),
      // use babel-plugin-remove-graphql-queries to remove static queries from components when rendering in storybook
      require.resolve("babel-plugin-remove-graphql-queries"),
    ],
  },
})

config.resolve.extensions.push(".ts", ".tsx")

main.jsは最終的に以下の状態になります。

.storybook/main.js
js
module.exports = {
  stories: ['../stories/**/*.stories.js',"../stories/**/*.stories.tsx"],
  addons: ['@storybook/addon-actions', '@storybook/addon-links'],
  webpackFinal: async config => {
    // Transpile Gatsby module because Gatsby includes un-transpiled ES6 code.
    config.module.rules[0].exclude = [/node_modules\/(?!(gatsby)\/)/]

    // use installed babel-loader which is v8.0-beta (which is meant to work with @babel/core@7)
    config.module.rules[0].use[0].loader = require.resolve("babel-loader")

    // use @babel/preset-react for JSX and env (instead of staged presets)
    config.module.rules[0].use[0].options.presets = [
      require.resolve("@babel/preset-react"),
      require.resolve("@babel/preset-env"),
    ]

    config.module.rules[0].use[0].options.plugins = [
      // use @babel/plugin-proposal-class-properties for class arrow functions
      require.resolve("@babel/plugin-proposal-class-properties"),
      // use babel-plugin-remove-graphql-queries to remove static queries from components when rendering in storybook
      require.resolve("babel-plugin-remove-graphql-queries"),
    ]

    // Prefer Gatsby ES6 entrypoint (module) over commonjs (main) entrypoint
    config.resolve.mainFields = ["browser", "module", "main"];
    config.module.rules.push({
      test: /\.(ts|tsx)$/,
      loader: require.resolve("babel-loader"),
      options: {
        presets: [["react-app", { flow: false, typescript: true }]],
        plugins: [
          require.resolve("@babel/plugin-proposal-class-properties"),
          // use babel-plugin-remove-graphql-queries to remove static queries from components when rendering in storybook
          require.resolve("babel-plugin-remove-graphql-queries"),
        ],
      },
    })
    
    config.resolve.extensions.push(".ts", ".tsx")
    return config;
  },
};

babel-preset-react-appのインストール

プラグインをインストールします。開発でしか使わないので--devオプションを付けておきます。

yarn add --dev babel-preset-react-app

以上で設定は完了です。

動作確認

テスト用ストーリーの作成

storiesディレクトリを作り、その下にストーリーを格納していきます。テスト用なのでexample.stories.jsとしました。

stories/example.stories.js
import React from "react"

export default {
  title: "Dashboard/header",
}

export const exampleStory = () => (
  <div style={{ padding: "16px", backgroundColor: "#eeeeee" }}>
    <h1 style={{ color: "rebeccapurple" }}>Hello from Storybook and Gatsby!</h1>
  </div>
)

実行

以下のコマンドをstorybookを実行します。成功するとブラウザが立ち上がりstorybookが表示されます。

yarn run storybook

左側のメニューにDashboard/header/Example Storyが表示されていれば成功です。