静的なWEBサイト用にも使えるstorybookのpug環境を作ってみた

storybookって色々使い道があると思うのですが、動的なWEBサイト用の情報しか殆どないので**storybook for html**を静的WEBサイトでも使えるようなものを作ってみました。
やりたい事が形になったので紹介したいと思います。
gitも公開しているので良かったらお試しください。

![story01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320569/93e0c665-9a3c-9b55-bfa3-b6d6f1fbce82.jpeg)
 
- [DEMO](https://bigfly3.github.io/storybook_html_pugdev/)
- [Repository](https://github.com/BigFly3/storybook_html_pugdev)

## 出来ること
・/stories以下のtree-title.stories.pugを/path/to/tree-titleへ自動追加（日本語も可）
・/stories以下のsassやstylusを自動で読み込み
・パネルにpugとhtmlのソース表示＆コピー
・pugのmixinでコンポーネント作成し、knobsやテストをjsで記述可
・複数の環境に合わせて設定を変更して構築可

### サブの環境として使えます
(.stories.)pug + sass,stylus + jqueryをデフォルトで使えるようにしてあるので、storybookやjsの知識があまり無い状態でも、マークアップからstorybook上での開発が進められるようにしてあります。
プロジェクトの本環境を作成している間に、コンポーネントを先に開発してもらうとか既存のgulp環境などの横に独立して並べて、stories以下のファイルを本環境でincludeして使用するみたいなことも可能です。

### pugとhtmlのソース表示＆コピー
アドオンパネルにpugとhtmlのソースを表示することができます。
※自作のpugパネルはホットリロードが効きません。変更確認したい場合はリロードしてください。

#### pugパネルへのソース追加方法
【.stories.pug】で追加されたストーリー　→　自動的にパネルに追加されます
【.stories.js】で追加するストーリー → parametersの【pugCode】に/stories/以下のパスを入れる

```javascript

//csf
export const Default = () => {/省略/}
Default.story = {
  parameters:{pugCode:'Examples/Media/Media.pug'}
}

//storiesOf
storiesOf('Examples/Media',modules)
.add('Media', () => {/省略/},
{pugCode:'Examples/Media/Media.pug'})

```

## Pugのmixinで作成するコンポーネント
pugのmixinを使用することでstorybookの形に合うコンポーネント作成をすることが出来ます。

[静的なHTMLのためのコンポーネント駆動開発](https://standard.shiftbrain.com/blog/cdd-for-static-deliverables-ja)の記事が参考になると思います。
※[DEMO](https://bigfly3.github.io/storybook_html_pugdev/)にもこちらの記事の動作サンプル（テスト以外）を入れてあります。


### リンクボタンの作成サンプル
**大中小サイズ、横幅フルサイズ、色、URL、外部リンク**の設定ができるコンポーネントをpugのmixinで作成します。
paramsの設定で切り替わるような仕組みになります。

```js:/stories/Components/LinkButton/LinkButton.pug
mixin LinkButton(params={})
  - const props = Object.assign({ url: '' ,full:false}, params) //デフォルトのパラメータ

  - let className = attributes.class !== undefined ? attributes.class : ''
  - if(props.size) className += ' LinkButton--' + props.size
  - if(props.full) className += ' LinkButton--full'
  - if(props.color) className += ' LinkButton--' + props.color
  - if(props.isBlank) Object.assign( attributes , {'target':'_blank'})
  - const attrs = Object.assign( attributes , {class:className})
  a.LinkButton(href=props.url)&attributes(attrs)
    block
```
```css:/stories/Components/LinkButton/LinkButton.styl
.LinkButton
  padding 10px 50px
  text-decoration none
  color #000000
  position relative
  text-align center
  border 1px solid #CCCCCC
  border-radius 5px
  font-size 16px
  display inline-flex
  justify-content center
  align-items center
  box-sizing border-box

  /* サイズ */
  &--small
    padding 5px 50px
    font-size 12px

  &--large
    font-size 24px

  /* フルサイズ */
  &--full
    width 100%

  /* カラー */
  &--gray
    background #8c9094
    border 1px solid #8c9094
    color #ffffff

  &--green
    background green
    border 1px solid #FFF
    color #ffffff
```

こんな感じmixinを作ると【.stories.pug】では、コンポーネントベースのコードで書くことが出来ます。
knobsまで作らなくても色々なパターンを作成しておけば、コピー用サンプルやコンポーネントを修正した際の見た目のテストみたいな使用も可能です。

```js:/stories/Components/LinkButton/サンプル.stories.pug
include LinkButton
p
  +LinkButton({
  })
    | ノーマル
p
  +LinkButton({
    size:'small',
    color:'green',
  })
    | 緑小
p
  +LinkButton({
    size:'large',
    color:'gray',
    url:'https://google.com',
    isBlank:true
  })
    | グレー大
p
  +LinkButton({
    size:'small',
    full:true,
  })(class="addClass")
    | 小フルサイズ
```

![mixin_sample.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320569/680ab09c-0d9c-9654-cc18-895c6e176243.jpeg)


### knobsを書くJSサンプル
今のコンポーネントをKnobs化するサンプルです。

```js:/stories/Components/LinkButton/LinkButton.stories.js
import { withKnobs, boolean, select ,text} from '@storybook/addon-knobs'
import { renderer } from 'storypug'
import LinkButton from './LinkButton.pug'
const { html } = renderer()

const sizes = {
  'ノーマルボタン':'',
  '小さいボタン':'small',
  '大きいボタン':'large',
}
const colors = {
  'デフォルト':'',
  'グレー':'gray',
  'グリーン':'green',
}

export default {
  title: 'Components/LinkButton',
  decorators: [withKnobs],
}

// knobs
export const Knobs = () =>
html(
  LinkButton,
  {
    size: select('ボタンタイプ', sizes, sizes[0]),
    full: boolean('フルサイズ', false),
    color: select('色', colors, colors[0]),
    url: text('URL',''),
    isBlank: boolean('別窓', false),
  },
  'ボタンテキスト',
)
Knobs.story = {
  parameters:{pugCode:'Components/LinkButton/LinkButton.pug'}
}
```

![knobs_sample.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320569/4df9a7a5-5207-f4d9-b7ad-85fe7514e257.jpeg)


## ビルドについて
githubpagesとテスト環境にそれぞれ作成したいというようなケースのために、ビルド設定を二つ用意しています。

- .storybook/default　→　【npm run build】 元の設定と同様publicフォルダを設定しています。
- .storybook/second　→　【npm run build2】 githubpagesを想定してdocsフォルダを設定しています。

.storybook/defaultをコピーしてpackage.jsonのscriptsを修正すれば、さらに複数作ることも可能です。
staticフォルダも元の設定と同様staticなのでメイン環境などと合わせる場合は、この辺も調整してください。

### ビルドにおけるpugの共通変数の設定
ファイルを置くパスが違うケースもあると思うので、それぞれの環境にpugの共通変数用として**pug-data.js**に変数をセットする仕組みを用意しています。
通常layout.pugなどに設定するような変数をここにセットすることで、コンポーネントを修正せずに出し分けすることができます。
【stories.pug】による自動追加でも、この共通変数が一緒にセットされます。

### pug-dataの使い方
先ほど作成したLinkButtonを使用して、外部リンク設定時にアイコンのimgタグを表示するように変更してみます。


1 画像フォルダの共通変数を設定

```js:/.storybook/default/pug-data.js
//pug common values
module.exports = {
  imgDir:"/images/"
}
```

2 LinkButton.pugコンポーネントの【block】の下にblankのif文を追記

```js:/stories/Components/LinkButton/LinkButton.pug
  a.LinkButton(href=props.url)&attributes(attrs)
    block
    -if(props.isBlank)
      img(src=imgDir + 'icon-blank.png' width="16px" style="margin-left:10px")
```

3 LinkButton.stories.jsの【renderer()】の行をpug-data.jsを読み込むように修正

```js:/stories/Components/LinkButton/LinkButton.stories.js
import pugData from '@conf/pug-data'
const { html } = renderer(pugData)
```

別窓チェック時に共通変数で設定する画像アイコンが表示できるようになります。
![blank画像.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320569/c5639ba7-5be5-d29a-498f-6ed9e3a5493c.jpeg)

このpug-dataの共通変数と同名の変数を本プロジェクトのlayout.pugに作成すれば、コンポーネント開発はstorybook上でやりつつ、本プロジェクトの環境はincludeで使用していく同一ソース運用もできるかと思います。
package.jsonのbuild設定でoutputやstaticの場所少しいじれば、比較的簡単に今お使いの開発環境と連携できると思うので色々お試しください。