Introduction to Using Storybook for Angular

Storybook is a tool that allows to test an app’s components in isolation and in a flat hierarchy. A storybook is a series of stories that display our components with specified inputs and outputs in a nice browser view.

Storybook has been getting really popular for React projects, and support for Angular and Vue projects was added with v3.3.0 so we can now just as easily use it for creating stories for Angular components.

Let’s quickly go over setting up Storybook for Angular as well as basic usage.

Setup

Storybook now comes with a CLI tool that makes it really easy to add it to a project. First, install the @storybook/cli package globally:

# Yarn:
$ yarn global add @storybook/cli

# or npm:
$ npm i @storybook/cli -g

Now, in the root of an Angular project, run the getstorybook command:

$ getstorybook

This command will autodetect that it’s an Angular project and will add the necessary configurations, devDependencies and npm scripts.

For example, at the time of this writing, the following devDependencies are added to the project:

"@storybook/angular": "^3.3.15",
"@storybook/addon-notes": "^3.3.15",
"@storybook/addon-actions": "^3.3.15",
"@storybook/addon-links": "^3.3.15",
"@storybook/addons": "^3.3.15",
"babel-core": "^6.26.0"

And the following npm scripts are also added to the project’s package.json file:

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

You can now try it out by invoking the storybook npm script:

$ npm run storybook

This will start a local webpack server on port 6006 and you can visit the generated storybook by going to http://localhost:6006/. What you’ll see is an example Storybook that was generated from an index.stories.ts file under a stories folder at the root of the project. Here’s what the generated Storybook looks like:

Initial Storybook example

The local server is equiped with webpack's Hot Module Replacement (HMR) so changes to your stories will be reflected automatically in your generated storybook.

You can have a look at the generated sample stories in /stories/index.stories.ts for an example of the syntax and API.

Your First Stories

Let’s now create a simple Card component and a few stories for it to give you an overview of Storybook’s usage. Here’s our component’s template:

card.component.html

<h1>{{ title }}</h1>
<h2>{{ subtitle }}</h2>

<hr *ngIf="title || subtitle">

<p>{{ content }}</p>

<button (click)="handleBtnClick()">Click me!</button>

And here’s our component’s class:

card.component.ts

import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-card',
  templateUrl: './card.component.html',
  styles: [
    `
    :host {
      text-align: center;
      background: white;
      display: block;
      padding: .45rem .65rem;
      border-radius: 3px;
      max-width: 325px;
      box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
    }

    h2 {
      color: #c85f7f;
    }

    p {
      text-align: center;
    }
  `
  ]
})
export class CardComponent {
  @Input('title') title;
  @Input('subtitle') subtitle;
  @Input('content') content = '😄';

  @Output() btnClicked = new EventEmitter<boolean>();

  constructor() {}

  handleBtnClick() {
    this.btnClicked.emit(true);
  }
}

As you can see, it’s a very simple component that uses a few inputs and one output. We’re also making use of the :host selector to style our component’s shell.


Now let’s modify index.stories.ts to add a few stories for our card component:

index.stories.ts

import { storiesOf } from '@storybook/angular';
import { withNotes } from '@storybook/addon-notes';
import { action } from '@storybook/addon-actions';

import { CardComponent } from '../src/app/card/card.component';

storiesOf('Card', module)
  .add('empty', () => ({
    component: CardComponent,
    props: {}
  }))
  .add('with title', () => ({
    component: CardComponent,
    props: {
      title: 'Hello card!'
    }
  }))
  .add('with title and subtitle', () => ({
    component: CardComponent,
    props: {
      title: 'Hello card!',
      subtitle: 'Well hello there 👋'
    }
  }))
  .add(
    'with notes',
    withNotes('Just a few notes about this story...')(() => ({
      component: CardComponent,
      props: {}
    }))
  )
  .add('with action', () => ({
    component: CardComponent,
    props: {
      title: 'A card...',
      subtitle: 'Waiting to be clicked-on',
      btnClicked: action('👊 Button was clicked')
    }
  }));

// let's nest a story into our main `Card` stories
storiesOf('Card/nested', module).add('special card', () => ({
  component: CardComponent,
  props: {
    content: "I'm a card in a nested story!"
  }
}));

Here are a few things to note:

  • As you can see, stories are created by chaining calls to add() on a call to storyOf().
  • add() takes a name for the story and a function that returns an object with the component and props (inputs and outputs).
  • Actions triggered by outputs can be logged using the action addon.
  • Notes can be added to a story using the withNotes addon.

See this page for a list of other addons that can be used with Storybook.


For this example, I wanted the global styles of my Angular app to be reflected in the generated Storybook. It’s as simple as adding a file called preview-head.html in the .config folder that’s added by Storybook to the root of the project. In that file, you can add anything that would go into the head of a document. So let’s add our global styles:

preview-head.html

<style>
  body {
    background: #f3f3f3;
    font-family: Roboto, sans-serif;
    color: #6ba083;
  }
</style>

...and here's what our Storybook now looks like:

Storybook with card stories

Building a Storybook for Deployment

It’s easy to generate a Storybook with static files that can be deployed to a static hosting service like Github pages. This allows to host a Storybook that other members of the team can easily consult.

Simply invoke the build-storybook npm script:

$ npm run build-storybook

It’ll generate a storybook-static folder at the root of the project with all the files ready to be uploaded to your static hosting service of choice. You can also try it out locally using npx and http-server:

$ cd storybook-static
$ npx http-server

📙 The End! Read more about Storybook's usage in the official docs.

  Tweet It

🕵 Search Results

🔎 Searching...