How To Use Storybook v7 With Ionic Framework And Angular

Aaron K Saunders - May 15 '23 - - Dev Community

Storybook is a tool that helps you develop and test your UI components in isolation. It can be used with any framework, but it is especially well-suited for Angular applications. In this blog post, we will show you how to set up Storybook version 7 to work with Ionic Framework and Angular.

This blog post assumes you have already created your Ionic Framework Angular Project. See related video covering this blog post here on YouTube

Setup

In your project directory run the command below to setup Storybook in your project

npx storybook@latest init
Enter fullscreen mode Exit fullscreen mode

Now we need to modify the angular.json file in your projects root directory to include assets, styles and ionic icons in the project that Storybook will create for your Storybook stories.

"storybook": {
  "builder": "@storybook/angular:start-storybook",
  "options": {
    "configDir": ".storybook",
    "browserTarget": "app:build",
    "compodoc": false,
    "port": 6006,
    "assets": [
      {
        "glob": "**/*",
        "input": "src/assets",
        "output": "assets"
      },
      {
        "glob": "**/*.svg",
        "input": "node_modules/ionicons/dist/ionicons/svg",
        "output": "./svg"
      }
    ],
    "styles": [
      "src/theme/variables.scss",
      "src/global.scss"
    ]
  }
},
Enter fullscreen mode Exit fullscreen mode

MemberCard Component

In my sample project I have a component called MemberCard that I would like to create a story for and here is the code for the component template

<div>
  <ion-card>
    <ion-card-header>
      <ion-card-title>{{ memberInfo?.name }}</ion-card-title>
      <ion-card-subtitle>
        <span *ngIf="memberInfo?.companyName">{{ memberInfo?.companyName }} : </span>
        {{ memberInfo?.title }}
        </ion-card-subtitle>
    </ion-card-header>

    <ion-card-content>
      <div>{{ memberInfo?.bio }}</div>
      <ion-item lines="none" class="ion-float-right">
        <ion-button
          *ngIf="memberInfo?.linkedIn"
          (click)="
            onClick.emit({
              event: 'social-clicked',
              value: memberInfo?.linkedIn
            })
          "
          ><ion-icon icon="logo-linkedin" slot="start"></ion-icon
          >LinkedIn</ion-button
        >
        <ion-button
          *ngIf="memberInfo?.twitter"
          (click)="
            onClick.emit({
              event: 'social-clicked',
              value: memberInfo?.twitter
            })
          "
          ><ion-icon icon="logo-twitter" slot="start"></ion-icon
          >Twitter</ion-button
        >
        <ion-button
          *ngIf="memberInfo?.website"
          (click)="
            onClick.emit({
              event: 'social-clicked',
              value: memberInfo?.website
            })
          "
          ><ion-icon icon="globe" slot="start"></ion-icon>Web Site</ion-button
        >
      </ion-item>
    </ion-card-content>
  </ion-card>
</div>
Enter fullscreen mode Exit fullscreen mode

and this is the code for the component

import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { globe as globeIcon } from 'ionicons/icons';

export interface MemberInfo {
  name: string;
  title: string;
  companyName?: string;
  bio: string;
  twitter?: string;
  linkedIn?: string;
  website?: string;
}
@Component({
  selector: 'app-member-card',
  templateUrl: './member-card.component.html',
  styleUrls: ['./member-card.component.scss'],
})
export class MemberCardComponent implements OnInit {
  @Input() memberInfo!: MemberInfo;
  @Output()
  onClick = new EventEmitter<{ event: string; value: string | undefined }>();

  constructor() {}

  ngOnInit() {}
}
Enter fullscreen mode Exit fullscreen mode

MemberCard Story

I created my story by copying the sample Button.story and making the appropriate modifications. you can find your project's stories in the src\stories directory.

First you need to import your component, and then supporting modules

import { MemberCardComponent } from 'src/app/member-card/member-card.component';
import { IonicModule } from '@ionic/angular';
import { importProvidersFrom } from '@angular/core'; 
Enter fullscreen mode Exit fullscreen mode

Then you need to setup the story using the imported component.

  • The title is setup so that it will create a hierarchy with the toplevel being project and the new story MemberCardComponent as a child of Project
  • component is the name of the component MemberCardComponent
  • render set the type of the args to your component this ensures that the properties from your component get passed to story when it is rendered
  • argTypes - the MemberCardComponent emits and onClick event when the user clicks on the social media buttons

The decorators property is an array of decorators that will wrap the story.

  • applicationConfig is where we import modules that are needed for the Story.
  • moduleMetadata the additional module information needed to support the component
  • componentWrapperDecorator wraps the rendering of the component. In this case I am wrapping the component in the IonApp element
const meta: Meta<MemberCardComponent> = {
  title: 'Project/MemberCardComponent',
  component: MemberCardComponent,
  tags: ['autodocs'],
  render: (args: MemberCardComponent) => ({
    props: {
      ...args,
    },
  }),
  argTypes: { onClick: { action: 'clicked' } },
  decorators: [
    applicationConfig({
      providers: [importProvidersFrom([IonicModule.forRoot()])],
    }),
    moduleMetadata({
      declarations: [MemberCardComponent],
    }),
    componentWrapperDecorator((story) => `<ion-app>${story}</ion-app>`),
  ],
};
Enter fullscreen mode Exit fullscreen mode

For the actual stories, all you have left to do is create a export for each story, name it and pass it appropriate arguments. Here is an example of a story with all parameters passed in the memberInfo property called Primary

export const Primary: Story = {
  args: {
    memberInfo: {
      name: 'Aaron Saunders',
      title: 'CEO',
      bio: 'Amazing Experience',
      companyName: 'Clearly Innovative Inc',
      linkedIn: 'Clearly Innovative Inc',
      twitter: 'https://twitter.com/aaronksaunders',
      website: 'www.clearlyinnovative.com',
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

To see how the UI for the component is rendered when missing the social media properties we have this story named MissingSocial you can see the code below

export const MissingSocial: Story = {
  args: {
    memberInfo: {
      name: 'Aaron Saunders',
      title: 'CEO',
      companyName: 'Clearly Innovative Inc',
      bio: 'No news',
      website: 'www.clearlyinnovative.com',
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

Running Storybook

npm run storybook
Enter fullscreen mode Exit fullscreen mode

Conclusion

In this blog post, we showed you how to set up Storybook version 7 to work with Ionic Framework and Angular. We also showed you how to create stories to test your UI components.

By following the steps in this blog post, you will be able to use Storybook to improve the quality of your Angular applications.


Video

Project Source Code

GitHub logo aaronksaunders / storybookv7-ionic-angular

Simple demonstration of using storybook v7 with ionic framework

Simple Storybook v7 Angular Ionic Integration

Simple demonstration of using storybook v7 with ionic framework

Video

Setup

In project directory

npx storybook@latest init

Changes to angular.json

"storybook": {
  "builder": "@storybook/angular:start-storybook",
  "options": {
    "configDir": ".storybook",
    "browserTarget": "app:build",
    "compodoc": false,
    "port": 6006,
    "assets": [
      {
        "glob": "**/*",
        "input": "src/assets",
        "output": "assets"
      },
      {
        "glob": "**/*.svg",
        "input": "node_modules/ionicons/dist/ionicons/svg",
        "output": "./svg"
      }
    ],
    "styles": [
      "src/theme/variables.scss",
      "src/global.scss"
    ]
  }
},
Enter fullscreen mode Exit fullscreen mode

Specific Change to a basic story to support Angular & Ionic Framework

  decorators: [
    // include IonicModule
    applicationConfig({
      providers: [importProvidersFrom([IonicModule.forRoot()])],
Enter fullscreen mode Exit fullscreen mode

Please consider signing up for our Newsletter​

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .