Storybook v5.3のざっくり解説

こんにちはアソビューでテックリードやってます井上です。
コロナの影響でテレワークが続いており、運動不足です。(元々してない)
リングフィットアドベンチャーが買えないので筋トレ&30分散歩&Beat Saberを始めました。

さて、弊社ではフロントエンドは基本的にReactで開発しており、コンポーネントカタログとしてstorybookを活用していますが、バージョン5.3のアップデートに伴って設定周りが使いやすく一新されていましたので今回はそちらを簡単に紹介したいと思います。
古い設定からのマイグレーションの参考にしていただければと思います。

ちなみにすでに6.0もα版が進んでいてリリースを控えているのでそちらはまた追って。。

Storybook 6.0 Release 🏆 · Issue #9311 · storybookjs/storybook · GitHub

Storybookについて

f:id:masino83:20200316103959p:plain
storybook github
storybook はReact,Vueを始めとした様々なJSのUIライブラリやフレームワークのコンポーネントカタログやUT結果の表示をしてくれるツールです。
弊社でもプロダクトごとにstorybookを導入してコンポーネントカタログを生成して、開発時にすでにあるコンポーネントを把握して活用しやすいようにしたり、storyファイルを作ることで共有するコンポーネントの独立性を保つように心がけやすくしています。

storybook 5.3の設定方法

5.2 -> 5.3でシンプルになった設定周りについてかいつまんで説明します。
より詳しくはこちらを確認いただければと思います!

storybook/MIGRATION.md at next · storybookjs/storybook · GitHub

設定ファイルの構成

まず、各設定ファイルの名前、構成が変わっています。わかりやすくなりましたね。
大体こういう対応みたいです。(中身は結構違う)

preset.js -> main.js

config.js -> preview.js

addon.js -> manager.js

main.js

Reactの場合の説明はこちら https://storybook.js.org/docs/guides/guide-react/

storybookのメインの設定を書くファイルです。
具体的には下記のような記述になります。

module.exports = {
  stories: ['../**/*.stories.js'],
  addons: ['@storybook/addon-knobs', '@storybook/addon-actions'],
};

story fileのエントリーポイントの指定やaddonの設定を書く感じですね。
基本の設定はこれだけでOKです。シンプル!

これまでaddon.jsに下記のような形で書いてましたがそれは不要でmain.jsに書きます。

// 5.2までの書き方 addon.js
import '@storybook/addon-knobs/register'
import '@storybook/addon-actions/register'

storyファイルの書き方

export const storyName = () => (
  <SomeComponent />
)

こういう風にコンポーネントを直接exportする感じです。 結果、コンポーネント名がstory名に変換されます。 (camel case, snake caseが対応)
storyName or story_name--> Story Name 直感的でわかりやすいですね。

import React from 'react'

import base from 'paths.macro'
import { withKnobs, text } from '@storybook/addon-knobs'
import {action} from '@storybook/addon-actions'
import Button from '../'

export default {
  title: base.replace('/src/components/', '').replace('/__stories__/', ''),
  decorators: [withKnobs]
}

export const normal = () => (
  <Button
    label={text('label', 'ボタン')}
    onClick={action('clicked')}
  />
)

export const primary = () => (
  <Button
    label={text('label', 'ボタン')}
    type='primary'
    onClick={action('clicked')}
  />
)

export const primaryDisabled = () => (
  <Button
    label={text('label', 'ボタン')}
    type='primary'
    disabled
    onClick={action('clicked')}
  />
)

export const secondary = () => (
  <Button
    label={text('label', 'ボタン')}
    type='secondary'
    onClick={action('clicked')}
  />
)

export const secondaryDisabled = () => (
  <Button
    label={text('label', 'ボタン')}
    type='secondary'
    disabled
    onClick={action('clicked')}
  />
)

出力結果はこちら

f:id:masino83:20200402095450p:plain
storybookの表示結果

5.2以前はこういった書き方でした。

// 5.2以前のstory fileの書き方。
const stories = storiesOf('sp/atoms/Button', module)
stories.addDecorator(withKnobs)

stories.add('other', () => (
  <Button
    label={text('label', 'ボタン')}
    onClick={(e) => console.log(`click`)}
  />))

storybookのtitleについて

story fileのtitleの設定でstorybookのディレクトリ階層が指定できますが、ここを手で指定するのが地味に面倒でした。

// これまではパスの指定を手で書いていた
const stories = storiesOf('sp/atoms/Button', module)

babelプラグインのpath.macroを活用するとstorybook階層化が楽になります。

https://storybook.js.org/docs/basics/writing-stories/

import base from 'paths.macro'
 // console.log(base)  -> /src/components/atoms/Button/__stories__/
// このreplaceも書かないようにしたい..
export default {
  title: base.replace('/src/components/', '').replace('/__stories__/', ''),
  decorators: [withKnobs]
}

カスタムwebpackの読み込み

storybook用のwebpack設定を作らず、実際のアプリケーションビルドで使ってるconfigをそのまま読み込めるようになりました。

const custom = require('../webpack/config.base.js')

module.exports = {
  stories: ['../src/**/__stories__/index.js'],
  webpackFinal: config => {
    return {
      ...config,
      module: { ...config.module, rules: custom.module.rules }
    }
  },
  addons: ['@storybook/addon-knobs', '@storybook/addon-a11y']
}

まとめ

簡単ですが、今回は以上になります。
弊社ではデザインシステム運用のためにstorybookを活用してエンジニアやデザイナー、その他プロダクトメンバー間でコンポーネントの共有をできる形を構想しています。
figma連携のaddonなんかもあるので今度検証してみたいと思います。