Vue3 x Vuetify3のPJにStorybook7を入れる

## はじめに

とあるVue3 x Vuetify3のPJにおいて、コンポーネントの手動管理が手間だったので[Storybook7](https://storybook.js.org/)を導入した。
その際、VuetifyをStorybook上で表示する設定について、情報が少なかったため、導入の過程をここにメモ。

## 環境情報

```shell
$ npm list vue
└─ vue@3.3.4

$ npm list vuetify
└─ vuetify@3.3.5

$ npm list storybook
└─ storybook@7.0.22
```

## 手順

### 環境の作成

Vue3とVuetify3の初期PJを作成。

```shell
$ npm create vuetify
```

Storybook7をインストール。

```shell
$ npx storybook@latest init
```

### Storybookの設定

ここからはStorybook上でVuetifyコンポーネントを表示する設定を行う。

まず、Storybook側でVuetifyを読み込む。


```typescript:preview.ts (抜粋)
setup((app) => {
  // regsiterPluginsはnpm create vuetify`で作成される関数で、
  // webfont、Vuetifyを読み込んでいる。
  registerPlugins(app);
});
```


次に、[decorator](https://storybook.js.org/docs/react/writing-stories/decorators)を用意する。

```typescript:withVuetifyTheme.decorator.ts
import { VApp } from "vuetify/components";
import { Decorator, StoryContext } from "@storybook/vue3";

export const withVuetifyTheme: Decorator = (story, context) => {
  return {
    components: { story, VApp },
    // それぞれのstoryをv-appタグでラップ
    template: `
      <v-app>
          <story/>
      </v-app>
    `,
  };
};
```

preview.tsでdecoratorを登録。

```typescript:preview.ts (抜粋)
const preview: Preview = {
  ... // 省略
  decorators: [withVuetifyTheme], // 追加
};
```

storybookを起動。

```shell
$ npm run storybook
```

下記のように、Vuetifyのコンポーネントが表示されたら成功。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290859/a8fac676-5530-6f7e-db3c-2287090fe0b1.png)

### 【オプション】 Storybook上でThemeを可変にする。

PJによってはダークモード実装などで、Vuetifyの[Theme](https://vuetifyjs.com/en/features/theme/)を使用する場合がある。
その際、decoratorのv-appタグにthemeを設定すれば変更できるが、いちいちdecoratorを書き換えるのは面倒、、、

そこでStorybookの[Toolbars & globals](https://storybook.js.org/docs/react/essentials/toolbars-and-globals)を使用することで、ThemeをUIから可変にできる。

まずは、Storybookの設定にTheme用のtoolbarとglobalsを追加

```typescript:preview.ts (抜粋)
const preview: Preview = {
  ... // 省略
  globalTypes: {
    theme: {
      name: "Theme",
      description: "Global theme for components",
      defaultValue: DEFAULT_THEME,
      toolbar: {
        icon: "paintbrush",
        // tool barに表示されるitemの詳細
        items: [
          { value: "light", title: "Light", left: "🌞" },
          { value: "dark", title: "Dark", left: "🌛" },
        ],
        // 選択した値に基づいてtool bar上の表示を変更
        dynamicTitle: true,
      },
    },
  },
};
```

先程追加したdecoratorで選択したThemeを読ませる。

```typescript:withVuetifyTheme.decorator.ts
import { VApp } from "vuetify/components";
import { Decorator, StoryContext } from "@storybook/vue3";

export const DEFAULT_THEME = "light";

export const withVuetifyTheme: Decorator = (story, context) => {
  // Themeが未設定の場合、lightにする
  const globalTheme = context.globals.theme || DEFAULT_THEME;
  return {
    components: { story, VApp },
    // VuetifyにThemeを設定する
    template: `
      <v-app theme="${globalTheme}">
        <story/>
      </v-app>
    `,
  };
};
```

Storybook UI上部からThemeを選択し、**ブラウザをリロード**。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290859/b97ed3e5-16a4-d697-c817-51261b7c141e.png)

:::note warn
[公式の説明](https://storybook.js.org/docs/react/essentials/toolbars-and-globals#globals
)によると、globalsを変更すれば再レンダリングされるとあるが、当方の環境ではブラウザをリロードしないとThemeの変更が反映されなかった。
この事象については、[公式にissueが立っており](https://github.com/storybookjs/storybook/issues/12840)、まだ対応されていない模様。
:::

## まとめ

今回はVue3 x Vuetify3のPJにStorybook7を導入した。
初期の設定は多少面倒でも、コンポーネント単位での開発効率は劇的に上がったので、ぜひ導入を検討してほしい。

またサンプルコードはGitHubにpushしてあるので、詳細はこれを参照されたし。

https://github.com/yasuaki640/vuetify3-storybook7-sample

## 参考

https://storybook.js.org/recipes/vuetify

## 宣伝

この記事は下記のイベントに合わせて執筆されたものです。

https://qiita.com/official-events/0cc35c4799f72609b7ec

いいねをください。