Storybook for Angular
You may have tried to use our quick start guide to setup your project for Storybook. If you want to set up Storybook manually, this is the guide for you.
This will also help you understand how Storybook works.
Starter Guide Angular
Storybook has its own webpack setup and a dev server.
The webpack setup is very similar to Angular CLI, but allows you to configure it however you want.
In this guide, we are trying to set up Storybook for your Angular project.
Table of contents
- Create Angular project
- Add @storybook/angular and babel-core
- Create the config file
- Write your stories
- Run your Storybook
Create Angular project
First of all, you need to prepare an Angular project. To do that, run:
npm i -g @angular/cli
ng new your-angular-prj
cd your-angular-prj
Add @storybook/angular, @babel/core, and babel-loader
Next, install @storybook/angular
, @babel/core
, and babel-loader
(it’s a peerDependency) to your project:
npm i --save-dev @storybook/angular @babel/core babel-loader
Then add the following NPM script to your package json in order to start the storybook later in this guide:
{
"scripts": {
"storybook": "start-storybook -p 9001 -c .storybook"
}
}
Create the config file
Storybook can be configured in several different ways.
That’s why we need a config directory. We’ve added a -c
option to the above NPM script mentioning .storybook
as the config directory.
For the basic Storybook configuration file, you don’t need to do much, but simply tell Storybook where to find stories.
To do that, simply create a file at .storybook/config.js
with the following content:
import { configure } from '@storybook/angular';
function loadStories() {
require('../src/stories/index.ts');
}
configure(loadStories, module);
That’ll load stories in ../src/stories/index.ts
.
Just like that, you can load stories from wherever you want to.
Storybook TypeScript configuration
Note: You only need this if you are using Storybook >= 4.0.0-alpha.23
.
@storybook/angular
is using ForkTsCheckerWebpackPlugin to boost the build performance.
This makes it necessary to create a tsconfig.json
file at .storybook/tsconfig.json
with the following content:
{
"extends": "../tsconfig.json",
"exclude": [
"../src/test.ts",
"../src/**/*.spec.ts",
"../projects/**/*.spec.ts"
],
"include": [
"../src/**/*",
"../projects/**/*"
]
}
Only files that are included in the
include
orfiles
section are checked for semantic TypeScript errors. For more information visit the ForkTsCheckerWebpackPlugin documentation.
Write your stories
Now you can write some stories inside the ../src/stories/index.ts
file, like this:
import { storiesOf } from '@storybook/angular';
import { action } from '@storybook/addon-actions';
import { MyButtonComponent } from '../app/my-button/my-button.component';
storiesOf('My Button', module)
.add('with some emoji', () => ({
component: MyButtonComponent,
props: {
text: '😀 😎 👍 💯',
},
}))
.add('with some emoji and action', () => ({
component: MyButtonComponent,
props: {
text: '😀 😎 👍 💯',
click: action('clicked'),
},
}));
Each story is a single state of your component. In the above case, there are two stories for the MyButton component:
- story with
@Input()
property binding. - story with
@Input()
and@Output()
property binding.
Run your Storybook
Now everything is ready. Simply run your storybook with:
npm run storybook
Now you can change components and write stories whenever you need to. You’ll get those changes into Storybook in a snap with the help of webpack’s HMR API.
Module Metadata
If your component has dependencies on other Angular directives and modules, these can be supplied using the moduleMetadata
property on an individual story:
import { CommonModule } from '@angular/common';
import { storiesOf } from '@storybook/angular';
import { MyButtonComponent } from '../app/my-button/my-button.component';
import { MyPanelComponent } from '../app/my-panel/my-panel.component';
import { MyDataService } from '../app/my-data/my-data.service';
storiesOf('My Panel', module)
.add('Default', () => ({
component: MyPanelComponent,
moduleMetadata: {
imports: [CommonModule],
schemas: [],
declarations: [MyButtonComponent],
providers: [MyDataService],
}
}));
If you have metadata that is common between your stories, this can configured once using the moduleMetadata()
decorator:
import { CommonModule } from '@angular/common';
import { storiesOf, moduleMetadata } from '@storybook/angular';
import { MyButtonComponent } from '../app/my-button/my-button.component';
import { MyPanelComponent } from '../app/my-panel/my-panel.component';
import { MyDataService } from '../app/my-data/my-data.service';
storiesOf('My Panel', module)
.addDecorator(
moduleMetadata({
imports: [CommonModule],
schemas: [],
declarations: [MyButtonComponent],
providers: [MyDataService],
})
)
.add('Default', () => ({
component: MyPanelComponent
}))
.add('with a title', () => ({
component: MyPanelComponent,
props: {
title: 'Foo',
}
}));
Trouble Shooting
If you have problems running @angular/cli using “ng serve” after install specifically the following error:
ERROR in node_modules/@storybook/angular/index.d.ts(31,44): error TS2304: Cannot find name 'NodeRequire'.
You may need to exclude your stories from being compiled when running your angular dev environment. To do this add “stories”, ”**/*.stories.ts” to the exclude section in src/app/tsconfig.app.json:
{
"exclude": [
"stories",
"**/*.stories.ts"
]
}
If you’re using scss for styling, you need to have node-sass
dependency installed on your side (directly or via @angular/cli
).