TailwindCSS, Storybookを使った、Next.jsのかんたん環境構築

# はじめに
TailwindCSS、Storybookを使用するNext.jsアプリケーションの環境を作成する機会があり、そのときの環境構築の手順の覚え書き📝


# バージョン
- Next.js : `13.4.5`
- TailwindCSS : `3.3.2`
- Storybook : `7.0.20`


# 手順
## 1. Next.jsアプリを作成
```zsh
npx create-next-app アプリ名
```
使用するライブラリなどを対話式で選択していく。（今回はデフォルトのまま）
![スクリーンショット 2023-06-12 10.00.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823930/35bc7606-5f8a-591d-50de-d06b231b0d02.png)




## 2. Storybookをインストール
作成したNext.jsアプリのルートディレクトリで下記を実行。
```zsh
npx storybook@latest init
```

![スクリーンショット 2023-06-12 10.11.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823930/c1cce245-4923-4c74-e7b5-3e504eaa519b.png)

eslint-plugin-storybookを使用するか？→`Yes`とする

https://github.com/storybookjs/eslint-plugin-storybook#readme

:::note info
ここまでで、Next.js上でStorybookを使用することができるようになる。かんたん。
:::




## 3. TailwindCSS が storybook に反映されるように設定
### 3-1. @storybook/addon-postcss をインストール
```
npm install --save-dev @storybook/addon-postcss
```

![スクリーンショット 2023-06-12 10.21.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823930/9a8f270e-3911-5eb0-8718-b8fc32f71759.png)


### 3-2. 上記アドオンを使用する設定、静的ファイル置き場のパスを指定

`.storybook/main.ts`に以下の設定をする

- `addon-postcss`を使用する設定
- Storybookで読み込む静的ファイルのディレクトリのリストを設定

```diff_typescript:.storybook/main.ts
import type { StorybookConfig } from "@storybook/nextjs";
const config: StorybookConfig = {
  stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
  addons: [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/addon-interactions",
+   {
+     name: "@storybook/addon-postcss",
+     options: {
+       postcssLoaderOptions: {
+         implementation: require("postcss"),
+       },
+     },
+   },
  ],
  framework: {
    name: "@storybook/nextjs",
    options: {},
  },
  docs: {
    autodocs: "tag",
  },
+ staticDirs: ["../public"],
};
export default config;
```
:::note warn
Next.jsのImageコンポーネントなどで/public配下の画像を読み込む記述をしているとき、Storybook上でうまく表示されないっぽいので設定が必要
:::



### 3-3. TailwindCSSをStorybookに読み込む
`.storybook/preview.ts`に以下の設定をする
- `/src/app/globals.css` をimportする

```diff_typescript:.storybook/preview.ts
import type { Preview } from "@storybook/react";
+ import "../styles/globals.css";

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

export default preview;
```
:::note warn
Storybookでは各`*.stories.ts` にて、コンポーネントをimportしてStoryを作成する。このとき、このコンポーネント単体ではTailwindCSSのスタイルを読み込んでいないため、全体に適用するための設定が必要っぽい。
（`src/app/layout.tsx`でやっていることとおなじ感じ？）

:::


## 4. TailwindCSSを使ってコンポーネントを作成→動作確認
### 4-1. Buttonコンポーネントの作成
```typescript:src/components/atoms/Button.tsx
interface ButtonProps {
  primary?: boolean;
  fontSize?: 'sm' | 'base' | 'lg';
  innerText: string;
  onClick: () => void;
}

export const Button = ({
  primary = false,
  fontSize = 'base',
  innerText,
  onClick,
}: ButtonProps) => {
  const buttonColor = primary
    ? 'text-white bg-blue-400'
    : 'text-gray bg-transparent';

  return (
    <button
      type="button"
      className={[
        'font-bold border border-solid border-gray-100 rounded-2xl cursor-pointer inline-block leading-none py-2 px-4 shadow-md',
        `text-${fontSize}`,
        buttonColor,
      ].join(' ')}
      onClick={onClick}
    >
      {innerText}
    </button>
  );
};
```

### 4-2. Storybookファイルの作成
```typescript:src/components/atoms/Button.stories.ts
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';

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

export default meta;
type Story = StoryObj<typeof Button>;

export const Primary: Story = {
  args: {
    primary: true,
    innerText: 'Button',
  },
};

export const Secondary: Story = {
  args: {
    innerText: 'Button',
  },
};

export const Large: Story = {
  args: {
    fontSize: 'lg',
    innerText: 'Button',
  },
};

export const Small: Story = {
  args: {
    fontSize: 'sm',
    innerText: 'Button',
  },
};

```


### 4-3. 動作確認
```zsh
npm run storybook
```
`http://localhost:6006`に立ち上がる
![スクリーンショット 2023-06-12 11.05.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823930/6da3a51b-7c28-6405-d43b-6940c2805b73.png)


# おわりに
- Stroybookの進化はやい。自動でDocsとかできる。すごい。
- 不足していることあるかも。これから開発していくにあたって他に必要なことがあったら追記していきたい。
- 何か間違いなどありましたらご教示お願いいたします。




# 参考
- https://storybook.js.org/recipes/next
- https://storybook.js.org/docs/react/api/main-config-static-dirs