📖

Nuxt.jsにStorybookを導入したら、プレビューがうまく表示できなかった

2022/10/13に公開約4,600字

概要

Nuxt.jsを使用したプロジェクト(TypeScriptは不使用)に参加していて、「コンポーネントが増えてきたので、Storybookを使おう」という話になりました。
https://storybook.js.org/

実際に使用したのは@nuxtjs/storybookです。
https://storybook.nuxtjs.org/

いざ導入してみたら、表題のとおりプレビューがうまく表示できなくて、調べていくと、Storybookが途中のディレクトリ名も含めたコンポーネント名で自動登録することが原因だと分かりました。

具体的には、当該プロジェクトがAtomic Designを採用していたため、たとえば以下のようにコンポーネントファイルが配置されていて、

project
└── components
    └── molecules
        └── Button.vue

このコンポーネントをコンテンツで使用する際は、「Button」として実装していました。

<Button label="ラベル" />

一方で、Storybookはこのコンポーネントを「MoleculesButton」として自動登録するため、「Button」でストーリーを作成しても表示できない、という状況でした。

つまり、コンテンツで使用する際に以下のように「MoleculesButton」として実装してれば、この問題は起きません。

<MoleculesButton label="ラベル" />

が、やはり「Button」で使いたいケースもあると思う(ない?)ので、調整した内容をまとめることにしました。

誰かの何かの参考になれば幸いです。

調整内容

ざっくりした手順とともに書いていきます。

Nuxt.jsのプロジェクトを作成

任意のディレクトリで、プロジェクトを作成します。

/nuxt-storybook-test
npx create-nuxt-app --overwrite-dir

適当に選択します。

/nuxt-storybook-test
Need to install the following packages:
  create-nuxt-app
Ok to proceed? (y) y

create-nuxt-app v3.7.1
✨  Generating Nuxt.js project in .
? Project name: nuxt-storybook-test
? Programming language: JavaScript
? Package manager: Npm
? UI framework: None
? Nuxt.js modules: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Linting tools: ESLint
? Testing framework: None
? Rendering mode: Single Page App
? Deployment target: Server (Node.js hosting)
? Development tools: jsconfig.json (Recommended for VS Code if you're not using typescript)
? Continuous integration: None
? Version control system: Git

適当なコンポーネントとページを作成

/nuxt-storybook-test/components/test/Button.vue
<template>
  <button type="button">
    {{ label }}
  </button>
</template>

<script>
export default {
  props: {
    label: {
      type: String,
      required: true,
      default: ''
    }
  }
}
</script>
/nuxt-storybook-test/pages/test.vue
<template>
  <Button label="すぱらしいボタン" />
</template>

<script>
import Button from '@/components/test/Button.vue'

export default {
  components: {
    Button
  }
}
</script>

Storybookを導入

@nuxtjs/storybookをインストールします。

/nuxt-storybook-test
npm i -D @nuxtjs/storybook

本来たぶんこれだけで動くはずですが、なぜかStorybookを実行すると以下のようなエラーが出たので、

ERROR in ./node_modules/@nuxtjs/storybook/storybook/mock/nuxt-link.js

追加で@storybook/addon-actionsをインストールします。

/nuxt-storybook-test
npm i -D @storybook/addon-actions

Storybookを実行すると、.nuxt-storybookというディレクトリが自動生成されるので、あらかじめ.gitignoreに以下を追加しておきます。

/nuxt-storybook-test/.gitignore
# @nuxtjs/storybook
.nuxt-storybook

ストーリーを作成

用意しておいたコンポーネントのストーリーを作成します。

/nuxt-storybook-test/components/test/_stories/Button.stories.js
// 対象のコンポーネント
import Button from '../Button.vue'

// 表示設定
export default {
  title: 'Button',
  component: Button
}

// デフォルト
export const Default = () => `
  <Button label="ラベル" />
`
Default.storyName = 'デフォルト'

Storybookを実行(うまく表示されない確認)

この時点でStorybookを起動して、コンポーネントがうまく表示されない問題を、実際に確認してみます。

/nuxt-storybook-test
npx nuxt storybook

「ラベル」と書かれたボタンが表示されていません。
前述のとおり、「Button」というコンポーネントがStorybookに登録されていないからです。

本題:Storybookの表示を調整

/.nuxt-storybook/storybook/preview.jsを編集すれば表示をカスタマイズできますが、Storybook実行時に上書きされて元に戻ってしまいます。

そこで、以下ページを参考に、独自構成を用意します。
https://storybook.nuxtjs.org/advanced/manual-setup

/nuxt-storybook-test
npx nuxt storybook eject

ejectを実行すると、.storybookと以下のファイルが生成されます。

project
└── .storybook
    ├── main.js
    ├── middleware.js
    └── preview.js <= 今回さわるのはこれだけ

生成されたpreview.jsに、以下のように追記します。

/nuxt-storybook-test/.storybook/preview.vue
export * from '~~/.nuxt-storybook/storybook/preview.js'

// 追記はここから下

import Vue from 'vue'
import '~storybook'

// 対象のコンポーネント
import Button from '../components/test/Button.vue'

// オブジェクトにまとめる
const components = {
  Button: Button
  // 他のコンポーネントがあればここに追加
}

// コンポーネントを登録
Object.keys(components).forEach((name) => {
  Vue.component(name, components[name])
})

Storybookを実行

/nuxt-storybook-test
npx nuxt storybook

無事、コンポーネントを表示することができました。

Discussion

ログインするとコメントできます