最近仕事でデザインガイドラインを整備する中で、
「各種UIパーツはStorybook (https://storybook.js.org/) で管理できたほうが嬉しいんじゃないかな」という思いもあり
Angular + Storybookを試してみたメモです。
環境
- macOS Mojave 10.14
- node v8.11.3
- npm v5.6.0
- yarn 1.9.4
- @angular/cli v7.0.2
Angular CLIプロジェクトを作成する
$ ng new awesome-web-app --prefix=pp
で
新規Angular CLIプロジェクトを作成します。
僕はデフォルトであらゆるprefixがapp
になるのがイヤだったので、--prefix=pp
をつけています。
Angular CLI v7 から ng new
時にいくつか質問されます。自分の環境では
Would you like to add Angular routing? (y/N) > N
-
Which stylesheet format would you like to use? > Stylus
を選択しました。
Storybookをインストールする
$ npm i -g @storybook/cli
で
StorybookのCLIをグローバルインストールします。
プロジェクトにStorybookを導入する
AngularにStorybookを導入するための方法は、公式の
Storybook for Angular を参考にするといいです。
今回はQuick Startのやり方と併用させます。
先程生成したプロジェクトフォルダに移動して
$ getstorybook
を叩きます。
Angularのプロジェクトであることを認識して、関連パッケージのインストールなどを行ってくれます。
babel-core
の追加インストールが追加で必要なので
$ yarn add babel-core --dev
を叩きます。
package.json
も少し変更します。
- "storybook": "start-storybook -p 6006"
+ "storybook": "start-storybook -p 9001 -c .storybook"
ng serve
コマンド実行時にエラーが出ないように
src/app/tsconfig.app.json
に
"exclude": [
"test.ts",
- "**/*.spec.ts"
+ "**/*.spec.ts",
+ "stories",
+ "**/*.stories.ts"
]
の変更を加えます。
一旦 $ yarn run storybook
を叩いて、
http://localhost:9001 を開いて以下の画面が出れば一旦の導入はOKです。
コンポーネントを作成する
試しにボタンのコンポーネントを作ってみます。
$ ng serve
で開発用サーバーを立ち上げたあと、
新しいタブで $ ng g c button-normal
を叩いてコンポーネントを作成します。
今回は以下のような ts/html/stylを書きました。お好みで好きなコンポーネントを作るといいと思います。
<div class="container" (click)="click()" [class.disabled]="disabled">
{{label}}
</div>
.container
height 50px
width 200px
display flex
align-items center
justify-content center
background-color #489BC6
color #ffffff
font-size 14px
font-weight bold
border-radius 5px
cursor pointer
&:hover
opacity .8
&.disabled
background-color #eeeeee
color #bdbdbd
pointer-events none
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
@Component({
selector: 'pp-button-normal',
templateUrl: './button-normal.component.html',
styleUrls: ['./button-normal.component.styl']
})
export class ButtonNormalComponent implements OnInit {
@Input() label = '';
@Input() disabled = false;
@Output() buttonClick = new EventEmitter<string>();
constructor() { }
ngOnInit() { }
click () {
this.buttonClick.next(`${Date.now()}`);
}
}
作成したコンポーネントは以下のような見た目です。
いたって普通のボタンって感じですね。
<button-normal label="awesome-web-app"></button-normal>
<button-normal label="awesome-web-app" disabled="true"></button-normal>
Storybookで見てみる
Storybookのファイルに追記する
src/stories/index.stories.ts
に先ほどビルドしたときに出てきた
いくつかのサンプルが予め書かれています。
とりあえずそれらはそのままにしておいて、
import { storiesOf } from '@storybook/angular';
import { withNotes } from '@storybook/addon-notes';
import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links';
import { Welcome, Button } from '@storybook/angular/demo';
+ import { ButtonNormalComponent } from '../app/button-normal/button-normal.component';
<略>
+ const buttonStories = storiesOf('App Button', module);
+ buttonStories.add('button-normal', () => (
+ {
+ component: ButtonNormalComponent,
+ props: {
+ label: 'Storybook',
+ }
+ }));
Stylusを使えるようにする
追記後に再び yarn run storybook
とすると
ブラウザ上に以下のようなエラーが出ました。Stylusで書いているので設定ファイルをいじる必要がありました。
Custom Webpack Configをゴリゴリ書く必要がありました。
というわけでプロジェクトのルートにある .storybook
フォルダの中に
webpack.config.js
を作成します。
const path = require('path');
module.exports = {
module: {
rules: [
{
test: /\.styl$/,
loaders: ['to-string-loader', 'style-loader', 'css-loader', 'stylus-loader'],
include: path.resolve(__dirname, '../')
}
]
}
};
$ yarn add to-string-loader --dev
もあわせて叩きます。
そして…
再び$ yarn run storybook
を叩くと
無事にボタンをStorybookで表示させることができました
knobsアドオンを追加する
knobsアドオンを追加すると、ブラウザ上のstorybookから各コンポーネントの状態を
いじることができるようになります。
$ yarn add @storybook/addon-knobs --dev
を叩いて
import '@storybook/addon-actions/register';
import '@storybook/addon-links/register';
import '@storybook/addon-notes/register';
+ import '@storybook/addon-knobs/register';
+ import { text, boolean, withKnobs } from '@storybook/addon-knobs/angular';
const buttonStories = storiesOf('App Button', module);
+ buttonStories.addDecorator(withKnobs);
buttonStories.add('button-normal', () => (
{
component: ButtonNormalComponent,
props: {
- label: 'Storybook',
+ label: text('text', 'Storybook'),
+ disabled: boolean('disabled', false)
}
}));
ブラウザ上からボタンの状態を操作することができるようになりました!
staticサイトを生成する
できあがったコンポーネントのカタログをどこかにアップして、
他のエンジニアやデザイナーと共有するために
$ yarn run build-storybook
でstaticなサイトを生成します。
storybook-static
というフォルダが出来上がるので、どこかにアップして終了です。
メモ: コンポーネントのOutputを拾う
buttonStories.add('button-normal', () => (
{
component: ButtonNormalComponent,
props: {
label: text('label', 'Storybook'),
- disabled: boolean('disabled', false)
+ disabled: boolean('disabled', false),
+ buttonClick: action('clicked')
}
}));
メモ: storybook-readme を使ってみる
https://github.com/tuchk4/storybook-readme を使ってみました。
フル機能でサポートしているわけではなさそうでしたが、いい感じに使えそうです。
$ yarn add storybook-readme --dev
を叩いて
+ declare module '*.md' {
+ const value: string;
+ export default value;
+ }
"typeRoots": [
- "node_modules/@types"
+ "node_modules/@types",
+ "src/typings.d.ts"
],
+ import 'storybook-readme/register';
## Button normal component
いい感じのボタンです。
| a | b | c |
| :--: | :--: | :--: |
| v | v | v |
+ import ButtonReadme from '../app/button-normal/README.md';
+ import { withReadme, withDocs } from 'storybook-readme';
<略>
- buttonStories.add('button-normal', () => (
- {
- component: ButtonNormalComponent,
- props: {
- label: text('label', 'Storybook'),
- disabled: boolean('disabled', false)
- }));
+ buttonStories.add(
+ 'button-normal',
+ withReadme(ButtonReadme, () => (
+ {
+ component: ButtonNormalComponent,
+ props: {
+ label: text('label', 'Storybook'),
+ disabled: boolean('disabled', false),
+ buttonClick: action('clicked')
+ }
+ ))
+ );