tacamy--blog

JavaScriptを勉強中の人のブログです。

Nuxt.jsへのStorybookの導入と、Sassの変数や共通CSSを読めるようにする設定

かなり疲弊した。バージョンは次のとおり。

  • "nuxt": "2.4.0"
  • "@storybook/vue": "5.0.11"

Nuxt.jsにStorybookを追加

Storybookの公式ドキュメントを参考にした。手動で入れる方法もあるみたいだけど、CLIの方が簡単そうなので任せることに。

npx -p @storybook/cli sb init --type vue

Storybookを起動する

設定ファイルやデモ用のコンポーネントの生成、npm scriptへの追加などはCLIがすべて用意してくれるので、とりあえずもうStorybookの起動はできる。ここまではとても簡単。

npm run storybook

アドオンの追加

アドオンは、Storybookの拡張機能みたいなもので、これを使ってStorybookをカスタマイズできる。CLIを実行すると、デフォルトでActionsLinksが最初から入ってたけど、それ以外にも、よさげなアドオンをいくつか入れてみることに。

でも、紹介されているアドオンすべてがVue.jsに対応しているわけじゃないので、Addon / Framework Support Tableで確認してどれを入れるか検討する。

アドオンを追加する場合、だいたい以下のような手順で入れられる。

  1. npm i --save-dev {ADDON}でインストールする
  2. /.storybook/addons.jsでインポートする
  3. 全体の設定は/.storybook/config.jsに記述
  4. 個別のストーリーの設定が必要なら/stories/*.stories.jsに記述

Viewport

Viewportは、ChromeのDevToolsみたいに、Viewportをポチポチ切り替えて表示を確認できる。

インストール

npm i --save-dev @storybook/addon-viewport

/.storybook/addons.js

import '@storybook/addon-viewport/register'

/.storybook/config.js

import { addParameters } from '@storybook/vue'
addParameters({ viewport: { defaultViewport: 'iphonex' } })

ここでは、デフォルトのVuewportのサイズをiPhoneXに設定した。

a11y

a11yは、アクセシビリティ的にNGな箇所をエラーで教えてくれる。

インストール

npm i --save-dev @storybook/addon-a11y --dev

/.storybook/addons.js

import '@storybook/addon-a11y/register'

/.storybook/config.js

import { withA11y } from '@storybook/addon-a11y'
addDecorator(withA11y)

storybook-addon-vue-info

storybook-addon-vue-infoは、コードのプレビューやpropsの情報などを、自動で生成してくれるので、簡易的なドキュメントの代わりになりそう。Storybookの公式サイトにあるInfoはVue.jsに対応していなかったので、同じようなことができるstorybook-addon-vue-infoを入れた。

インストール

npm install --save-dev storybook-addon-vue-info

/.storybook/addons.js

import 'storybook-addon-vue-info/lib/register'

/.storybook/webpack.config.js

.storybook以下にwebpack.config.jsを新規作成し、以下の設定を記述する。

module.exports = ({ config }) => {
  config.module.rules.push({
    test: /\.vue$/,
    loader: 'storybook-addon-vue-info/loader',
    enforce: 'post'
  })

  return config
}

/stories/index.stories.js

import { withInfo } from 'storybook-addon-vue-info'

storiesOf('MyComponent', module)
  .addDecorator(withInfo)
  .add(
    'foo',
    () => ({
      components: { MyAwesomeComponent },
      template: '<my-awesome-component/>'
    }),
    {
      info: {
        summary: 'Summary for MyComponent'
      }
    }
  )

ストーリーごとにaddDecorator(withInfo)で個別に指定しないと動かなかった。あと、infoもあわせて指定しないと動かなかった。

Moduleが見つからないエラーを解消

@~を使ってパスを指定すると、Error: Can’t resolve ‘@/components/*’のようなエラーとなるので、次の設定で解消する。

/.storybook/.babelrc

.storybook以下に.babelrcを新規作成し、以下の設定を記述する。

{
  "presets": [
    "@babel/preset-env",
    "babel-preset-vue"
  ]
}

/.storybook/webpack.config.js

storybook-addon-vue-infoの設定で作成したwebpack.config.jsにaliasの設定を追記する。

const path = require('path')
const rootPath = path.resolve(__dirname, '../')

module.exports = ({ config }) => {
  config.resolve.alias['@'] = rootPath
  config.resolve.alias['~'] = rootPath

  config.module.rules.push({
    test: /\.vue$/,
    loader: 'storybook-addon-vue-info/loader',
    enforce: 'post'
  })

  return config
}

Sassの変数とmixinをStorybook上で読み込みできるようにする

/components/*.vueのファイルを/stories/*.stories.jsで読み込むと、変数やmixinを使っていたVueコンポーネントがエラーになってしまう。これは、StorybookはNuxt.jsのレールから外れることになるので、Nuxt.jsで共通のSCSSを読めるように設定していたとしても、Storybookで別途設定しないといけないから。

/.storybook/webpack.config.js

webpack.config.jsに以下の設定を追記する。

これまでの設定も含めた最終的なコードは以下のとおり。

const path = require('path')
const rootPath = path.resolve(__dirname, '../')

module.exports = ({ config }) => {
  config.resolve.alias['@'] = rootPath
  config.resolve.alias['~'] = rootPath

  config.module.rules.push({
    test: /\.s?css$/,
    loaders: [
      'style-loader',
      'css-loader',
      'sass-loader',
      {
        loader: 'sass-resources-loader',
        options: {
          resources: ['./assets/stylesheets/_variables.scss', './assets/stylesheets/_mixins.scss'],
          include: path.resolve(__dirname, '../')
        }
      }
    ]
  })

  config.module.rules.push({
    test: /\.vue$/,
    loader: 'storybook-addon-vue-info/loader',
    enforce: 'post'
  })

  return config
}

共通のスタイルを適用できるようにする

Decoratorという機能を使うとStorybook上で共通のスタイルを読み込むことができるので、リセット用のCSSや、サイト共通で使うCSSコンポーネントのスタイルを読み込んでおくとよさそう。

/.storybook/Decorator.vue

.storybook以下にDecorator.vueを新規作成して、以下のコードを記載し、読み込みたい共通CSS<style lang="scss">内でインポートする。

<template>
  <div class="decoarator">
    <slot name="story"></slot>
  </div>
</template>
<script>
  export default {
    name: 'Decorator'
  }
</script>
<style lang="scss">
  @import "@/assets/stylesheets/_normalize.scss";
  @import "@/assets/stylesheets/_base.scss";
  @import "@/assets/stylesheets/_components.scss";
</style>

/.storybook/config.js

config.jsにDecoratorを追加して、Storybook全体で共通CSSが読み込まれるようにする。アドオン等の設定も含めた最終的なコードは以下のとおり。

import { configure, addParameters, addDecorator } from '@storybook/vue'
import { withA11y } from '@storybook/addon-a11y'
import Decorator from './Decorator.vue'

addParameters({ viewport: { defaultViewport: 'iphonex' } })
addDecorator(withA11y)

addDecorator(story => ({
  components: { Decorator },
  render(h) {
    return (
      <decorator>
        <story slot="story" />
      </decorator>
    )
  }
}))

// automatically import all files ending in *.stories.js
const req = require.context('../stories', true, /\.stories\.js$/)
function loadStories() {
  req.keys().forEach(filename => req(filename))
}

configure(loadStories, module)

まとめ

こんなにがんばったのに、まだVuexをStorybookから読めるようにはなってないけど、つかれたので今日はここまで。