ReactでmswとStoryBookを連携する

## 前提
前回のこちらの記事の続編になります。msw自体の説明や後述のコードはこちらを参考にしてください。 

https://qiita.com/stake15/items/50476b986c8dab8d7fcf

## StoryBookとは
Storybookは、UIコンポーネントとページを分離して構築するためのオープンソースツールです。UIの開発、テスト、およびドキュメント化に活用することができます。動作イメージや仕様については以下の公式を参考ください。

https://storybook.js.org/

## StoryBookの環境準備

StoryBookCLIのCLIをインストールします。

```bash
npm install -g @storybook/cli
```

StoryBook環境の初期化を行います。

```bash
npx sb init
```

StoryBookを起動します。実行のコマンドは環境によってことなりますが、上記コマンドの最後に出力されると思います。以下はcreate-react-app+npmの環境の場合です。

```bash
npm run storybook
```


create-react-appで作成したプロジェクトでは`@storybook/preset-create-react-app`のバージョンが低いと失敗したので、エラーが発生した場合、package.jsonを以下のように修正し、再度`npm i` を実行してください。

```jsx
"@storybook/preset-create-react-app": "^4.0.0",
```

</aside>

起動後のブラウザで以下のようなデフォルトのストーリーファイル(Button,Header,Page)が表示されます。

![reactmswstorybook1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2277523/dfba984d-3b4a-b491-0707-4b7392822ed3.png)


## msw連携

### storybook起動時にmswのServiceWorker起動

mswとmswのstorybookの連携アドオンをインストールします

```bash
npm i -D msw msw-storybook-addon
```

mswのServiceWorkerを設定します。（前回の記事で設定していればスキップ可能です）

```bash
npx msw init public/ --save
```

.storybook/preview.js にプラグインを追加し、起動するようにします。

```jsx
import { initialize, mswDecorator } from "msw-storybook-addon";

// Initialize MSW
initialize();

// Provide the MSW addon decorator globally
export const decorators = [mswDecorator];
```

.storybook/main.js を以下のように書き換えます。

```jsx
module.exports = {
  ...
  staticDirs: ["../public"],  //追加
};
```

package.jsonを以下のように書き換えます。(-sオプションは6.4以降でdeprecatedなので消します）

```json
"scripts": {
　　　　　　　　・・・
    "storybook": "start-storybook -p 6006",　//変更箇所
    "build-storybook": "build-storybook" //変更箇所
},
```

この時点で一度起動(`npm run storybook`)します。mswのServiceWorkerの起動に成功するとブラウザのコンソールログに以下のようなメッセージが表示されます。

![reactmswstorybook2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2277523/77da4556-7de7-aa1b-1182-55179d344211.png)


## ストーリーファイルからmswのAPIモックを呼び出す

前回作成したコンポーネント（UserList.tsx）に対するストーリーファイル(UserList.stories.tsx)を作成します。
前回カスタムフックのテスト用に作成したAPIモック(mocks/handlers.ts)をそのまま使うことができます。異常系のように普段のAPIと違う挙動をしたい場合、parameterを上書きすることもできます。

```tsx
import { handlers } from "../mocks/handlers";
import { rest } from "msw";
import UserList from "./UserList";

export default {
  title: "UserList",
  component: UserList,
};

export const SuccessBehavior = () => <UserList />;

SuccessBehavior.parameters = {
  // 正常系は定義済みのhandlersをそのまま利用
  msw: {
    handlers: handlers,
  },
};

export const FailureBehavior = () => <UserList />;

FailureBehavior.parameters = {
  // 異常系はhandlersを上書き
  msw: {
    handlers: [
      rest.get("/api/users", (req, res, ctx) => {
        return res(ctx.status(500), ctx.json({ message: "Internal Server Error" }));
      }),
    ],
  },
};
```

ストーリーファイルの作成場所を変更した場合、.storybook/main.jsも変更します。

```jsx
module.exports = {
  // stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
  stories: ["../src/components/**/*.stories.@(js|jsx|ts|tsx)"], //components配下に移動した場合
  ...
```

再度storybookを起動します。(main.js等を書き換えた場合、再起動が必要です）

```bash
npm run storybook
```

StoryBook上で通信結果に応じた表示を確認することができました。

- API通信成功時に表示されるコンポーネント(コンソールには200 OK)
    
![reactmswstorybook3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2277523/54bf28b0-a9c0-e046-97a0-db82cbcffe4c.png)    

- API通信失敗時に表示されるコンポーネント(コンソールには500 Internal Error)
    
![reactmswstorybook4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2277523/1f6a1ab4-2cbd-d605-6904-814e0e53c681.png)
    

## 感想

前回のテストにmswを導入するだけではメリットを感じにくかったですが、こうやってStoryBookのデータ連携でも簡単に使えるとモチベーションがあがりました。StoryBook自体まだまだ初心者なので色々覚えていきたいと思います。

## 参考

https://storybook.js.org/addons/msw-storybook-addon