mokajima.com

Storybook のアップデート

はじめに

ESLint と Prettier のアップデートstylelint のアップデートに引き続き、Storybook のアップデートを行いました。yarn upgrade-interactive --latest で一括アップデートしています。

npm パッケージのバージョンの差異

Storybook の npm パッケージの差異は以下の通りです。5.3.x から 6.1.x へのアップデートとなりました。

-    "@storybook/addon-a11y": "^5.3.17",
-    "@storybook/addon-actions": "^5.3.17",
-    "@storybook/addon-backgrounds": "^5.3.18",
-    "@storybook/addon-info": "^5.3.17",
-    "@storybook/addon-knobs": "^5.3.17",
-    "@storybook/addon-links": "^5.3.17",
-    "@storybook/addon-storyshots": "^5.3.18",
-    "@storybook/preset-create-react-app": "^2.1.1",
-    "@storybook/react": "^5.3.17",
-    "@types/babel-plugin-macros": "^2.8.1",
+    "@storybook/addon-a11y": "^6.1.18",
+    "@storybook/addon-essentials": "^6.1.18",
+    "@storybook/addon-links": "^6.1.18",
+    "@storybook/addon-storyshots": "^6.1.18",
+    "@storybook/preset-create-react-app": "^3.1.6",
+    "@storybook/react": "^6.1.18",
     "@types/prettier": "^2.1.6",
     "@types/react-test-renderer": "^16.9.2",
-    "@types/storybook__addon-info": "^5.2.1",
     "@types/stylelint": "^9.10.1",
     "@typescript-eslint/eslint-plugin": "^4.13.0",
     "@typescript-eslint/parser": "^4.13.0",
     "axios-mock-adapter": "^1.17.0",
-    "babel-plugin-macros": "^2.8.0",
     "eslint-config-airbnb": "^18.2.1",
     "eslint-config-prettier": "^7.1.0",
     "eslint-plugin-import": "^2.22.1",
@@ -67,16 +61,10 @@
     "eslint-plugin-react-hooks": "^4.2.0",
     "jest-fetch-mock": "^3.0.1",
     "prettier": "^2.2.1",
-    "react-docgen-typescript-loader": "^3.7.1",
-    "react-docgen-typescript-webpack-plugin": "^1.1.0",
     "react-test-renderer": "^16.13.1",
     "stylelint": "13.10.0",
     "stylelint-config-standard": "^20.0.0",

TypeScript 関連の設定を削除

Storybook 6.0 から TypeScript がビルトインサポートされたため、TypeScript 関連の設定が不要となりました。

https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#zero-config-typescript

そのため .storybook/webpack.config.js を削除しました。また、.storybook/webpack.config.js の削除に伴い不要となった react-docgen-typescript-loaderreact-docgen-typescript-webpack-plugin を削除しています。

.storybook/webpack.config.js

-const path = require('path')
-const SRC_PATH = path.join(__dirname, '../src')
-
-module.exports = ({ config }) => {
-  config.module.rules.push({
-    test: /\.(ts|tsx)$/,
-    include: [SRC_PATH],
-      use: [
-        {
-          loader: require.resolve('babel-loader'),
-          options: {
-            presets: [['react-app', { flow: false, typescript: true }]],
-          }
-        },
-        { loader: require.resolve('react-docgen-typescript-loader') }
-      ]
-  })
-  config.resolve.extensions.push('.ts', '.tsx')
-  return config
-}

@storybook/addon-essentials を追加

@storybook/addon-essentials は Storybook のアドオンのコレクションです。@storybook/addon-actions@storybook/addon-backgrounds@storybook/addon-controls@storybook/addon-docs@storybook/addon-viewport@storybook/addon-toolbars の 6 つのアドオンを含み、.storybook/main.jsaddons@storybook/addon-essentials を追加するだけで使用できます。

@storybook/addon-essentials の追加に伴い @storybook/addon-actions@storybook/addon-backgrounds の個別インストールが不要となったため削除しました。

また、@storybook/addon-docs@storybook/addon-info の、@storybook/addon-controls@storybook/addon-knobs の後継のため、不要となった @storybook/addon-info@storybook/addon-knobs を削除しました。

.storybook/main.js

 module.exports = {
   stories: ['../src/components/**/*.story.tsx'],
   addons: [
     '@storybook/addon-a11y',
-    '@storybook/addon-actions',
-    '@storybook/addon-backgrounds',
-    '@storybook/addon-knobs',
+    '@storybook/addon-essentials',
     '@storybook/addon-links',
     '@storybook/preset-create-react-app'
   ]
 }

.storybook/preview.js

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

-addDecorator(withInfo)
-addDecorator(withKnobs)

なお、@storybook/addon-info は Storybook 6.0 で非推奨となっており、@storybook/addon-knobs は Storybook 7.0 より後で非推奨となる可能性があります。

https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#60-deprecations https://github.com/storybookjs/storybook/tree/master/addons/controls#how-will-this-replace-addon-knobs

ストーリーファイルの書き換え

Args

Storybook 6.0 で Args が導入されました。

https://medium.com/storybookjs/introducing-storybook-args-2dadcdb777cc

Args はストーリーファイルの新しい書き方です。Args とは Storybook のアドオンによってストーリーに渡される動的データのことを指します。Args を使ってストーリーファイルを書き換えることで、@storybook/addon-essentials に含まれるアドオンを簡単に利用できます。

そこで、ストーリーファイルを従来の書き方から Args を使った書き方に書き換えました。以下は場所のリストを表す LocationsList component のストーリーファイルを書き換えた例です。

変更前

import React from 'react'
import { number, object } from '@storybook/addon-knobs'

// ...

const noop = () => {}

export default {
  component: LocationList,
  title: 'LocationList'
}

export const Default = (): JSX.Element => (
  <LocationList
    locations={locations.map((location) => object(location.name, location))}
    tabIndex={number('tabIndex', 0)}
    venue={object('venue', venue)}
    onClickLocation={noop}
  />
)

変更後

import { Story } from '@storybook/react/types-6-0'
import React from 'react'

// ...

export default {
  component: LocationsList,
  title: 'LocationsList'
}

const Template: Story<Props> = (args): JSX.Element => (
  <LocationsList {...args} />
)

export const Default = Template.bind({})
Default.args = {
  locations,
  tabIndex: 0,
  venue
}

props を Storybook の UI 上で編集可能にする

ストーリーファイルの変更前ではコンポーネントに渡す props を Storybook の UI 上で編集できるようにするために、 @storybook/addon-knobs の関数をインポートし、props を渡す際にその関数を噛ませる必要がありました。一方、変更後のように Args を使った書き方でストーリーファイルを書けば、それだけで Storybook の UI 上で props を編集することが可能となります。これは @storybook/addon-controls によるものです。

イベントハンドラの実行をログ出力する

LocationsList component は onClickLocation というイベントハンドラを props として受け取り、リスト要素のクリック時にイベントハンドラを実行します。Storybook の UI 上でイベントハンドラが実行されたことを確認するには @storybook/addon-actions を使います。

@storybook/addon-actions を使いたいイベントハンドラの命名規則を .storybook/preview.js で指定しておくと @storybook/addon-actions がイベントハンドラを作成し、該当する props に渡してくれます。

.storybook/preview.js

 import '../src/index.css'

+export const parameters = {
+  actions: { argTypesRegex: '^on[A-Z].*' }
+}

先程の LocationsList component のストーリーファイルの変更前では LocationsListonClickLocation を渡していましたが、変更後のストーリーファイルでは Default.argsonClickLocation が含まれていないことがわかります。しかし、実際には @storybook/addon-actions によって onClickLocationLocationsList component に渡されています。

Storybook の UI 上でリスト要素をクリックしてみると、Actions タブにログが出力され、イベントハンドラが実行されたことがわかります。

ストーリーの背景色を指定する

ストーリーの背景色の指定は @storybook/addon-backgrounds を使います。アップデートに伴い @storybook/addon-backgrounds の API が変更となりました。Storybook 6.0 以降では配列ではなくオブジェクトを受け取ります。

https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#backgrounds-addon-has-a-new-api

以下は Loader component のストーリーファイルを書き換えた例です。

 export default {
   component: Loader,
   parameters: {
-    backgrounds: [
-      {
-        name: 'milk',
-        value: theme.chocolate.milk,
-        default: true
-      }
-    ]
+    backgrounds: {
+      default: 'milk',
+      values: [{ name: 'milk', value: theme.chocolate.milk }]
+    }
   },
   title: 'Loader'
 }

参考