Storybook for html 導入

VueやReactを使わないHTMLとscssだけでStorybookを使用するためにいろいろ調べました。

HTMLソース出力の部分がJSでベタに書くことになるので、コードがきたくなってしまうのが難点だけど、いったん運用がまわればそれなりに使いやすいと思いました。

導入手順

プロジェクトルートでセットアップ

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

セットアップが終わったら npm run storybook で立ち上げて確認。
(確認が終わったらCtrl+c でいったんとめて次)

scssが使えるようにする

まず、パッケージのインストール

npm i -D sass style-loader css-loader sass-loader@10.1.1 sass-resources-loader

※sass-loaderは最新版をいれたら動かず。

※LESSを使う場合は、sass-loader, sass-resources-loader がいらなくて、 less と less-loader がいる。
ただwebpack4 だと、less-loaderの最新のバージョンが動かず、less-loaderはバージョンを5に落として使用した。(どのバージョンならOKかまで詳細確認・テストはしてない)

.storybook\main.js にsass処理の記述を追加。

const path = require('path') // 1行めに追加

module.exports = {
  // 元々あった設定は略、以下のwebpackFinal の設定を追加
  webpackFinal: async(config, {configType}) => {
    config.module.rules.push({
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
       options: {
            url: false // scss内のURLはコンパイルしない
          }
        },
        'sass-loader',
        {
          loader: 'sass-resources-loader',
          options: {
            resources: ['./stories/assets/scss/_variables.scss'], // 全scss処理で必要となる変数設定のファイルを指定
          }
        }
      ],
    })
    return config
  }
}

resetやbaseのスタイルは preview.js で読み込む

.storybook\preview.js

import '../stories/assets/scss/_reset.scss' // 1行目に追加

コンポーネント追加

最終的に別の環境にscssファイル群を持っていくことを考えて、以下のディレクトリ構造にした

├─.storybook
│ ├ main.js
│ └ preview.js
├─public
│ └ 静的画像置き場
└─stories 
  ├ assets
  │ └ scss
  │   └ 【コンポーネント名】.scss
  ├【コンポーネント名】.stories.js
  └【コンポーネント名】.html テンプレート置き場 

追加カスタマイズ

ストーリーのレイアウト調整

paddingをなくす

// コンポーネントに対して
export default {
  title: '【コンポーネント名】',
  // 略
  parameters:{
    layout:'fullscreeen'
  }
}

// Storyに対して
export const 【ストーリー名】= Template.bind({});
// 略
【ストーリー名】.parameters = {
  layout:'fullscreeen'
}

デザイン切り替え処理追加

デザイン切り替え用のclassをbodyにつける処理。
.storybook\main.js に切り替えUIの追加と、bodyにclassを追加する

export const decorators = [
  (story, context) => {
    document.addEventListener("DOMContentLoaded", e => {
      Array.prototype.forEach.call(document.body.classList, function (class_name, index) {
        if(class_name.match(/^theme-/)){
          document.body.classList.remove(class_name);
        }
      });
      document.body.classList.add('theme-'+context.globals.Theme);
    });
    return story();
  }
]

export const globalTypes = {
  Theme: {
    name: 'Theme',
    description: 'Themeを設定します',
    defaultValue: 'default',
    toolbar: {
      icon: 'circlehollow',
      items: ['default', 'white', 'black']
    }
  }
}

HTMLソースコピペアドオン

パッケージのインストール

npm i -D @pickra/copy-code-block

HTMLのDOMを返す処理をしているソースで

// 最初で読み込んで
import copyCodeBlock from '@pickra/copy-code-block';

// 以下、 ret が戻すDOM だとして。
ret.appendChild(copyCodeBlock(【表示したいHTMLソーステキスト】,{
    shouldReturnDomEl: true,
    cssOverrides:`
    .container {
      margin-top:1rem;
      padding:1rem;
    }
    .container code {
      padding: 0;
      white-space: break-spaces;
    }
    .container .copyButton{
      text-align:center;
    }
    `
}));

※これ、実際に実装してみて、CreateXXXの中でこの処理をいれてしまうと、組み合わせたときによろしくないので、XX.stories.js の Template 処理の中でやったほうがよさそう。

JavaScriptの処理を差し込む

以下のファイルを追加し、JS処理をいれる。
.storybook\preview-head.html

var で宣言したら、グローバルとなるので、ストーリー内のイベントリスナー内から呼べる。

DOMが生成されてからでないと動かない処理は DOMContentLoaded を使えばよさそう。
document.addEventListener(“DOMContentLoaded”, e => { /* 処理 */});

静的ファイル置き場

package.json のscripts で指定

"scripts": {
  "storybook": "start-storybook -p 6006 -s public",
  "build-storybook": "build-storybook -s public"
},

public下のファイルがルートからのパスでとれるようになるので、
HTMLソースから、scssファイル内から  “./画像名” で取得できる。

ローカルホストのドメイン名を変更

hostsファイルに以下の設定を追加。
#すでに127.0.0.1 の設定がある場合は半角スペースで ローカルのドメイン名を追加

127.0.0.1 local.hoge.com

package.json のscripts で -h オプション、 -p オプションをつける

"storybook": "start-storybook -h local.hoge.com -p 80 -s public",