Storybookはアプリケーションから切り離し独立した状態でコンポーネントの開発を行うために利用するツールです。この説明が最初は”?”でも本文書では公式のドキュメントよりもさらにシンプルなコードを利用しているのでStorybookの基礎的な利用方法や機能を短時間で理解することができます。

Storybookが利用できるJavaScriptのフレームワーク/ライブラリにはReact以外にもVue.js, React Native, Svelte, Amber, Angularなどがありますが本文書ではReactを利用しています。

Reactのインストール

create-react-appを利用してreact-storybookという名前のReactプロジェクトを作成します。プロジェクト名は任意の名前をつけてください。


 % npx create-react-app react-storybook

プロジェクトの作成が完了したら作成されるreact-storybookフォルダに移動します。


 % cd react-storybook

StoryBookのインストール

react-storybookフォルダでstorybook initコマンドを実行してstorybookのインストールを行います。


 % npx storybook init
Need to install the following packages:
  storybook
Ok to proceed? (y) 
//略
✅ migration check successfully ran


To run your Storybook, type:

   yarn storybook 

For more information visit: https://storybook.js.org

インストールが完了したらインストールメッセージに表示されている通りyarn strorybookを実行します。起動メッセージにバージョン(6.5.5)が表示されます。本文書ではStorybookのバージョン6.5.5を利用して動作確認を行います。


 % yarn strorybook
//略
99% done plugins webpack-hot-middlewarewebpack built preview b835dc28a07a2114afea in 15221ms
╭────────────────────────────────────────────────────╮
│                                                    │
│   Storybook 6.5.5 for React started                │
│   14 s for manager and 17 s for preview            │
│                                                    │
│    Local:            http://localhost:6006/        │
│    On your network:  http://192.168.2.110:6006/    │
│                                                    │
╰────────────────────────────────────────────────────╯
<i> [webpack-dev-middleware] wait until bundle finished: /
99% done plugins webpack-hot-middlewarewebpack built preview 27f8a5b932c644b98baa in 1200ms

インストールが完了するとブラウザが自動で起動しWelcomeページが表示されます。

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

左のメニューにあるButtonを展開するとLarge, Primary, Secondary, Smallが表示されLargeをクリックすると大きなボタン、Primaryだとブルーカラーのボタン、Smallだと小さなボタンというように同じボタンでも異なるデザインのボタンが表示されることが確認できます。Button, Header, Pageなどはコンポーネントに対応する名前でLarge, Primary,…などがStorybookではストーリーと呼びます。1つのコンポーネントに対して複数のストーリーを設定すると各ストーリーで設定した値を元にサイドメニューの右側のCanvasタブにデザインの異なるコンポーネントが描写されます。

Buttonを確認
Buttonを確認

サイドメニューに表示されているストーリーがどのファイルに記述されているか確認するためにstorybookをインストール後のフォルダ構成を確認します。プロジェクトフォルダ(storybook-tutorial)の中に2つのフォルダを確認することができます。1つはプロジェクトフォルダ直下に作成される.storybookフォルダ、もう一つはsrcフォルダ直下に作成されるstoriesフォルダです。

ブラウザ上に表示されている内容を記述したファイルはstoriesの中に保存されているので中身を確認するとブラウザ上されていたIntroduction, Button, Header, Pageに対応するファイルを確認することができます。それらのファイルの中でファイル名に*.stories.jsxが含まれているファイルがStorybookのストーリーを記述するファイルです。

storiesフォルダの中身を確認
storiesフォルダの中身を確認

Exampleとして作成されているそれらのファイルを確認することもできますが初めてStorybookを利用する人にとって少しハードルが高いのでstoriesフォルダの中身はスキップして別のフォルダで一からストーリーを設定していきます。

初めてのStoryBook設定

Buttonコンポーネントの設定

Buttonコンポーネントを利用してStorybookの動作確認をしたいのでcomponentsの下にButtonフォルダを作成しその下にButton.jsxファイルとButton.stories.jsxファイルを作成します。

Button.jsxファイルとButton.stories.jsxファイルは同じフォルダに作成する必要はありません。srcフォルダの下であればフォルダ構成は自由に決めることができます。

Button.jsxファイルに下記の内容を記述します。親コンポーネントからpropsでchildrenを受け取るだけのシンプルなコンポーネントです。


function Button({ children }) {
  return <button>{children}</button>;
}

export default Button;

ButtonコンポーネントのストーリーをButton.stories.jsxファイルに記述していきます。ストーリーといっても何か特別なものではButtonコンポーネントをどのように描写するのかを決めるJavaScriptの関数です。ストーリーはComponent Story Format(CSF)というフォーマットを利用して記述していきます。

export defaultの中にmetadataであるtitleとcomponentを設定します。titleはサイドメニューのストーリーをまとめていたButton, Header, Pageに対応します。componentはストーリーを設定するコンポーネントを指定します。コンポーネントはimportしておく必要があります。


import Button from './Button';

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

export defaultでmetadataを設定後その下にストーリーを記述します。ストーリーとしてHelloButtonを定義しています。HelloButton関数の中ではButtonタグの間に文字列を挿入し、挿入した文字列はprops.childrenでButtonコンポーネントに渡されます。


import Button from './Button';

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

export const HelloButton = () => <Button>Hello World!</Button>;

これでButtonコンポーネントに対するストーリーの設定は完了です。現在の設定ではsrcフォルダ下に存在するstories.jsxファイルが自動で検知されるように設定されているので.storybookフォルダに存在する設定ファイルの設定を変更します。

設定変更は必須ではありませんがゼロから作成したストーリーのみブラウザ上で表示させるために設定を行なっています。storiesフォルダのストーリーが表示されたままで問題ない場合はそのまま進めてください。

componentsフォルダ下でstories.jsxという名前が入ったファイルのみ自動検知できるように変更しています。


module.exports = {
  // "stories": [
  //   "../src/**/*.stories.mdx",
  //   "../src/**/*.stories.@(js|jsx|ts|tsx)"
  // ],
  stories: ['../src/components/**/*.stories.jsx'], //追加
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
    '@storybook/preset-create-react-app',
  ],
  framework: '@storybook/react',
  core: {
    builder: '@storybook/builder-webpack5',
  },
};

設定ファイルのmain.jsを更新した場合はStorybookを再起動する必要があるのでyarn storybookを再実行してください。

Button.stories.jsxファイルの中のmetadataのtitleに設定したButtonは左のサイドメニューに表示されその中にストーリーHello Buttonが表示されます。ストーリーHello Buttonを選択するとHello World!が入ったボタンが表示されます。

ブラウザからの確認
ブラウザからの確認

ボタンに表示される文字を変えたい場合には新たにストーリーを追加することでテキストに変更によってどのような違いができるのかを確認することができます。


import Button from '../Button/Button';

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

export const HelloButton = () => <Button>Hello World!</Button>;
export const ClickButton = () => <Button>Click!</Button>;

追加後にブラウザを確認するとClickButtonが追加され、ClickButtonを選択するとボタンには設定したテキストが表示されます。

ClickButtonの確認
ClickButtonの確認

titleの変更

titleを変更することでサイドメニューに表示される名前を変更することができますが階層化することも可能です。例えばtitleをButtonからCommon/Buttonに変更します。


export default {
  title: 'Common/Button',
  component: Button,
};

Buttonの上にCOMMONが表示され階層化されることが確認できます。

titleによる階層化
titleによる階層化

.storybookフォルダの確認

先ほど設定ファイルのmain.jsファイルの変更を行いましたがStorybookをインストールする作成される.stroybookフォルダには設定ファイルが保存されています。メインの設定ファイルであるmain.jsファイルを確認します。storiesにはstoryファイルのパスが配列で設定されています。この設定によりsrcフォルダ化に*.stories.jsxが自動で認識されることになります。addonsにはStorybookのアドオンが設定されてろStorybookの機能を拡張することができます。その他のオプションについてはStorybookのドキュメントから確認することができます。


module.exports = {
  "stories": [
    "../src/**/*.stories.mdx",
    "../src/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/addon-interactions",
    "@storybook/preset-create-react-app"
  ],
  "framework": "@storybook/react",
  "core": {
    "builder": "@storybook/builder-webpack5"
  }
}

addonsの確認

addonsでStorybookの機能の拡張といってもどのようなことが可能なのかわかりにくいかと思います。addonsの影響によってどのような変化があるのか確認しておきましょう。

デフォルトの状態の画面を見ると上部はさまざまなアイコン、下部にはControlsやActoinsなどのタブを確認することができます。

デフォルトの状態の画面
デフォルトの状態の画面

main.jsファイルを開いてaddonsの中からaddon-essentialsをコメントします。


module.exports = {
  // "stories": [
  //   "../src/**/*.stories.mdx",
  //   "../src/**/*.stories.@(js|jsx|ts|tsx)"
  // ],
  stories: ['../src/components/**/*.stories.jsx'],
  addons: [
    '@storybook/addon-links',
    // '@storybook/addon-essentials',
    '@storybook/addon-interactions',
    '@storybook/preset-create-react-app',
  ],
  framework: '@storybook/react',
  core: {
    builder: '@storybook/builder-webpack5',
  },
};

main.jsを更新した後は設定を反映させるためyarn storybookを再実行する必要があります。

ブラウザを確認するとCanvasの横のDocsが消え、上部に複数表示されていたアイコンの数が5つ消え下部のタグもInteractionsのみの表示になっています。

essentialのaddonsを利用していない場合
essentialのaddonsを利用していない場合

addonsのessentialは複数のaddonsが含まれており公式ドキュメントから確認することができます。確認するとDocs, Controls, Actions, Viewport, Backgrounds, Toolbars&globals, Measure&outlineが含まれていることがわかります。消えたアイコンや下部のタブはessentialに含まれるaddonsということがわかります。

このようにaddonsによってStorybookの機能拡張が行われていることを理解することができました。動作確認は再度main.jsを元の状態に戻してaddonsを利用できる状態にしてください。

ストーリーの追加

CSSの適用

ButtonコンポーネントにCSSが適用できるようにButtonフォルダの中にbutton.cssファイルを作成してbutton要素に対してCSSを適用します。


button {
  font-size: 14px;
  padding: 10px;
  border: 0;
  border-radius: 1em;
  cursor: pointer;
  display: inline-block;
}

作成したbutton.cssファイルをButton.jsxファイルでimportします。


import './button.css';
function Button({ children }) {
  return <button>{children}</button>;
}

export default Button;

CSSが適用されブラウザ上のボタンが変更されます。

CSSの適用
CSSの適用

propsの値によって背景色が変わるように新たにpropsにcolorを追加します。デフォルト値をdefaultとします。


import './button.css';
function Button({ children, color = 'default' }) {
  return <button>; className={color}>{children}</button>;
}

export default Button;

defaultのclassをbutton.cssに追加します。button要素の文字の色をwhiteに設定します。


button {
  font-size: 14px;
  padding: 10px;
  border: 0;
  border-radius: 1em;
  cursor: pointer;
  display: inline-block;
  color: white;
}

.default {
  background-color: #6c757d;
}

ブラウザ上のボタンが下記のように変わります。

propsのcolorを設定
propsのcolorを設定

propsのcolorの値によってボタンのデザインが変わるようにbutton.stories.jsxで設定したストーリーを変更します。


import Button from '../Button/Button';

export default {
  title: 'Common/Button',
  component: Button,
};

export const Default = () => <Button>Default</Button>;
export const Primary = () => <Button color="primary">Primary</Button>;

Primary関数の場合にはButtonタグのcolor propsにprimaryを設定していますがprimaryはbutton.cssに存在しないのでbutton.cssに追加します。


//略
.primary {
  background-color: #007bff;
}

Primaryのストーリーを選択すると背景色がブルーのボタンが表示されます。

Primaryを選択した場合
Primaryを選択した場合

さらに背景色の異なるストーリーのDangerを追加することもできます。


export const Default = () => <Button>Default</Button>;
export const Primary = () => <Button color="primary">Primary</Button>;
export const Danger = () => <Button color="danger">Danger</Button>;

CSSの追加も合わせて行います。


//略
.danger {
  background-color: #dc3545;
}
ストーリーのDangaerを追加
ストーリーのDangaerを追加

Buttonコンポーネントにcolor propsを設定することでストーリーにより色を変更することでどのようにボタンが表示されるかブラウザ上で確認できるようになりました。

サイズの変更

Buttonコンポーネントに背景色だけではなくサイズの変更もできるうにpropsのsizeを追加します。


import './button.css';
function Button({ children, color = 'default', size = 'base' }) {
  return <button className={`${color} ${size}`}>{children}</button>;
}

export default Button;

size propsのdefault値はbaseとしています。baseの他にsm, lgも設定できるようにsmとlgもbutton.cssファイルに追加しておきます。文字の大きさだけではなくpaddingも変更するためbutton要素に設定していたpaddingをbase, sm, lgで設定できるように変更します。


button {
  border: 0;
  border-radius: 1em;
  cursor: pointer;
  display: inline-block;
  color: white;
}

//略

.base {
  font-size: 14px;
  padding: 10px;
}

.sm {
  font-size: 12px;
  padding: 8px;
}

.lg {
  font-size: 18px;
  padding: 14px;
}

Button.stories.jsxにカラーとサイズを指定したストーリー(PrimarySmall, PrimaryLarge)を追加します。


import Button from '../Button/Button';

export default {
  title: 'Common/Button',
  component: Button,
};

export const Default = () => <Button>Default</Button>;
export const Danger = () => <Button color="danger">Danger</Button>;
export const Primary = () => <Button color="primary">Primary</Button>;
export const PrimarySmall = () => (
  <Button size="sm" color="primary">
    Small
  </Button>
);
export const PrimaryLarge = () => (
  <Button size="lg" color="primary">
    Large
  </Button>
);

設定が完了するとブラウザ上から”Primary Large”と”Primary Small”が選択できるようになります。Primary Largeの場合は大きなボタン、Primary Smallの場合は小さなボタンが表示されます。Primaryの場合は通常のサイズのボタンが表示されます。ストーリーによってサイズの異なるボタンをブラウザ上で確認できるようになりました。

Primary Largeを選択
Primary Largeを選択

Argsの利用

これまでの設定でもストーリーを設定することができましたがより柔軟にストーリーを記述するためにArgsを利用した記述方法を確認します。Argsはargumentの略です。

Argsを利用する場合はまず最初にTemplate関数を設定します。argsはコンポーネントに渡すpropsです。


const Template = (args) => <Button {...args} />;

ストーリーのDefault関数をTemplate関数のbindすることで新規の関数として作成します。作成後はargsを利用してpropsの値を設定します。


const Template = (args) => <Button {...args} />;
Default.args = {
  children: 'Default',
};

同様の方法でほかのストーリーも変更を行います。


const Template = (args) => <Button {...args} />;
export const Default = Template.bind({});
Default.args = {
  children: 'Default',
};

export const Danger = Template.bind({});
Danger.args = {
  children: 'Danger',
  color: 'danger',
};

export const Primary = Template.bind({});
Primary.args = {
  children: 'Primary',
  color: 'primary',
};

export const PrimarySmall = Template.bind({});
PrimarySmall.args = {
  children: 'Small',
  color: 'primary',
  size: 'sm',
};

export const PrimaryLarge = Template.bind({});
PrimaryLarge.args = {
  children: 'Large',
  color: 'primary',
  size: 'lg',
};

ブラウザ上に表示されるボタンはArgsを変更する前と何も変わりません。

Argの値は他のストーリーで利用した値を利用することができます。例えばPrimarySmallとPrimaryLargeはchildren, sizeが異なりcolorは同じ値を設定しています。PrimaryLarge.argsの設定でSpread Operatorを利用してPrimarySmall.argsの値を利用することができます。propsが多い場合には便利です。


export const PrimaryLarge = Template.bind({});
PrimaryLarge.args = {
  ...PrimarySmall.args,
  children: 'Large',
  size: 'lg',
};

Storybookが持つ機能の確認

Controlsの利用

Argsの設定を行うと下部に表示されていたControlsが利用できるようになります。

Controlsを利用することで現在ブラウザ上に表示されているボタンを変更することができます。下記の画面ではPrimaryを選択しているのでストーリーを選択した直後では背景色はブルーでボタンの文字列はPrimaryですがControlsを通して設定を変更することで背景色が赤でボタンの文字列をPrimaryDangerに変更することができます。

Controlsを通してボタンを変更
Controlsを通してボタンを変更

DefaultのストーリーをArgsを利用しない場合のDefault関数に戻すとControlsは下記のように表示され変更することはできません。

Argsを設定していない場合
Argsを設定していない場合

propsの型を設定

React/JavaScriptの場合PropTypesを設定することでpropsで渡される型の設定を行うことができます。Argsによってボタンに表示されるテキストやcolorをブラウザ上で変更できるようになりました。しかしcolor, sizeは手動で入力する必要があります。Button.jsxファイルの中でPropTypesを設定することでcolorやsizeで選択できる値に制限をかけることができます。下記ではsizeはsm, base, lg, colorはprimary, default, dangerのみ選択できるように設定しています。


import './button.css';
import PropTypes from 'prop-types';

function Button({ children, color = 'default', size = 'base' }) {
  return <button className={`${color} ${size}`}>{children}</button>;
}

export default Button;

Button.propTypes = {
  color: PropTypes.oneOf(['primary', 'default', 'danger']),
  size: PropTypes.oneOf(['sm', 'base', 'lg']),
};

ブラウザで確認すると先ほどまでとは異なり、ラジオボタンでcolor, sizeを選択できるようになりました。

PropTypes設定により選択の制限
PropTypes設定により選択の制限

ラジオボタンの変更すると変更した値はCanvasのボタンの描写に反映されます。

Actionsの確認

ButtonコンポーネントにonClickイベントを設定しpropsから渡された関数を設定した場合にActionsによりボタンのクリックを検知することができます。

propsで渡されるonClick関数をButtonコンポーネントのbutton要素のonClickイベントに設定します。


import './button.css';
import PropTypes from 'prop-types';

function Button({ children, color = 'default', size = 'base', onClick }) {
  return (
    <button className={`${color} ${size}`} onClick={onClick}>
      {children}
    </button>
  );
}

export default Button;

Button.propTypes = {
  color: PropTypes.oneOf(['primary', 'default', 'danger']),
  size: PropTypes.oneOf(['sm', 'base', 'lg']),
};

Actionsが正常に動作している場合はボタンをクリックすると下部のActionsタブにメッセージが表示されますが現在の設定では何も反応がありません。

Clickに対する反応なし
Clickに対する反応なし

PropTypesの設定にonClickを追加します。


Button.propTypes = {
  color: PropTypes.oneOf(['primary', 'default', 'danger']),
  size: PropTypes.oneOf(['sm', 'base', 'lg']),
  onClick: PropTypes.func.isRequired,
};

再度ボタンをクリックするとクリックを検知してメッセージが表示されます。下記では3回ボタンをクリックしています。

Clickイベントの検知
Clickイベントの検知

propsで渡す関数の名前をonClickからhandleClickに変更します。変更するとまたイベントを検知できなくなります。その理由は.storybookフォルダのStorybookの設定ファイルの一つであるpreview.jsファイルにactionsに関する設定が行われておりonから開始する関数名のみ検知することができるためです。


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

handleClick関数の名前を変えず、preview.jsファイルの設定を変えずにclickイベントを検知できるように設定を行います。ButtonコンポーネントのhandleClick関数だけ検知できようにするためには以下のようmetadataの中にargTypesの設定を行うことで実現できます。


export default {
  title: 'Common/Button',
  component: Button,
  argTypes: { handleClick: { action: 'clicked' } },
};

argTypesの中のactionで指定した’clicked’はActionsタブのメッセージの先頭に表示されます。

argTypes設定後の動作確認
argTypes設定後の動作確認

actionの値をtrueに変更すると関数名が表示されます。


export default {
  title: 'Common/Button',
  component: Button,
  argTypes: { handleClick: { action: true } },
};

1回目は先ほどの”clicked”設定時のメッセージで2回目がactionの値をtrueにした後のメッセージです。

actionの値の変更によるメッセージへの影響
actionの値の変更によるメッセージへの影響

そのほかのActionsについての設定方法については公式ドキュメントに記載されています。

Interactionsの設定

Interactionsはストーリーでコンポーネントが描写された後にユーザによるインタラクションをシュミレートすること時に利用することができます。ボタンコンポーネントであればストーリーの中でボタンをクリックすることができます。

Interactionsではtesting-library、jestの機能を利用します。そのほかにaddonsの@storybook/addon-interactionsが必要になります。デフォルトで’@storybook/addon-interactionsとstorybook/testing-libraryのインストールは行われているのでstorybook/jestのみ追加でインストールします。jestの機能を利用しない場合は必要ありません。ここではjestのexpect関数を利用するのでインストールしています。


 % npm install @storybook/jest

InterationsはPrimaryLargeストーリーを利用して行います。Interactionsの設定は下記のようにplay関数の中に記述します。引数でargsとcanvasElementを受け取ることができます。


PrimaryLarge.play = async ({ args, canvasElement }) => {
  //処理
};

今回のButtonコンポーネントであればargsは以下のオブジェクトです。


{
  children: "Large"
  color: "primary"
  handleClick: ƒ ()
  size: "lg"
}

canvasElementはidにrootの値を持つdiv要素で囲まれたButtonコンポーネントのDOMです。


<div id="root">
  <button class="primary lg">Large</button>
</div>

button要素に取得する際に利用するgetByRoleメソッドを利用するためにwithin関数の引数にcanvasElementを指定します。取得したボタンのクリックイベントを実行するためにuserEventのclickメソッドを実行します。実行後にhandleClick関数が実行されたかMatcher関数のtoHaveBeenCalled関数でチェックを行なっています。


PrimaryLarge.play = async ({ args, canvasElement }) => {
  const canvas = within(canvasElement);
  await userEvent.click(canvas.getByRole('button'));
  await expect(args.handleClick).toHaveBeenCalled();
};

ブラウザ上でPrimaryLargeストーリーを選択した後にplay関数が実行され下部のInterationsのタブに2件のメッセージが表示されます。

Interationsタグにメッセージが表示
Interationsタグにメッセージが表示

Actionsのタグを見るとクリックが行われているので1件メッセージが表示されます。

Actionsタブの確認
Actionsタブの確認

ここでのInteractionsはクリックを実行し、クリックが実行されたかどうかのチェックを行っているだけですがInteractionsによってボタンの要素の見た目が変わるようにButton.jsxファイルの設定を変更します。

useState Hookでmessageを定義します。handleClick関数はpropsで渡されるのではなくButtonコンポーネントの中で定義し、実行するとclickedがmessageに設定されるようにしています。


import './button.css';
import PropTypes from 'prop-types';
import { useState } from 'react';

function Button({ children, color = 'default', size = 'base' }) {
  const [message, setmessage] = useState('');

  const handleClick = () => {
    setmessage('clicked');
  };
  return (
    <button className={`${color} ${size}`} onClick={handleClick}>
      {children} {message}
    </button>
  );
}

export default Button;

Button.propTypes = {
  color: PropTypes.oneOf(['primary', 'default', 'danger']),
  size: PropTypes.oneOf(['sm', 'base', 'lg']),
};

PrimaryLargeストーリーのplay関数の中ではbuttonをクリックする設定のみ残してexpect関数によるチェックは削除しています。


PrimaryLarge.play = async ({ canvasElement }) => {
  const canvas = within(canvasElement);
  await userEvent.click(canvas.getByRole('button'));
};

ブラウザでPrimaryLargeストーリーを選択するとボタンはLarge clickedと表示されます。先程よりも描写されているボタンに変化があるのでInterationsを利用することでユーザとのインタラクションを行えるという説明も理解できるかと思います。

Interactionsによるボタンの変化
Interactionsによるボタンの変化

Canvasの表示設定

Parametersの設定

デフォルトの状態ではボタンが表示されているCanvasの背景色はlightかdarkのみ選択することができます。

backroundの変更はデフォルトで利用できる@storybook/addon-essentialsのaddonsに含まれています。
背景色の選択
背景色の選択

lightに設定されているのでdarkに変更すると背景色が暗くなります。

背景色をdarkに
背景色をdarkに

light, dark以外の背景色を設定したい場合にParametersを以下のように設定することができます。Primaryのストーリーのみで選択できるように設定を行っており、選択できる背景色はred, green, blueです。


export const Primary = Template.bind({});
Primary.args = {
  children: 'Primary',
  color: 'primary',
};
Primary.parameters = {
  backgrounds: {
    values: [
      { name: 'red', value: '#f00' },
      { name: 'green', value: '#0f0' },
      { name: 'blue', value: '#00f' },
    ],
  },
};

設定後light, darkではなくred, green, blueが選択できるようになります。

Parametersで設定したカラーの選択
Parametersで設定したカラーの選択

実際にgreenを選択します。背景色がgreenになることが確認できます。

背景色をグリーンに
背景色をグリーンに

個別のストーリーではなくButton内のすべてのストーリーでParametersの選択を行いたい場合はexport defaultのmetadataの中で設定を行うことができます。


export default {
  title: 'Common/Button',
  component: Button,
  parameters: {
    backgrounds: {
      values: [
        { name: 'red', value: '#f00' },
        { name: 'green', value: '#0f0' },
        { name: 'blue', value: '#00f' },
      ],
    },
  },
};

Buttonコンポーネントに含まれるストーリーで背景色の設定が可能になります。

Decoratorsの設定

Decoratorsを利用することでCanvasに表示されているButtonコンポーネントの外側にスタイルを設定することができます。

現在左上に表示されているButtonコンポーネントをCanvasの真ん中に表示したい場合には以下のようにDecoratorsを利用することで実現することができます。


export default {
  title: 'Common/Button',
  component: Button,
  decorators: [
    (Story) => (
      <div
        style={{
          display: 'flex',
          justifyContent: 'center',
          alignItems: 'center',
          height: '100vh',
        }}
      >
        <Story />
      </div>
    ),
  ],
};

ブラウザ上ではボタンが中央に表示されることが確認できます。

Decoratorsでボタンを中央に表示
Decoratorsでボタンを中央に表示

Layoutの設定

Decoratorsを利用することでButtonコンポーネントの外側にスタイルを設定することができました。style属性で設定を行いましたが中央に表示するのであればparametersのlayoutを利用することができます。


export default {
  title: 'Common/Button',
  component: Button,
  parameters: {
    layout: 'centered',
  },
};

centeredを設定することで下記のように表示されます。

layoutにより中央表示
layoutにより中央表示

設定値には”centered”以外にデフォルトの”padded”と”fullscreen”があります。paddedではpaddingが設定されていますがfullscreenにするとpaddingがなくなります。


export default {
  title: 'Common/Button',
  component: Button,
  parameters: {
    layout: 'fullscreen',
  },
};

fullscreenでは下記のように表示されます。

layoutでfullscreenを設定
layoutでfullscreenを設定

Docsの設定

これまでCanvasのみ表示していましたがCanvasの右側にDocsがありこの機能もaddonsにより実現されています。addonsの@storybook/addon-essentialsに含まれています。

Docsではコンポーネントに関する情報を確認することができます。PropsTypeで設定したデフォルト値やcolor, sizeの選択項目も確認することができます。

Docsの確認

Button.stories.jsxファイルのexport defaultに設定したcomponentをコメントすると表示されなくなります。


export default {
  title: 'Common/Button',
  // component: Button,
};

DescriptionやDefault値が表示されていません。

Docsの情報が表示されていない画面
Docsの情報が表示されていない画面

表示される情報を増やしたい場合はButton.jsxにコメントを追加することでDocsに表示されます。


import './button.css';
import PropTypes from 'prop-types';

/**
 * Button Component for explanation of StoryBook
 */

function Button({ children, color = 'default', size = 'base', handleClick }) {
//略

記述した”Button Component for explanation of StoryBook”が表示されます。

Button.jsxに記述したコメントが表示
Button.jsxに記述したコメントが表示

propTyesの上にコメントを追加すると追加したコメントがDescriptionの中に表示されます。


Button.propTypes = {
  /**
   * What background color to use
   */
  color: PropTypes.oneOf(['primary', 'default', 'danger']),
  /**
   * How large should the button be?
   */
  size: PropTypes.oneOf(['sm', 'base', 'lg']),
};

colorのDescriptionの中に”What background color to use”、sizeのDescriptionの中に”How large should the button be?”が表示されます。

PropTypesのコメント表示
PropTypesのコメント表示

コメントについてはButton.stories.jsxファイルでも設定することができます。


export default {
  title: 'Common/Button',
  component: Button,
  parameters: {
    docs: {
      description: {
        component: '説明用のボタンコンポーネント',
      },
    },
  },
};

記述した内容が表示されます。

Button.stories.jsxファイルで記述した内容が表示
Button.stories.jsxファイルで記述した内容が表示

Exampleの確認

main.jsファイルのstoriesの値を最初の値に戻します。


module.exports = {
  stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  // stories: ['../src/components/**/*.stories.jsx'],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
    '@storybook/preset-create-react-app',
  ],
  framework: '@storybook/react',
  core: {
    builder: '@storybook/builder-webpack5',
  },
};

これで作成したButtonコンポーネントも含めて本プロジェクトに含まれるストーリーがすべて表示されます。

デフォルトのストーリーを表示
デフォルトのストーリーを表示

本文書を読み終えた人であれば最初にstoriesフォルダのファイルに記述されていた内容がわからなかった人も理解できるようになっているはずです。ぜひ確認してもらいさらにStorybookの理解を深めてください。