ComponentをStorybookで管理する方法

## Storybookとは
Componentをカタログのように管理したり、ビジュアルリグレッションテストをしたりすることができる、オープンソースツールです。
StorybookでComponentを管理することで、下記のようなメリットがあります。

* [コンポーネント駆動開発(CDD)](https://www.componentdriven.org/)ができる
    * Componentの状態を網羅的に確認しやすい
* デザイナーとの認識合わせがしやすい
* ビジュアルリグレッションテストを通して、デグレを検知することができる

## 準備

### インストール

```shell
npx sb init
```

###　起動

```shell
yarn run storybook
```

https://storybook.js.org/docs/react/get-started/install

## 基本的な書き方
Storybookはアドオンを追加することで、さまざまな拡張ができますが、
ベースとなる基本的な書き方のみご紹介します。

### ファイルの作成
基本的にコンポーネントファイルと同階層に、同じ名前の`stories.tsx|jsx|ts|tsx`ファイルを作成します。


### メタデータ
`stories`ファイルでは、Storybookのメタデータを`default export`します。

```Component.stories.tsx
import React, { ComponentProps } from 'react';
import { Meta, Story } from '@storybook/react';
import { Component } from './Component';

type Props = ComponentProps<typeof Component>;

const meta: Meta<Props> = {
  title: 'Component', // カタログのタイトルになる部分。 スラ(/)で区切ると階層ができる。
  component: Component, // ストーリーブック化するComponent
};

export default meta;
```

### ストーリー
ストーリーを作成するには、名前つきexportを用います。
変数名がそのままStorybookに表示されます。

```jsx
export const Default: Story = () => <Component />;
```

#### 名前を変える
Storybookに表示される名前を変更したい時は、`Story.storyName`の値を書き換えることで変更できます。

```jsx
export const Default: Story = () => <Component />;
Default.storyName = "My Component" // スペース含んでもok！
```

### ストーリーに引数を渡す
メタデータ、もしくはストーリー自体に引数を渡すことができます。

```jsx
const meta: Meta<Props> = {
  title: 'Component',
  component: Component,
  args: {
    title: "タイトル"
  }
};

export const Default: Story = (args)=> <Component {...args} />;

```

```jsx
export const Default: Story = () => <Component />;
Default.args = {
 title: "タイトル"  
}
```

### コントロールパネルを変更する
storybookの`essentials`アドオンを使うことで、引数をコントロールパネルで操作できるようにすることができます。
また、その操作パネルをカスタマイズし、コンポーネントの状態をカスタマイズしやすい状態にすることも可能です。

https://storybook.js.org/docs/react/essentials/controls

### パラメーターを設定する
viewportやbackgroundなど、storybookが持っている状態に対するパラメーターを設定することができます。

```jsx
const meta: Meta<Props> = {
  title: 'Component',
  component: Component,
  args: {
    title: "タイトル"
  },
  parameters: {
    viewports: {
      small: {
        name: 'iphone5/SE',
        styles: {
          width: '320px',
          height: '568px',
        },
      },
    },
    backgrounds: {
      values: [
        { name: 'red', value: '#f00' },
        { name: 'green', value: '#0f0' },
      ],
    },
  }
};

export const Default: Story = (args)=> <Component {...args} />;
```


### decoratorsを追加する
ストーリーをラップするスタイルの変更ができます。

```jsx
const meta: Meta<Props> = {
  title: 'Component',
  component: Component,
  args: {
    title: "タイトル"
  },
  decorators: [
    (story) => (
      <div
        style={{
          background: '#eee',
          padding: '16px',
          maxWidth: '300px',
        }}
      >
        {story()}
      </div>
    ),
  ],
};

export const Default: Story = (args)=> <Component {...args} />;
```




## Storybookはめんどくさい...？ 大変？
そうでもないです。
基本的にどのComponentに対しても書くことは一緒です。
そのため、エディタの機能を用いてスニペット化すると良いでしょう！

<details><summary>snippetのコードはこちら</summary><div>

```json
{
  "storybook": {
    "scope": "javascript,typescript,typescriptreact,javascriptreact",
    "prefix": ["storybook"],
    "body": [
      "import React, { ComponentProps } from 'react';",
      "import { Meta, Story } from '@storybook/react';",
      "import { $TM_FILENAME_BASE } from './$TM_FILENAME_BASE'",
      "",
      "type Props = ComponentProps<typeof $TM_FILENAME_BASE>;",
      "",
      "const meta: Meta<Props> = {",
      "  component: $TM_FILENAME_BASE ,",
      "  title: '$TM_FILENAME_BASE' ,",
      "  args: {",
      "    // ",
      "  },",
      "};",
      "",
      "const Template: Story<Props> = (args) => <$TM_FILENAME_BASE {...args} />",
      "",
      "export const Default = Template.bind({});",
      "",
      "export default meta;"
    ],
    "description": "storybook"
  }
}

```
</div></details>