Nuxt(Vue)でStorybookの環境を作る。VuetifyやSASSの読み込みも

# Storybookとは
ざっくりいうと【UIコンポーネントの簡易カタログ作成ツール】です。
チーム内のエンジニアだけではなく、デザイナーとのやりとりにも便利です。
詳しくは以下のサイトを見ていただけるとイメージが分かるかと思います。

[公式サイト](https://storybook.js.org/)
[Storybookがなぜ必要か？(Vue.js編)](https://qiita.com/masaakikunsan/items/dad8d84807918f3a43cb)

# まずは導入
[手動で入れる方法](https://storybook.js.org/docs/guides/guide-vue/)もあるのですが、[CLI](https://www.npmjs.com/package/@storybook/cli)で簡単に導入してから細かく自己設定を増やしていく方をおすすめします。
[CLI Options](https://storybook.js.org/docs/configurations/cli-options/)

```bash
# 対象のディレクトリ直下で行ってください
$ npx -p @storybook/cli sb init
```
or

```bash
$ yarn add -D @storybook/cli
$ yarn sb init
```

最初はおすすめのaddon(拡張ツール)なども入っているので、のちのち不要であれば消してしまってください。

以下のディレクトリが追加されます

```
.storybook/
src/stories/
```

ビルド用コマンドも自動で追記してくれます

```json:package.json
"scripts": {
    ~
    "storybook": "start-storybook -p 9009 -s public",
    "build-storybook": "build-storybook -s public"
  }
```
なんて便利なんでしょう！
(これ手動で作ってたときはえらい手間でした・・・)
追加されたファイルなどをざっと確認したら、早速実行してみてみましょう

```bash
# 以下面倒なのでyarnの書き方で統一します
$ yarn storybook
```

# story(ページ)を追加する
`src/stories`配下に`test.sotires.js`というファイルを追加してみましょう。
適当にv-data-tableを表示したページを作ります。

```js:test.stories.js
import { action } from '@storybook/addon-actions';

export default {
  title: 'Vue'
};

export const DataTable = () => ({
  template: `
    <div>
      <h2>【DataTable】Vue既存のコンポーネント</h2>

      <v-data-table
      :headers="[
        { text: 'ID', value: 'id' },
        { text: '名前', value: 'name' },
        { text: '編集', value: 'action', align: 'center', sortable: false }
      ]"
      :items="[
        {id: 0, name: '名前0'},
        {id: 1, name: '名前1'},
        {id: 2, name: '名前2'},
        {id: 3, name: '名前3'},
        {id: 4, name: '名前4'},
        {id: 5, name: '名前5'},
        {id: 6, name: '名前6'}
      ]"
      :items-per-page="10"
      multi-sort
      class="elevation-1"
    >
      <template v-slot:item.action="{ item }">
        <v-icon @click="action" color="grey" dark>
          mdi-pencil
        </v-icon>
      </template>
    </v-data-table>
    </div>
  `,
  methods: {
    action: action('clicked')
  }
});
export const Title = () => ({
  template: `
    <div>
      <h1>H1タイトル</h1>
      <h2>H2タイトル</h2>
    </div>
  `
});
```

すると左のメニューに`Vue`というリンクが追加されたかと思います
<img width="295" alt="スクリーンショット 2020-01-09 15.08.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80770/3c006c4a-aabc-8b5e-e733-bda366951b21.png">

このように `xxx.stories.js`を追加していけばページを作ることができます。


# Vuetifyを導入する
私の環境ではVuetifyを導入していたので、storybookでも使えるようにします。
`.storybook`配下に`config.js`を作成してください。

```js:.sorybook/config.js
import { configure, addDecorator } from '@storybook/vue';

import Vuetify from 'vuetify';
import colors from 'vuetify/es5/util/colors';
import 'vuetify/dist/vuetify.min.css';

const vuetifyOptions = { }

Vue.use(Vuetify, {
  customVariables: ['../src/assets/variables.scss'],
  theme: {
    dark: false,
    themes: { // うちではこのように設定していますが、それぞれの環境に合わせてください
      dark: {
        primary: colors.blue.darken2,
        accent: colors.grey.darken3,
        secondary: colors.amber.darken3,
        info: colors.teal.lighten1,
        warning: colors.amber.base,
        error: colors.deepOrange.accent4,
        success: colors.green.accent3
      }
    }
  }
});

addDecorator(() => ({
  vuetify: new Vuetify(vuetifyOptions),
  template: `<v-app><story slot="story" /></v-app>`
}));

configure(require.context('../stories', true, /\.stories\.js$/), module);
```

# プラグインを導入する
プラグインを入れている環境の場合、storybookでも同様に入れてあげます。
先程作ったconfigに以下を追記しましょう。
今回は[ToggleButton](https://www.google.com/search?q=ToggleButton+vue&rlz=1C5CHFA_enJP848JP849&oq=ToggleButton+vue&aqs=chrome..69i57j0l7.2496j0j4&sourceid=chrome&ie=UTF-8)を入れてあげます

```js:.sorybook/config.js
import Vue from 'vue';
import ToggleButton from 'vue-js-toggle-button'

Vue.use(ToggleButton);
```

# エイリアスを設定する
`src/hoge/hoge...`のようにフルパスを書くのは面倒なので、aliasを設定してあげましょう。
`.storybook/webpack.config.js`という設定ファイルを作ってください。

```js:.sorybook/webpack.config.js
const path = require('path')
const rootPath = path.resolve(__dirname, '../src')

module.exports = ({ config }) => {
  config.resolve.alias['@']           = rootPath
  config.resolve.alias['@components'] = `${rootPath}/components`
  config.resolve.alias['@assets']     = `${rootPath}/assets`
  
  return config
}
```

# SASSを使える環境にする

`.storybook/webpack.config.js`ファイルに以下のように追記します
※`style-loader``css-loader``sass-loader`がinstallされていない環境の場合、installをお願いします。

```js:.sorybook/webpack.config.js
module.exports = ({ config }) => {
  
  config.module.rules.push({
    test: /\.scss$/,    
    loaders: ['style-loader', 'css-loader', 'sass-loader']
  });
  return config
}
```

# 共通CSS(SASS)をimportする
`.storybook/Decoretor.vue`というファイルを作りましょう

```vue:.storybook/Decoretor.vue
<template>
  <div class='decoarator'>
    <slot name='story'></slot>
  </div>
</template>
<script>
  export default {
    name: 'decorator'
  }
</script>

<style lang='scss'> // ここにimportしたいファイルを入れます。
@import '@assets/variables.scss';
@import '@assets/app.scss';
</style>
```

`config.js`にてDecoretorをimportします。
以下のように追記してください。

```js:.sorybook/config.js
import decorator from './Decorator';

addDecorator(() => ({
  components: { decorator }, // componentを使えるようにします
  vuetify: new Vuetify(vuetifyOptions),
  template: `<v-app><decorator><story slot="story" /></decorator></v-app>` // Vuetifyも使いたいので、一段decoratoreをはさみます
}));
```

# addonを導入
addonは以下のファイルにまとめてimportします。
最初の導入時からあるファイルです。必要なaddonを入れてください。

```js:.storybook/addons.js
import '@storybook/addon-actions/register';
import '@storybook/addon-storysource/register';
```

## source-loader
[source-loader](https://www.npmjs.com/package/@storybook/addon-storysource)は開いているsotyies.jsのコードを表示してくれるアドオンです。
こんな感じでコードが読めます。
docs入れるほどじゃないプロジェクト向けです。
![スクリーンショット 2020-01-09 14.36.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80770/86bf3485-4a1f-8f3d-b59d-4d5b3cd4d4e8.png)
こちらはページごとに設定して使うのではなく、全体反映です。
webpack.configファイルに以下のように追記します

```js:.sorybook/webpack.config.js
module.exports = ({ config }) => {
  config.module.rules.push({
    test: /\.stories\.jsx?$/,
    loaders: [require.resolve('@storybook/source-loader')],
    enforce: 'pre',
  });

  return config
}
```

## 気になってるaddon
[Storybookの新しいアドオン addon-docs がいい感じ](https://qiita.com/takeyuichi/items/d21cb0a884e5aaac3a17)
[Storybookの便利なaddon機能のご紹介](https://blog.spacemarket.com/code/storybook-addon/)

# トラブルシューティング
困ったことと、その対応をざっくりまとめておきます。

## Error node-sass??
自分の環境では`node-sass`を導入していたのですが、storybookでsassを導入したあと動かなくなり、困りました。

```bask:エラー文言
SassError: Invalid CSS after " @content": expected "}", was "($material-light);
```

エラー文言を読んでいると、なにやら`node-sassではなくsassを入れろ！`という事が書いてありました。

```bash:sass導入
$ yarn add sass
```

するとpackege.jsonからnode-sassが消え、sassオンリーになりました。
これで動くようになりました。
詳しくはnode-sassの何が悪いのかわかりません。
node-sassを消したくなかったので、この方法以外の解決法はないかといろいろ調べましたが、
結局みつからず・・・

## install core-js??
山程のエラーが出て、ビルドできなくなりました。
エラー文言の最後に以下のような`core-js`をinstallしろ！という文言がありました。

```bash:エラー文言
To install them, you can run: npm install --save core-js/modules/es6.array.find core-js/modules/es6.array.iterator core-js/modules/es6.date.to-string core-js/modules/es6.function.name core-js/modules/es6.object.assign core-js/modules/es6.object.keys core-js/modules/es6.object.to-string core-js/modules/es6.promise core-js/modules/es6.regexp.constructor core-js/modules/es6.regexp.match core-js/modules/es6.regexp.replace core-js/modules/es6.regexp.search core-js/modules/es6.regexp.split core-js/modules/es6.regexp.to-string core-js/modules/es6.string.includes core-js/modules/es6.string.iterator core-js/modules/es6.string.repeat core-js/modules/es6.string.starts-with core-js/modules/es6.symbol core-js/modules/es7.array.includes core-js/modules/es7.promise.finally core-js/modules/es7.symbol.async-iterator core-js/modules/web.dom.iterable
```
なのでinstallしてみたのですが、

```bash
$ yarn add core-js
```
なおもエラー。
困ったところ、こちらのサイトが参考になりました。
[Nuxt2.5でのエラー：core-js/modules/es6.array.find in ./.nuxt/client.js](https://www.suzu6.net/posts/127-nuxt-es6-error/)
2.6.2を入れなくちゃいけないようでしたので、入れてみました。

```bash
$ yarn add core-js@2.6.2
```

するとみごと解決！
issueも出ていて解決済ってあったんですけどね。まだcore-jsとの依存性はあるようです。

## sassの定数を読み込んでもらえない

Decoretor内で定数をimportしていたのですが、

```vue:.storybook/Decoretor.vue
<style lang='scss'>
@import '@assets/variables.scss'; // ※@assetsはaliassの登録をしています
@import '@assets/sass/app.scss';
</style>
```

定数を使ったvueファイルを読み込むとエラーが起きてしまいました。
↓例：buttonコンポーネント

```vue:button.vue
<template>
  <v-btn class="test-button">mdi-pencil</v-btn>
</template>

<style lang="scss">
.test-button {
  background-color: map-get($grey, lighten-2);
}
</style>
```
仕方がないので各compoent内でも定数が書かれたファイルをimport

```vue:button.vue
<style lang="scss">
@import '@assets/variables.scss';
</style>
```
これで動くようにはなりましたが、他にいい解決方法がないかと大分模索しました＞＜
アドバイスございましたらお願いしますm(_ _)m

# 感想
以上の工程などを行い、storybookを導入してみました。
まとめきれない導入トラブルなんかもあって、とにかく疲れました。。。
webpackとか初心者だからというのものあるのですが、もっとさっくり気軽に導入できるかと思っていたので（＾＾；

でも一旦環境をつくってしまえば、あとの更新などはとっても簡単なので、
こだわりすぎなければとても使い勝手の良いカタログになりそうです！

導入コストを呑めるのであればおすすめします！

ざっくりなまとめなので不明な点などございましたら
ご質問・編集リクエストくださいませm(_ _)m