Chapter 05

[基本編] .stories.js ファイル の作成

.stories.js の書き方

Storybook によるストーリーの定義は、(JSの場合) .stories.js という拡張子で定義します。

本書では、コンポーネント(.vue) の実装とストーリー(.stories.js) の定義を同じディレクトリに配置するので、 src/components/Button.vue のストーリーは src/components/Button.stories.js となります。

それでは Button コンポーネントのストーリーを書いてみましょう。

src/components/Button.stories.js
import Button from './Button'

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

const Template = (args, { argTypes }) => ({
  props: Object.keys(argTypes),
  components: { Button },
  template: `<Button v-bind="$props" /> `
})

export const Primary = Template.bind({})
Primary.args = {
  primary: true,
  secondary: false,
  label: 'プライマリーボタン'
}

export const Secondary = Template.bind({})
Secondary.args = {
  primary: false,
  scondary: true,
  label: 'セカンダリーボタン'
}

CSF(Component Story Format)について

もしかすると、これまでに Storybook を使っていて、 storiesOf 関数 や add 関数でストーリーを書かれていた方もいるかもしれません。

そのような関数(storiesOf API) は、現在では非推奨となり、 Storybook 6 では上記のようなESmodules(import/export) を使った、CSF(Component Story Format) が主流となります。

CSF の構造は至ってシンプルで

  • 一連のストーリーに関するメタデータをデフォルトエクスポート(export default) する
  • 各ストーリーを名前付きエクスポート(export const Hoge) する

さえ守れば、あとはどのようなコードを書いても構いませんし、CSF自体は Storybook に依存していないため、ここで宣言したストーリーを Storybook 以外で活用するといった想定もされているようです。

メタデータの宣言

では上記のCSFの詳細を見ていきましょう。

まず、デフォルトエクスポートで一連のストーリーのメタデータを定義します。ここでは一連のストーリーのタイトル及び、使用するコンポーネントのみ宣言していますが、他にも様々なメタデータの宣言が可能です。(以降の章で紹介)

src/components/Button.stories.js
export default = {
  title: 'Button',
  component: Button
}

また、上記では title: 'Button' のように、コンポーネント名をそのまま使用していますが、例えば以下のように Atomic Design の階層を記述することで、ストーリーに階層付けも行えます。

src/components/Button.stories.js
export default = {
  title: 'atoms/Button',
  component: Button
}

component 属性でコンポーネントを指定している理由は以降の章で判明するので、ここではお約束として宣言しておいてください。

テンプレートの宣言

src/components/Button.stories.js
const Template = (args, { argTypes }) => ({
  props: Object.keys(argTypes),
  components: { Button },
  template: `<Button v-bind="$props" /> `
})

ここでは、Button コンポーネントに関する各ストーリーを描画するための、共通テンプレートを宣言します。

テンプレートは Vue コンポーネント となるオブジェクトを返す関数を記述しますが、このとき (args, { argTypes }) を受け取り、それをコンポーネントにバインドすることで、動的なpropsの切り替えをできるようにします。

このあたりはお約束で文字通りテンプレなので、深く考えなくとも大丈夫です。

なお、このテンプレートから各ストーリーを派生させていくため、テンプレート自体をexportする必要はありません。

ストーリーの記述

作成したテンプレートを元に、ストーリーを記述します。

ストーリーは、テンプレートに渡す属性(≒props) を宣言し、名前付きエクスポートするだけです。

export const Primary = Template.bind({})
Primary.args = {
  primary: true,
  secondary: false,
  label: 'プライマリーボタン',
  size: 'medium',
  backgroundColor: ''
}

export const Secondary = Template.bind({})
Secondary.args = {
  primary: false,
  scondary: true,
  label: 'セカンダリーボタン',
  size: 'medium',
  backgroundColor: ''
}

Template から bind することで、同じテンプレートから props の構成だけが異なる2つのストーリーを簡易的に宣言することができます。

また、 export const Primary のように、名前付きエクスポートをすることで、 Primary がストーリー名となるので、名前付けを明示的に行う必要もありません。 (日本語名にしたいなど、明示的に名前をつける手段もあります)

動作確認

この状態でStorybookを起動してみましょう。

$ yarn storybook

以下のように、Primary Secondary 二種類のストーリーが描画されることが確認できました。