storybook for htmlを使って簡単にコンポーネントのカタログを作成する



# 対象読者

- React.js,Vue.jsを使っていないプロジェクトでstorybookを導入したいけどやり方がわからない人
- マークアップ言語 + JavaScriptちょっとわかる人
- storybookの勉強はしたいけど、React.js,Vue.jsの勉強まではやりたくない人

# 成果物

## URL

https://miwa-storybook-html.netlify.com/

## GitHub

現在作成中です。

https://github.com/miwashutaro0611/html-storybook

# 対応環境

## 言語

- HTML
- SCSS

`css`で行いたい場合、「既存のscssファイルの読み込み」の部分などの`scss`を`css`に変えたり、「storybook上でscssファイルが読み込まれるように設定」の部分を飛ばすなどで対応することができると思います。(未検証)

## バージョン

- node: v12.16.1
- @storybook/html: "^5.3.18"
- node-sass: "^4.13.1"


# セットアップ

参考: [Storybook for HTML](https://storybook.js.org/docs/guides/guide-html/)

## 自動セットアップ

```shell
$ npx -p @storybook/cli sb init --type html
```

## ゼロから作成する場合

<details><summary>ゼロから設定を行う方法を見る</summary><div>

### 1. `package.json`作成。必要なパッケージのインストール

```shell
$ npm init
```

```shell
$ npm i -D @storybook/html babel-loader @babel/core
```

#### パッケージ一覧

- [@storybook/html](https://www.npmjs.com/package/@storybook/html)
- [babel-loader](https://www.npmjs.com/package/babel-loader)
- [@babel/core](https://www.npmjs.com/package/@babel/core)

### 2. `npm script`の追加

```package.json
{
    "scripts": {
      "storybook": "start-storybook"
    }
}
```

### 3. `.storybook/main.js`の作成

```
$ mkdir .storybook
$ touch .storybook/main.js
```

`.storybook/main.js`に記載する内容

```javascript
module.exports = {
	stories: ['../src/**/*.stories.[tj]s'],
};
```

上記にように記載した場合、以下のような感じで読み込みが行われる

```console
├── .storybook
│   └── main.js
└── src 
    ├── index.stories.js　# 対象ファイル
    └── components # 対象ディレクトリ
        └── index.stories.js　# 対象ファイル
```

### 4. ストーリーファイルの作成

```javascript
export default { title: 'Button' }

export const withText = () => '<button class="btn">Hello World</button>' // HTMLで書く場合

export const withEmoji = () => {
  const button = document.createElement('button') // button要素作成
  button.innerText = '😀 😎 👍 💯' // button要素のテキスト
  return button
};
```

</div></details>

# storybook上でscssファイルが読み込まれるように設定

参考: [Custom Webpack Config](https://storybook.js.org/docs/configurations/custom-webpack-config/)

## 必要なパッケージのインストール

```
$ npm i -D style-loader css-loader sass-loader
```

### パッケージ一覧

- [style-loader](https://www.npmjs.com/package/style-loader)
- [css-loader](https://www.npmjs.com/package/css-loader)
- [sass-loader](https://www.npmjs.com/package/sass-loader)

## `.storybook/main.js`にscss読み込み設定を記載

```javascript
const { resolve } = require('path')

module.exports = {
  webpackFinal: async(config, {configType}) => {
    config.module.rules.push({
      test: /\.scss$/,
      use: [
        'style-loader',
        'css-loader',
        'sass-loader'
      ],
      include: resolve(__dirname, '../'),
    })
    return config
  }
}
```

# storybookに読み込みを行うコンポーネントを作成

## `.html`を使ってHTMLの記載を行いたい場合

```console
├── _index.scss # スタイル用ファイル
├── index.html # テンプレート用ファイル
└── index.stories.js # storybook用ファイル
```

### `_index.scss`

```scss
.c-title {
  font-size: 40px;
  span {
      font-size: 60px;
  }
}
```

### `index.html`

```html
<p class='c-text'>Hello <span>World!!</span></p>
```

### `index.stories.js`

```javascript
import Templete from './index.html'
import './_index.scss'

export default {
  title: 'Heading',
}

export const HeadingTem = () =>  {
  return Templete
}
```

## テンプレート内に入れる

```
├── _index.scss # スタイル用ファイル
├── index.js # テンプレート用ファイル
└── index.stories.js # storybook用ファイル
```

### `_index.scss`

```scss
.c-title {
  font-size: 40px;
  span {
      font-size: 60px;
  }
}
```

### `index.js`

```javascript
export const Template = () => {
  return `
	<p class='c-text'>Hello <span>World!!</span></p>
  `
}
```

### `index.stories.js`

```javascript
import Templete from './index.js'
import './_index.scss'

export default {
  title: 'Heading',
}

export const HeadingTem = () =>  {
  return Templete
}
```

# 既存のscssファイルの読み込み

参考: [Default Config](https://storybook.js.org/docs/configurations/default-config/#css-support)

- reset.cssなどの読み込み
- 上記のようなコンポーネントごとにscssファイルを記載したくない時
- プロジェクトのscssをそのまま使いたい時

の時に`.storybook/preview.js`に読み込みを行うことで既存のscssファイルを読み込むこともできる。

## `.storybook/preview.js`にscss読み込み設定を記載

```javascript
import '../scss/style.scss' // 読み込みを行いたいscssファイル
```

# アドオン一覧

## アドオン参考リンク

- [アドオン一覧ページ](https://storybook.js.org/addons/)
- [アドオンフレームワーク対応テーブル](https://github.com/storybookjs/storybook/blob/master/ADDONS_SUPPORT.md)

<img width="1024" alt="スクリーンショット 2020-04-05 3.35.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214546/942c3ac9-34d8-6a77-5883-8002be8e76af.png">

※2020年4月5日時点

## 今回使ったアドオン一覧

### [@pickra/copy-code-block](https://www.npmjs.com/package/@pickra/copy-code-block)

HTMLのソースコードを表示・コピーすることができるアドオン

#### インストール

```shell
$ npm i @pickra/copy-code-block
```

#### 使い方

コンポーネントファイル

```javascript
import copyCodeBlock from '@pickra/copy-code-block'
import Templete from './index.html'

export const TempleteCode = () =>  {
  return (Templete + copyCodeBlock(Templete)) // テンプレートの内容 + コードブロックを表示
}
```

### [@storybook/addon-knobs](https://www.npmjs.com/package/@storybook/addon-knobs)

テンプレート内のテキストや状態を動的に変更を行うことができるアドオン

#### インストール

```shell
$ npm i @storybook/addon-knobs
```

#### 使い方

`.storybook/main.js`に以下を記載

```javascript
module.exports = {
  addons: ['@storybook/addon-knobs/register']
}
```

コンポーネントファイル

```javascript
import { withKnobs, text } from '@storybook/addon-knobs'

export default {
  decorators: [withKnobs]
}

export const Template = () => {
  const tmpText = text('Text', 'Hello World!!')
  return `
	<p class='c-text'>${tmpText}</p>
  `
}
```


### [@storybook/addon-a11y](https://www.npmjs.com/package/@storybook/addon-a11y)

コンポーエント内のアクセシビリティをチェックするアドオン

#### インストール

```shell
$ npm i @storybook/addon-a11y
```

#### 使い方

`.storybook/main.js`に以下を記載

```javascript
module.exports = {
  addons: ['@storybook/addon-a11y/register']
}
```

ファイル全体に適応させたい場合、`.storybook/preview.js`に以下を記載

```javascript
import { addDecorator } from '@storybook/html'
import { withA11y } from '@storybook/addon-a11y'

addDecorator(withA11y)
```

### [@storybook/addon-storysource](https://www.npmjs.com/package/@storybook/addon-storysource)

ストーリーのソースを表示するアドオン

#### インストール

```shell
$ npm i @storybook/addon-storysource
```

#### 使い方

`.storybook/main.js`に以下を記載

```javascript
module.exports = {
  addons: ['@storybook/addon-storysource'],
};
```

### [@storybook/addon-notes](https://www.npmjs.com/package/@storybook/addon-notes)

テキストやマークダウン形式でストーリーの説明部などを追加することができるアドオン

#### インストール

```shell
$ npm i @storybook/addon-notes
```

#### 使い方

`.storybook/main.js`に以下を記載

```javascript
module.exports = {
  addons: ['@storybook/addon-notes/register'],
};
```

コンポーネントファイル

```javascript
import README from './README.md' // コンポーネントと同じ階層にファイルを配置

export default {
  title: 'Heading',
  parameters: {
    notes: { README }
  }
}
```


### [@storybook/addon-viewport](https://www.npmjs.com/package/@storybook/addon-viewport)

#### インストール

```shell
$ npm i @storybook/addon-viewport
```

#### 使い方

`.storybook/main.js`に以下を記載

```javascript
module.exports = {
  addons: ['@storybook/addon-viewport/register']
}
```

サイズをカスタマイズしたい場合
既存だと「iphone5」などのサイズがないので、`.storybook/preview.js`をカスタマイズして、任意のサイズでの表示されるようにする。

```javascript
import { addParameters } from '@storybook/html'

const customViewports = {
  iPhone5: {
    name: 'iPhone5',
    styles: {
      width: '320px',
      height: '568px',
    },
  },
  iPhone6: {
    name: 'iPhone6,7,8',
    styles: {
      width: '375px',
      height: '667px',
    },
  },
  iPhoneX: {
    name: 'iPhoneX',
    styles: {
      width: '375px',
      height: '812px',
    },
  },
  iPhoneXsMax: {
    name: 'iPhoneXsMax',
    styles: {
      width: '414px',
      height: '896px',
    },
  },
}

addParameters({
  viewport: { viewports: customViewports }
})
```

### [@storybook/addon-docs](https://www.npmjs.com/package/@storybook/addon-docs)

ストーリーをドキュメント形式で表示してくれるアドオン　

#### インストール

```shell
$ npm i @storybook/addon-docs
```

#### 使い方

`.storybook/main.js`に以下を記載

```javascript
module.exports = {
  addons: ['@storybook/addon-docs'],
};
```

### [@storybook/addon-backgrounds](https://www.npmjs.com/package/@storybook/addon-backgrounds)

storybookの背景色を変更することができるアドオン

#### インストール

```
$ npm i @storybook/addon-backgrounds
```


#### 使い方

`.storybook/main.js`に以下を記載

```javascript
module.exports = {
  addons: ['@storybook/addon-backgrounds/register']
};
```

`.storybook/preview.js`に以下を記載

```javascript
import { addParameters } from '@storybook/html'

addParameters({
  backgrounds: [
    { name: 'white', value: '#fff', default: true }, // デフォルトの色を設定したい場合、「default: true」を記載。
    { name: 'black', value: '#333' },
    { name: 'facebook', value: '#3b5998' },
    { name: 'twitter', value: '#00aced' },
  ]
})
```


### [storybook-dark-mode/register](https://www.npmjs.com/package/storybook-dark-mode)

storybookのテーマカラーをダークモード・ライトモードで切り替えを行うことができるアドオン

#### インストール

```javascript
$ npm i storybook-dark-mode
```

#### 使い方

`.storybook/main.js`に以下を記載

```javascript
module.exports = {
  addons: ['storybook-dark-mode/register']
};
```

`.storybook/preview.js`に以下を記載

```javascript
import { addParameters } from '@storybook/html'

addParameters({
  darkMode: {
    dark: { ...themes.dark, appBg: 'black' },
    light: { ...themes.normal, appBg: 'white' }
  }
})
```

![20200309233915.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214546/50b46004-1493-462b-60d8-5ccb8822ad7b.png)

画像のようなエラーが出た場合、`@storybook/theming`のインストールを行う。

```shell
$ npm i @storybook/theming
```

`.storybook/preview.js`に「themes」を記載

```javascript
import { addParameters } from '@storybook/html'
import { themes } from '@storybook/theming' // これを追記

addParameters({
  darkMode: {
    dark: { ...themes.dark, appBg: 'black' },
    light: { ...themes.normal, appBg: 'white' }
  }
})
```

## アドオンを読みこんだ後の設定ファイルの中身

`.storybook/main.js`

```javascript
const { resolve } = require('path')

module.exports = {
  stories: ['../components/**/*.stories.js'],
  addons: [
    '@storybook/addon-knobs/register',
    '@storybook/addon-a11y/register',
    '@storybook/addon-storysource/register',
    '@storybook/addon-notes/register',
    '@storybook/addon-viewport/register',
    '@storybook/addon-docs',
    '@storybook/addon-backgrounds/register',
    'storybook-dark-mode/register'
  ],
  webpackFinal: async(config, {configType}) => {
    config.module.rules.push({
      test: /\.scss$/,
      use: [
        'style-loader',
        'css-loader',
        'sass-loader'
      ],
      include: resolve(__dirname, '../'),
    })
    return config
  }
}
```

`.storybook/preview.js`

```javascript
import { configure, addParameters, addDecorator } from '@storybook/html'
import { withA11y } from '@storybook/addon-a11y'
import { themes } from '@storybook/theming'

import '../scss/style.scss'

addDecorator(withA11y)

const customViewports = {
  iPhone5: {
    name: 'iPhone5',
    styles: {
      width: '320px',
      height: '568px',
    },
  },
  iPhone6: {
    name: 'iPhone6,7,8',
    styles: {
      width: '375px',
      height: '667px',
    },
  },
  iPhoneX: {
    name: 'iPhoneX',
    styles: {
      width: '375px',
      height: '812px',
    },
  },
  iPhoneXsMax: {
    name: 'iPhoneXsMax',
    styles: {
      width: '414px',
      height: '896px',
    },
  },
}

addParameters({
  viewport: { viewports: customViewports },
  backgrounds: [
    { name: 'white', value: '#fff', default: true },
    { name: 'black', value: '#333' },
    { name: 'facebook', value: '#3b5998' },
    { name: 'twitter', value: '#00aced' },
  ],
  darkMode: {
    dark: { ...themes.dark, appBg: 'black' },
    light: { ...themes.normal, appBg: 'white' }
  }
})
```