LoginSignup
22
9

More than 1 year has passed since last update.

おはようございます、こんにちは、こんばんわ。

今回はEJSでのStorybook導入とStorybookの簡単な使い方を書きます。
StorybookはUIコンポーネントとページを分離して構築するためのオープンソースツールです。
Storybook公式サイト

前置き

「Storybook 入門」とかで調べるとReactやらNuxtやらへの導入を前提とした記事が溢れています。
コンポーネント化を行うならばもちろんそれらのフレームワークを使うのが1番ですが、HTMLのテンプレートエンジンもフレームワークほどではないとはいえ、できない事もないです。
また、同時に2つのことを学ぶより1つのことを学ぶ方が楽だろうと思い、弊社で使われている「EJS」への導入を試みました。
Storybookはアドオンが強力なので、ぜひ最後まで読んで実践してみてください。

ファイル構成

ファイル構成
root/
   ├ dest/ <!-- srcの出力ファイル -->
   ├ src/ <!-- コンパイル・トランスパイル前提で編集するファイル -->
   │  ├ ejs/ <!-- .ejsを格納 -->
   │  └ scss/ <!-- .scssを格納 -->
   │
   ├ static/ <!-- コンパイルしない静的ファイル -->
   ├ node_modules/
   └ package.json

Storybook導入

StorybookVue.jsReact環境下だとCLIでバコッと導入できたりするのですが、EJSの環境下での導入なので手動で導入を進めます。

まずはHTML用のStorybookをインストールします。

$ npm install @storybook/html

次はStorybook用の設定ファイル・フォルダを用意します。
root/直下に.storybookフォルダを作成します。

ファイル構成
root/
   ├ .storybook/  <!-- new!! -->
   ├ dest/ 
   ├ src/ 
   │  ├ ejs/ 
   │  └ scss/ 
   │
   ├ static/ 
   ├ node_modules/
   └ package.json

作成した.storybookフォルダの中に設定ファイルを2ファイル(main.js,preview.js)を作成していきます。

.storybook/main.js
const path = require('path');

module.exports = {
  webpackFinal: async (config, { configType }) => {

    config.module.rules.push();

    return config;
  },
  stories: ['../stories/**/*.stories.@(js|mdx)']
};
.storybook/preview.js
import { addDecorator } from '@storybook/html'

もう一息です。
package.jsonに起動用のscriptを記述します。

package.json
"scripts": {
    "storybook": "start-storybook -p 6006"
 }

(6006のポート番号だと都合の悪い方は自由な番号で大丈夫です)

あとは正常に起動すれば一旦導入は完了です。

$ npm run storybook

以下の画面が表示されれば成功です!
スクリーンショット 2021-11-26 14.16.00.png

コンポーネントを作成

コンポーネントの作成場所は以下になります。

ファイル構成
root/
   ├ .storybook/
   ├ dest/ 
   ├ src/ 
   │  ├ ejs/ 
   │  │  └ _partials/ 
   │  │       └ _components/   <!-- _components/配下に作成 -->
   │  │
   │  └ scss/ 
   │
   ├ static/ 
   ├ node_modules/
   └ package.json

試しにボタンコンポーネントを作成してみましょう。

_components/_button.ejs
<% //初期値を設定%>
<% if (typeof disabled === 'undefined') { var disabled = false; } %>
<% if (typeof type === 'undefined') { var type = 'button'; } %>
<% if (typeof label === 'undefined') { var label = ''; } %>
<% if (typeof modefireClass === 'undefined') { var modefireClass = ''; } %>
<% if (typeof jsClass === 'undefined') { var jsClass = ''; } %>
<% //初期値を設定%>

<button <%= disabled ? 'disabled' : '' %> type=<%= type %> class="<%= modefireClass %> <%= jsClass %>" ><%= label %></button>

以下の5つの設定をできるように作成しました。

  • disabled
  • type
  • label
  • modefireClass
  • jsClass

余談ですが、初期値を設定を設定してあげないとEJSに怒られてしまうので、初期値は必須になります。

EJSを読み込めるようにする

EJSのローダーがあるので、インストールします。

$ npm install ejs-compiled-loader

インストール後は.storybook/main.jsEJSローダー用の設定を記述するだけです。

.storybook/main.js
const path = require('path');

module.exports = {
  webpackFinal: async (config, { configType }) => {

    config.module.rules.push(
      {
        test: /\.ejs$/,
        loaders: ['ejs-compiled-loader'],
        // 読み込む予定のEJSのディレクトリを指定する
        include: path.resolve(__dirname, '../src/ejs/_partials/_components/') 
      }
    );

    return config;
  },
  stories: ['../stories/**/*.stories.@(js|mdx)']
};

完了!

storiesの作成

作成したボタンコンポーネントをStorybookに表示させるため、storie/に設定ファイル(button.stories.js)を作成します。
xxx.stories.js
xxx部分は自由ですが、わかりやすいように表示するコンポーネントと名前を統一します。

storie/button.stories.js
import button from '../src/ejs/_partials/_components/_button.ejs'; // コンポーネントを読み込む

export default {
  title: 'button', // storybookでリストに表示されるタイトル名
  component: button // importしたコンポーネント
};

export const nomarl = () => {
  // コンポーネントに渡す変数を宣言
  const label = 'Click Here';
  const type = 'button';
  const disabled = false;
  const modefireClass = '';
  const jsClass = '';

  return button({ label, type, disabled, modefireClass, jsClass }); // 各変数を引数に入れてコンポーネントを表示
};

ここまで終わってStorybookを起動するとコンポーネントが表示されるはずです。
スクリーンショット 2021-11-26 15.04.04.png
これにてstoriesの作成は完了になります。
コンポーネントが複数ある場合は1ファイルで完結する事もできると思いますが、コンポーネントごとに設定ファイルを作成するのをお勧めします。

StorybookにSCSSを読み込ませる

CSSを読み込んでもいいですが、コンポーネント用のSCSSは大体インポートして使われると思うので、そのまま読み込めるようにSCSSに対応します。
まずは各ローダーをインストール

$ npm install style-loader@2.0.0 css-loader@5.2.6 sass-loader@10.0.0

各ローダーが最新バージョンだと動かないらしいので、こちらのissueを参考にバージョン指定しています。(鬼ハマりポイント)
https://github.com/storybookjs/presets/issues/195
sass-loaderなどバージョンを下げているので、すでにnode-sasssass(dart-sass)などをインストールされている方はバージョンの見直しをしてください。
今回はnode-sassを入れます。

$ npm install node-sass@4.14.1

インストール後は.storybook/main.jsにローダー用の設定を記述します。

.storybook/main.js
const path = require('path');

module.exports = {
  webpackFinal: async (config, { configType }) => {

    config.module.rules.push(
      {
        test: /\.ejs$/,
        loaders: ['ejs-compiled-loader'],
        include: path.resolve(__dirname, '../src/ejs/_partials/_components/') 
      },
      {
        test: /\.scss$/,
        use: ['style-loader', 'css-loader', 'sass-loader'],
        // 読み込む予定のSCSSのディレクトリを指定する
        include: path.resolve(__dirname, '../src/scss/css/')
      }
    );

    return config;
  },
  stories: ['../stories/**/*.stories.@(js|mdx)']
};

sass(dart-sass)を使っている方はsass-loaderoptionの記述をする必要があるので、ご注意を。
これでSCSSをインポートできるようになりました!
各ファイルに必要なSCSSを読み込んでいきましょう。
ますはstorie/button.stories.js

storie/button.stories.js
import button from '../src/ejs/_partials/_components/_button.ejs';
import '../src/scss/css/_partials/_components/_button.scss'; // 必要SCSSを適宜読み込む

export default {
  title: 'button',
  component: button
};

export const nomarl = () => {
  const label = 'Click Here';
  const type = 'button';
  const disabled = false;
  const modefireClass = '';
  const jsClass = '';

  return button({ label, type, disabled, modefireClass, jsClass });
};

全てのコンポーネントへ共通で読み込みたいファイルがある場合は.storybook/preview.jsで読み込めばOKです。

.storybook/preview.js
import { addDecorator } from '@storybook/html';
import '../src/scss/css/bundle.scss';

Storybookを起動すると、SCSSが反映されているはずです。

Storybookにアドオンを入れる

アドオンを入れてないStorybookはネタの乗っていない寿司みたいなものです。
アドオンを入れることでさらに強力なツールになります。
今回は以下4つのアドオンを追加します。

まずはインストール

$ npm install @storybook/addon-a11y @storybook/addon-knob @storybook/addon-notes @storybook/addon-storysource

各ファイルに必要な記述をしていきます。

.storybook/main.js
const path = require('path');

module.exports = {
  webpackFinal: async (config, { configType }) => {

    config.module.rules.push(
      {
        test: /\.ejs$/,
        loaders: ['ejs-compiled-loader'],
        include: path.resolve(__dirname, '../src/ejs/_partials/_components/') 
      },
      {
        test: /\.scss$/,
        use: ['style-loader', 'css-loader', 'sass-loader'],
        include: path.resolve(__dirname, '../src/scss/css/')
      }
    );

    return config;
  },
  stories: ['../stories/**/*.stories.@(js|mdx)'],
  addons: [ // アドオンは追加したらここに記述する
    '@storybook/addon-a11y', 
    '@storybook/addon-knobs',
    '@storybook/addon-storysource',
    '@storybook/addon-notes'
  ],
};
.storybook/preview.js
import { addDecorator } from '@storybook/html';
import { withA11y } from '@storybook/addon-a11y'; //全てのコンポーネントで反映させるため、ここでimport
import '../src/scss/css/bundle.scss';

addDecorator(withA11y);

@storybook/addon-knobsは覚えることがちょっとあるので、公式ドキュメントを参照しながら触ってみてください。

storie/button.stories.js
import { withKnobs, text, select, boolean } from '@storybook/addon-knobs'; // 渡す変数の指定方法によって記述が変わります。
import button from '../src/ejs/_partials/_components/_button.ejs';
import '../src/scss/css/_partials/_components/_button.scss';

const readme = ` // @storybook/addon-storysource用の記述
## Props
- label
  - 初期値: ''
- type
  - 初期値: 'button'
- disabled
  - 初期値: false
- modefireClass
  - modefire用のクラス付与
  - 初期値: ''
- jsClass
  - JavaScript用のクラス付与
  - 初期値: ''

## 関連コンポーネント
なし
`;

export default {
  title: 'button',
  component: button,
  decorators: [withKnobs],
  parameters: {
    notes: readme // readme読み込み
  }
};

export const nomarl = () => {
  const label = text('Text', 'Click Here'); // @storybook/addon-knobs用の記述
  const type = select('type', { // @storybook/addon-knobs用の記述
    button: 'button',
    submit: 'submit',
    reset: 'reset'
  });
  const disabled = boolean('disabled', false); // @storybook/addon-knobs用の記述
  const modefireClass = text('modefireClass', ''); // @storybook/addon-knobs用の記述
  const jsClass = text('jsClass', ''); // @storybook/addon-knobs用の記述

  return button({ label, type, disabled, modefireClass, jsClass });
};

これでStorybookの画面から設定変数の値を触れたり、コンポーネントの説明を参照することができるようになりました。
お気に入りのアドオンでカスタマイズして最強のStorybookを目指しましょう!

22
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
9