LoginSignup
1

More than 3 years have passed since last update.

Angular v7 + Storybookを試してみたメモ

Posted at

最近仕事でデザインガイドラインを整備する中で、
「各種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も少し変更します。

package.json
- "storybook": "start-storybook -p 6006"
+ "storybook": "start-storybook -p 9001 -c .storybook"

ng serveコマンド実行時にエラーが出ないように
src/app/tsconfig.app.json

tsconfig.app.json
   "exclude": [
     "test.ts",
-    "**/*.spec.ts"
+    "**/*.spec.ts",
+    "stories",
+    "**/*.stories.ts"
   ]

の変更を加えます。

一旦 $ yarn run storybook を叩いて、
http://localhost:9001 を開いて以下の画面が出れば一旦の導入はOKです。
スクリーンショット 2018-10-23 22.46.46.png

コンポーネントを作成する

試しにボタンのコンポーネントを作ってみます。
$ ng serve で開発用サーバーを立ち上げたあと、
新しいタブで $ ng g c button-normal を叩いてコンポーネントを作成します。

今回は以下のような ts/html/stylを書きました。お好みで好きなコンポーネントを作るといいと思います。

button-normal.component.ts
<div class="container" (click)="click()" [class.disabled]="disabled">
  {{label}}
</div>
button-normal.component.styl
.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
button-normal.component.ts
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>

スクリーンショット 2018-10-23 23.06.02.png

Storybookで見てみる

Storybookのファイルに追記する

src/stories/index.stories.ts に先ほどビルドしたときに出てきた
いくつかのサンプルが予め書かれています。
とりあえずそれらはそのままにしておいて、

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で書いているので設定ファイルをいじる必要がありました。
スクリーンショット 2018-10-23 23.12.59.png
Custom Webpack Configをゴリゴリ書く必要がありました。

というわけでプロジェクトのルートにある .storybook フォルダの中に
webpack.config.jsを作成します。

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 を叩くと
スクリーンショット 2018-10-23 23.20.59.png
無事にボタンをStorybookで表示させることができました :tada:

knobsアドオンを追加する

knobsアドオンを追加すると、ブラウザ上のstorybookから各コンポーネントの状態を
いじることができるようになります。

$ yarn add @storybook/addon-knobs --dev を叩いて

.storybook/addons.js
  import '@storybook/addon-actions/register';
  import '@storybook/addon-links/register';
  import '@storybook/addon-notes/register';
+ import '@storybook/addon-knobs/register';
src/stories/index.stories.ts
+ 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)
      }
  }));

スクリーンショット 2018-10-23 23.34.02.png

ブラウザ上からボタンの状態を操作することができるようになりました!

staticサイトを生成する

できあがったコンポーネントのカタログをどこかにアップして、
他のエンジニアやデザイナーと共有するために
$ yarn run build-storybook でstaticなサイトを生成します。
storybook-staticというフォルダが出来上がるので、どこかにアップして終了です。

メモ: コンポーネントのOutputを拾う

index.stories.ts
  buttonStories.add('button-normal', () => (
    {
      component: ButtonNormalComponent,
      props: {
        label: text('label', 'Storybook'),
-       disabled: boolean('disabled', false)
+       disabled: boolean('disabled', false),
+       buttonClick: action('clicked')
      }
  }));

とコードを追加すると
image.png
といった感じでイベントを拾える。

メモ: storybook-readme を使ってみる

https://github.com/tuchk4/storybook-readme を使ってみました。

フル機能でサポートしているわけではなさそうでしたが、いい感じに使えそうです。
スクリーンショット 2018-10-23 23.59.28.png

$ yarn add storybook-readme --dev を叩いて

src/typings.d.ts
+ declare module '*.md' {
+   const value: string;
+   export default value;
+ }
tsconfig.json
     "typeRoots": [
-      "node_modules/@types"
+      "node_modules/@types",
+      "src/typings.d.ts"
     ],
.storybook/addons.ts
+  import 'storybook-readme/register';
src/app/button-normal/README.md
## Button normal component
いい感じのボタンです。  

| a | b | c |
| :--: | :--: | :--: |
| v | v | v |
stories/index.stories.ts
+  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')
+       }
+   ))
+ );


参考

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
What you can do with signing up
1