React(Typescript)で Storybookを基本からまとめてみた【Storybook入門】

## Storybookとは？
UI コンポーネントをアプリケーションから独立した状態で開発を行うためのツールで、props を通してさまざまなバリエーションのデザインをブラウザ上に静的に表示させることができる。さらにブラウザ上から props の操作、外部リソースからのデータ取得、またテストライブラリーを利用して関数の実行、テストを行うことも可能で、また Storybook に登録したコンポーネントは自動でドキュメント化することができるのでドキュメントツールとして利用することもできる。

## 1.環境の構築

① プロジェクトの作成

```
npm create vite@latest
✔ Project name: … react-storybook7
✔ Select a framework: › React
✔ Select a variant: › TypeScript

cd react-storybook7
npm install
npm run dev
```

② Storybook のインストール

```
npx storybook@latest init
Need to install the following packages:
  storybook@7.0.17
Ok to proceed? (y) y

 storybook init - the simplest way to add a Storybook to your project.

 • Detecting project type. ✓
 • Detected Vite project. Setting builder to Vite. ✓
 • Adding Storybook support to your "React" app
  ✔ Getting the correct version of 10 packages
✔ We have detected that you're using ESLint. Storybook provides a plugin that gives the best experience with Storybook and helps follow best practices: https://github.com/storybookjs/eslint-plugin-storybook#readme

Would you like to install it? … yes
    Configuring Storybook ESLint plugin at .eslintrc.cjs
  ✔ Installing Storybook dependencies
. ✓
 • Preparing to install dependencies. ✓


up to date, audited 1043 packages in 2s

181 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
. ✓

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

To run your Storybook, type:

npm run storybook
```

## 2. StoryBook 設定
#### (1)Button コンポーネントの設定

① src フォルダの下に components フォルダ components の下に Button フォルダを作成しその下に Button.tsx ファイルと Button.stories.ts ファイルを作成する

```Button.tsx
type Props = {
  children: React.ReactNode,
};

function Button({ children }: Props) {
  return <button>{children}</button>;
}
export default Button;
```
② Button コンポーネントのストーリーを Button.stories.ts ファイルに記述する
※ ストーリーといっても何か特別なものではなく Button コンポーネントをブラウザ上でどのように描写するのかを決める JavaScript の関数で、ストーリーは Component Story Format(CSF)というフォーマットを利用して記述する。

```Button.stories.ts 
import type { Meta } from '@storybook/react';

import Button from './Button';

const meta = {
  title: 'Button',
  component: Button,
} satisfies Meta<typeof Button>;

export default meta;
```
③ストーリーは render 関数を利用して記述することもできる
※ render 関数の場合ファイルの中で JSX を記述するので拡張子を tsx に変更する必要がある。ts のままだとエラーになる。

```Button.stories.ts
import type { Meta, StoryObj } from '@storybook/react';

import Button from './Button';

const meta = {
  title: 'Button',
  component: Button,
} satisfies Meta<typeof Button>;

export default meta;

type Story = StoryObj<typeof Button>;

export const HelloButton: Story = {
  render: () => <Button>Hello World</Button>,
};
```

#### (2)表示するストーリーの制御

①  src フォルダ下に存在する stories.ts(tsx)ファイルが自動で検知されるように設定されているので.storybook フォルダに存在する設定ファイル main.ts の内容を変更する

```main.ts 
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
  // stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
  stories: [
    '../src/components/**/*.mdx',
    '../src/components/**/*.stories.@(js|jsx|ts|tsx)',
  ],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
  ],
  framework: {
    name: '@storybook/react-vite',
    options: {},
  },
  docs: {
    autodocs: 'tag',
  },
};
export default config;
```

#### (2)ストーリーの追加
① Button.stories.ts ファイルの中の metadata の title に設定した Button はサイドメニューに表示され、その中にストーリー Hello Button が表示される。
※ ボタンに表示される文字を変えたい場合には同じファイル(button.stories.ts)の中に新たにストーリーを追加することでテキストの変更によってデザインにどのような違いができるのかを確認することができる。HelloButton は render 関数、ClickButton は args を利用してストーリーを設定しているが表示に影響はない。

```Button.stories.ts
import type { Meta, StoryObj } from '@storybook/react';

import Button from './Button';

const meta = {
  title: 'Button',
  component: Button,
} satisfies Meta<typeof Button>;

export default meta;

type Story = StoryObj<typeof Button>;

export const HelloButton: Story = {
  render: () => <Button>Hello World</Button>,
};

export const ClickButton: Story = {
  args: {
    children: 'click',
  },
};
```
#### (3) title の変更

title を変更することでサイドメニューに表示される名前を変更することができますが階層化することも可能です。

```Button.stories.ts
const meta = {
  title: 'Common/Button',
  component: Button,
} satisfies Meta<typeof Button>
```
#### (4) .storybook フォルダの確認

① main.ts ファイルを開いて addons の中から addon-essentials をコメントする

```main.ts
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
  // stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
  stories: [
    '../src/components/**/*.mdx',
    '../src/components/**/*.stories.@(js|jsx|ts|tsx)',
  ],
  addons: [
    '@storybook/addon-links',
    // '@storybook/addon-essentials',
    '@storybook/addon-interactions',
  ],
  framework: {
    name: '@storybook/react-vite',
    options: {},
  },
  docs: {
    autodocs: 'tag',
  },
};
export default config;
```
## 3.ストーリーの追加/更新

#### (1) スタイルの適用 

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

```button.css
button {
  font-size: 14px;
  padding: 10px;
  border: 0;
  border-radius: 1em;
  cursor: pointer;
  display: inline-block;
}
```
② 作成した button.css ファイルを Button.tsx ファイルで import する

```Button.tsx 
import './button.css';

type Props = {
  children: React.ReactNode,
};

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

export default Button;
```

③ props の値によって背景色を変更できるように新たに props に color を追加する
※ デフォルト値を default とする。

```Button.tsx
import './button.css';

type Props = {
  children: React.ReactNode,
  color?: string,
};

function Button({ children, color = 'default' }: Props) {
  return <button className={color}>{children}</button>;
}

export default Button;
```
④ default の class を button.css に追加する
※ button 要素の文字の色を white に設定する。

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

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

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

※ ストーリーの名前を Default, Primary に設定して Primary では props で color の値を primary とする。

```button.strories.tsx
import type { Meta, StoryObj } from '@storybook/react';

import Button from './Button';

const meta = {
  title: 'Button',
  component: Button,
  tags: ['autodocs'],
} satisfies Meta<typeof Button>;

export default meta;

type Story = StoryObj<typeof Button>;

export const Default: Story = {
  args: {
    children: 'Default',
  },
};

export const Primay: Story = {
  args: {
    children: 'Primary',
    color: 'primary',
  },
};
```
#### (2)サイズの変更
① Button コンポーネントに背景色だけではなくサイズの変更もできるように props に size を追加する

```Button.tsx
import './button.css';

type Props = {
  children: React.ReactNode;
  color?: string;
  size?: string;
};

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

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

```button.css
//略

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

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

.lg {
  font-size: 18px;
  padding: 14px;
}
```
③ Button.stories.tsx にカラーとサイズを指定したストーリー(PrimarySmall, PrimaryLarge)を追加する

```Button.stories.tsx
export const PrimarySmall: Story = {
  args: {
    children: 'Primary',
    color: 'primary',
    size: 'sm',
  },
};

export const PrimaryLarge: Story = {
  args: {
    children: 'Primary',
    color: 'primary',
    size: 'lg',
  },
};
```





## 参考サイト
[TypeScript+Reactで始めるStorybook 7入門](https://reffect.co.jp/react/react-typescript-storybook7/)