N-LAB

Nuxt 3にStorybook 8を導入して環境を構築する

投稿日: 2024年11月20日


目標

  1. Nuxt 3の自動インポートをStorybookにも適用する
  2. PiniaとVee-ValidateをStorybookで使用する(省略可)


条件

※ここではソースディレクトリをデフォルトのルートディレクトリからsrcディレクトリに変更している想定としています。

・Nuxt.js: 3.14.159
・Storybook: 8.4.4
・unplugin-vue-components: 0.27.4
・unplugin-auto-import: 0.8.15

目次

  1. Storybook 8のインストール
  2. StorybookとNuxtの連携
  3. Storybookの設定ファイルのts化
  4. Vueファイルのエイリアスの読み込み設定
  5. Nuxt 3の自動インポートの設定
  6. Piniaの設定
  7. Vee-Validateの設定


Storybook 8のインストール

1. Nuxtプロジェクトのルートディレクトリで以下のコマンドを入力し、Storybook 8をインストールします。

$ npx storybook@latest init --type vue3 --builder vite
Need to install the following packages:
storybook@8.4.4
Ok to proceed? (y) y

※インストール完了後に自動生成されたstoriesディレクトリの中にあるサンプルファイルをすべて削除しておきます。

2. package.jsonに以下を追加します。

"scripts": {
  "storybook": "storybook dev -p 6006",
},


3. vite.config.tsに以下を追加して保存します。

// vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";

export default defineConfig({
  plugins: [vue()],
});

※Storybook 7ではViteプラグインの設定を自動で行っていましたが Storybook 8ではvite.config.tsに明示的に追加する必要があります。詳細は以下を参照ください。
https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#framework-specific-vite-plugins-have-to-be-explicitly-added

4. pages/index.vueを以下の内容で修正します

<template>
  <div>
    <h1>Index.vue</h1>
  </div>
</template>


5. stories/index.stories.tsを新規作成し、以下の内容で保存します。

import type { Meta, StoryObj } from '@storybook/vue3'
import Index from '../pages/index.vue'

type Story = StoryObj<typeof Index>
const meta: Meta<typeof Index> = {
  title: 'Index',
}

export const Default: Story = {
  render: () => ({
    components: { Index },
    template: '<Index />',
  }),
}

export default meta


6. 以下のコマンドを実行します。

$ npm run storybook


7. 以下のURLにアクセスします。Storybookのダッシュボードの左側にIndexが表示されており、選択した場合にIndex.vueファイルの内容が表示されていることを確認します。
http://localhost:6006/
dashboard

StorybookとNuxtの連携


1. 以下のコマンドを入力してNuxtのStorybookプラグインをインストールします。

$ npx nuxi@latest module add storybook


2. nuxt.config.tsに以下を追加します。

export default defineNuxtConfig({
  modules: ['@nuxtjs/storybook'],
  // nuxt.config.tsでStorybookの設定ができます。設定できる項目は以下を参照ください。
  // https://storybook.nuxtjs.org/getting-started/options
  storybook: {
      host: 'http://localhost',
      port: 6006,
  },
})


3. 以下のコマンドを入力してNuxtを起動します。3000番ポートでNuxtが起動し、6006番ポートでStorybookが起動していることを確認します。

$ npm run dev
> dev
> nuxt dev
Nuxt 3.14.159 with Nitro 2.10.4
  ➜ Local:    http://localhost:3000/
  ➜ Network:  use --host to expose
  ➜ DevTools: press Shift + Alt + D in the browser (v1.6.0)  
  ➜ Storybook: http://localhost:6006/         


4. 以下のURLにアクセスします。Storybookのダッシュボードが表示されていることを確認します。
http://localhost:6006/
dashboard

Storybookの設定ファイルのts化


1. main.jsとpreview.jsのファイルの拡張子をtsに修正します。

2. main.tsを以下の内容で保存します。

import type { StorybookConfig } from '@storybook/vue3-vite'

const config: StorybookConfig = {
  stories: [
    '../stories/**/*.mdx',
    '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)',
    // ソースディレクトリをデフォルトのルートからsrcに変更している場合は以下を設定します。
    '../src/**/*.mdx',
    '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'
  ],
  addons: [
    '@storybook/addon-onboarding',
    '@storybook/addon-essentials',
    '@chromatic-com/storybook',
    '@storybook/addon-interactions',
  ],
  framework: {
    name: '@storybook/vue3-vite',
    options: {},
  },
};
export default config


※修正前は以下になります。

/** @type { import('@storybook/vue3-vite').StorybookConfig } */
const config = {
  stories: [
    '../stories/**/*.mdx',
    '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)',
  ],
  addons: [
    '@storybook/addon-onboarding',
    '@storybook/addon-essentials',
    '@chromatic-com/storybook',
    '@storybook/addon-interactions',
  ],
  framework: {
    name: '@storybook/vue3-vite',
    options: {},
  },
};
export default config


3. preview.tsを以下の内容で保存します。

import type { Preview } from '@storybook/vue3'

const preview: Preview = {
  parameters: {
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/i,
      },
    },
  },
};
export default preview


※修正前

/** @type { import('@storybook/vue3').Preview } */
const preview = {
  parameters: {
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/i,
      },
    },
  },
};
export default preview


Vueファイルのエイリアスの読み込み設定

import { foo } from '~/bar'


TypeError: Failed to fetch dynamically imported module:



1. .storybook/main.tsに以下を追加して保存しま

import type { StorybookConfig } from '@storybook/vue3-vite'
import path from 'path'

const config: StorybookConfig = {
  // 以下を追加
  viteFinal: async (config) => {
    if (config?.resolve?.alias) {
      config.resolve.alias = {
        ...config.resolve.alias,
        '@': path.resolve(__dirname, '../src'),
        '~': path.resolve(__dirname, '../src'),
      }
    }
    return config
  },
};

※上記はエイリアスとして「@」と「~」を追加しています。ソースディレクトリは「src」を想定しています。

Nuxt 3の自動インポートの設定

  1. unplugin-auto-import
  2. unplugin-vue-components


1. 以下のコマンドを入力し、ライブラリをインストールします。

$ npm install --save-dev unplugin-auto-import
$ npm install --save-dev unplugin-vue-components


2. .storybook/main.tsに以下を追加して保存しま

import type { StorybookConfig } from '@storybook/vue3-vite'
import AutoImportFunctions from 'unplugin-auto-import/vite'
import AutoImportComponents from 'unplugin-vue-components/vite'
import path from 'path'

const config: StorybookConfig = {
  viteFinal: async (config) => {
    // 以下を追加
    if (config?.plugins) {
      config.plugins.push(
        // プリセットでvueやvee-validateなどが指定できます。
        // 指定できるプリセットは以下を参照ください。
        // https://github.com/unplugin/unplugin-auto-import/tree/main/src/presets
        AutoImportFunctions ({ imports: [
          'vue',
          'vee-validate',
          'vue-router',
          'pinia',
        ], dts: '.storybook/auto-imports.d.ts' }),
      )
      config.plugins.push(
        AutoImportComponents({
          // ソースディレクトリは「src」を想定しています。
          // ソースディレクトリがデフォルトの場合はdirs: ['components'],としてください。
          dirs: ['src/components'],
          dts: '.storybook/components.d.ts',
        }),
      )
    }
    return config
  },
}
export default config


3. components/button.vueを作成して以下の内容で保存しま

<template>
  <input type='text'>
</template>


4. pages/index.vueを以下の内容で保存しま

<script setup lang='ts'>
// nuxt 3が自動インポートする関数
const foo = ref('foo')
</script>
<template>
  <div>
    <h1>Index.vue</h1>
  <!--  nuxt 3が自動インポートするコンポーネント -->
    <Button />
    {{ foo }}
  </div>
</template>


5. 以下のコマンドを入力して、Storybookを起動します。

$ npm run storybook


6. 以下のURLにアクセスします。画面にrefの値(foo)とコンポーネント(inputタグ)が表示されていることを確認します。
http://localhost:6006/?path=/story/index--default

Piniaの設定

"getActivePinia()" was called but there was no active Pinia. 
Are you trying to use a store before calling "app.use(pinia)"?


// .storybook/preview.ts
import { type Preview, setup } from '@storybook/vue3'
import { type App } from 'vue'
import { createPinia } from 'pinia'

const pinia = createPinia()

setup((app: App) => {
  app.use(pinia)
})

※Piniaを利用していない場合はこの手順はスキップ可能です。

Vee-Validateの設定

Error: No such validator 'XXXX' exists.


// .storybook/preview.ts
import { localize } from '@vee-validate/i18n'
import ja from '@vee-validate/i18n/dist/locale/ja.json'
import { all } from '@vee-validate/rules'
import { defineRule, configure } from 'vee-validate'

configure({
  generateMessage: localize({ ja }),
})

Object.entries(all).forEach(([name, rule]) => {
  // 全バリデーションルールの読み込み
  defineRule(name, rule)
})

※Vee-Validateを利用していない場合はこの手順はスキップ可能です。

以上で全ての手順は完了になります