本ブログでは”ゼロから始めるStorybook入門(React編)”でStorybookの利用方法について基礎から説明を行なっています。本文書ではStorybookをどのように利用して開発を効率的に進めるかではなくReactのフレームワークのNext.jsのバージョン12.XでStorybookを利用したい場合にどのような設定が必要になるのかを説明しています。

StorybookをNext.jsで利用する中で必要になる以下の3点の機能について設定前と設定後の状態を比較しています。ネット上に公開されている手順通りに設定するとStorybookが動作するようになっただけではなく各設定で何を行い、その結果どのような変化があるのかを理解することができます。

  • eslint-plugin-storybook
  • 画像の表示
  • globals.cssの適用

動作確認はWindows 11、Next.jsのバージョンは12.1.6、Storybookのバージョンは6.5.8です。

Next.jsのプロジェクトの作成

npx create-next-appコマンドを利用してNext.jsのプロジェクトの作成を行っています。プロジェクト名にはnext-js-storybookという名前を付けています。


 % npx create-next-app@latest next-js-storybook

インストールが完了したら作成されるnext-js-storybookに移動してStorybookのインストールを行います。

Storybookのインストール

インストールには下記のコマンドを実行します。ビルドにはwebpack5を利用するためbuilderオプションでwebpack5を指定しています。指定しない場合はwebpack4がインストールされるようですが動作確認を行っていません。sbはstorybookの略なのでstorybookでも実行できます。


 % npx sb init --builder webpack5 

インストールの途中でStorybook用のESLintのeslint-plugin-storybookに関するメッセージが表示されるので”y”を選択します。


 checking possible migrations..
🔎 found a 'eslintPlugin' migration:

╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│                                                                                                              │
│   We've detected you are not using our eslint-plugin.                                                        │
│                                                                                                              │
│   In order to have the best experience with Storybook and follow best practices, we advise you to install    │
│   eslint-plugin-storybook.                                                                                   │
│                                                                                                              │
│   More info: https://github.com/storybookjs/eslint-plugin-storybook#readme                                   │
│                                                                                                              │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
? Do you want to run the 'eslintPlugin' migration on your project?

“y”を選択することでeslint-plugin-storybookがインストールされることになります。Storybookのインストール後にpackage.jsonを見ることでインストールを確認することができます。もし”n”を選択した場合はeslint-plugin-storybookがインストールされないので必要であれば後ほどインストールすることが可能です。

Storybookのインストールが完了するとプロジェクトフォルダ直下に.storybookとstoriesというフォルダが作成されます。

さらにpackage.jsonファイルを見るとscriptsにstorybookに関するコマンドが追加されています。


"scripts": {
  "dev": "next dev",
  "build": "next build",
  "start": "next start",
  "lint": "next lint",
  "storybook": "start-storybook -p 6006",
  "build-storybook": "build-storybook"
},

eslint-plugin-storybookの設定

eslint-plugin-storybookをインストールしただけでは有効になっていないのでプロジェクトフォルダにデフォルトから存在する.eslintrc.jsonファイルのextendsに追加設定を行います。デフォルトではnext.jsのESLintが設定されています。


{
  "extends": ["next/core-web-vitals","plugin:storybook/recommended"]
}

eslint-plugin-storybookを設定することでどのような効果があるのかは後ほどStorybookのストーリーを作成する際に説明します。

Storybookの動作確認

Storybookのインストールが完了したのでStorybookを起動するためにyarn storybookコマンドを実行します。


 % yarn storybook
//略
99% done plugins webpack-hot-middlewarewebpack built preview 7064ad18ae92de9d2469 in 19640ms
╭────────────────────────────────────────────────────╮
│                                                    │
│   Storybook 6.5.8 for React started                │
│   20 s for manager and 21 s for preview            │
│                                                    │
│    Local:            http://localhost:6006/        │
│    On your network:  http://192.168.2.104:6006/    │
│                                                    │
╰────────────────────────────────────────────────────╯
99% done plugins webpack-hot-middlewarewebpack built preview 948d23bb9fdd9c9f0a6c in 983ms

起動が完了するとhttp://localhost:6006/からStorybookにアクセスすることができます。

Welcomeページの表示
Welcomeページの表示

左側のメニューにはIntroduction, Button , Header, Pageが表示されています。これらはStorybookのインストール時に作成されたstoriesフォルダの中にあるintroduction.stories.mdx, Button.stories.jsx, Header.stories.jsx, Page.stories.jsxに対応します。中身については本文書では説明を行っていません。

ストーリーの作成

デフォルトから作成されているButton, Header, Pageを利用するのではなく新たにストーリーを作成することで追加設定を行っていきます。

storiesフォルダの下にpagesフォルダを作成してその下にhome.stories.jsxファイルを作成します。Next.jsの起動(yarn dev)を行うとブラウザ上に表示されるWelcomeページのpages/index.jsのストーリーを作成します。Storybookではコンポーネントだけではなくページコンポーネントでも利用することができます。

home.stories.jsxファイルでimport文を使ってindex.jsファイルを読み込みます。import文を記述するとメッセージが表示されメッセージの中に”The file should have a default export. eslint(storybook/default-exports)”を確認することができます。

ESLintによるメッセージが表示
ESLintによるメッセージが表示
コードエディターにはVScodeを利用しExtentionsにESLintをインストールしています。
fukidashi

この”The file shoud have a default export”のメッセージは記述したコードがeslint-plugin-storybookが持つルールdefault-exportsに違反していることから表示されています。もしeslint-plugin-storybookを設定していない場合にはこのメッセージは表示されません。

このメッセージによりStorybookのストーリーを記述するファイルには必ずexport defautが必要であることがわかります。

eslint-plugin-storybookのルールはhttps://github.com/storybookjs/eslint-plugin-storybookkで確認することができます。全部で12個のルールが存在します。

メッセージを解消するためにexport default{}を追加します。


import Home from "../../pages/index"

export default {
  
};

export defaultを追加すると新たなメッセージが表示されます。メッセージから最低一つのストーリーのexportが必要だということがわかります。

ESLintのメッセージ
ESLintのメッセージ

exportを追加することでメッセージは消えます。これでストーリーは作成できたことになります。


import Home from "../../pages/index";

export default {};

export const HomePage = () => <Home /> //追加

ファイルを保存してブラウザを確認すると左側のメニューに作成したストーリーが追加されていることが確認できます。このようにeslint-plugin-storybookを利用することでストーリーを作成する上でどこに記述間違いがあるのかをすぐに見つけることができます。

ストーリーの追加
ストーリーの追加

Next.jsでのStorybookの設定はこれで完了かと思うかもしれませんがブラウザのCanvasに表示されているWelcomeページを下にスクロールしてください。

Powerde byの横に画像のリンク切れがあることがわかります。画像が表示されるように設定を行っていきます。

画像が表示されていない
画像が表示されていない

画像の表示設定

コード上では表示されていない画像がどの部分に対応するのか確認するためにpages/index.jsファイルを確認します。確認するとImageコンポーネントの中でsrcに指定されているvercel.svgファイルであることがわかります。


import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'

export default function Home() {
  return (
//略

      <footer className={styles.footer}>
        <a
          href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
          target="_blank"
          rel="noopener noreferrer"
        >
          Powered by{' '}
          <span className={styles.logo}>
            <Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
          </span>
        </a>
      </footer>
    </div>
  )
}

vercel.svgファイルはpublicフォルダに存在しpublicフォルダのファイルを表示させるためにはStorybookの設定ファイルである.storybook/main.jsファイルにstaticDirsを指定する必要があります。


module.exports = {
  stories: [
    "../stories/**/*.stories.mdx",
    "../stories/**/*.stories.@(js|jsx|ts|tsx)",
  ],
  //略
  staticDirs: ["../public"], //追加
};

main.jsファイルを更新したら更新を反映させるためにStorybookの再起動を行います。

先ほどまで表示されていなかったSVGファイルが表示されるようになります。

publicフォルダの画像ファイルを表示
publicフォルダの画像ファイルを表示
package.jsonファイルの中でscriptsのstart-storybook, build-storybookに-sオプションでフォルダを指定する方法もありますがこちらの方法はDeprecatedになっています。main.jsの代わりに設定しても動作はします。
fukidashi

ストーリーへのglobals.cssの適用

home.stories.jsxファイルではmodule.cssファイルによりCSSの適用を行っています。/pages/index.jsはstylesフォルダにあるHome.module.cssファイルをimportしているためCSSが適用されています。しかしデフォルトから存在するstylesフォルダのglobals.cssに設定したCSSがindex.jsファイルで利用されている場合にはStorybook上のコンポーネントにglobals.cssのCSSが適用されません。

例えばglobals.cssの中でhtml, bodyタグに対してbackground-colorを設定します。


html,
body {
  padding: 0;
  margin: 0;
  background-color: aqua; //追加
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}

a {
  color: inherit;
  text-decoration: none;
}

* {
  box-sizing: border-box;
}

yarn devコマンドを実行してブラウザで確認するとbackgound-colorが設定されていることが確認できます。しかしStorybook上ではglobals.cssに追加したbackground-colorの設定は反映されません。globals.cssに設定した内容が反映されるためには.storybookフォルダ下にあるpreview.jsファイルでglobals.cssをimportします。


import "../styles/globals.css";

export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
};

ファイルを保存すると背景色にはglobals.cssに設定した内容が適用されます。

globals.cssへの設定の反映
globals.cssへの設定の反映

Next.jsでStorybookを利用するために抑えておきたい3つの設定方法を理解することができました。