How To Use Storybook with Ionic and ReactJS

Aaron K Saunders - Jun 17 '20 - - Dev Community

Using Storybook for React with Ionic Framework is a great way to increase your efficiency and so much more if you want to build real component based solutions in your application.

You can quickly implement UI components along with validating the actions/events that the components respond to with out constantly rebuilding the entire application and repeating a set of action to get to the proper component on the proper page.

Here I will take the sample list application template and create a story around the message item component

  • move all events to parent component
  • add ability to have event for deleting the item
  • add ability to have event for favoriting the item
  • create a 'decorator' to handle the default Ionic App wrapper

Setup

Working with sample Ionic App with a list of messages.

First we install storybook; this will take a minute, be patient.

npx -p @storybook/cli sb init
Enter fullscreen mode Exit fullscreen mode

Then open a new terminal window and start storybook, make sure you are in the root of the project directory.

npm run storybook
Enter fullscreen mode Exit fullscreen mode

All the Ionic Stuff

Storybook has the concept of decorators which can be used to wrap the stories. So I created an IonWrap decorator that has all of the code to setup a page and content in an Ionic App.

// .storybook/preview.js
import React, { useState } from 'react';
import { addDecorator } from "@storybook/react";
import { 
   IonApp, IonContent, IonPage, IonList, IonButton
} from "@ionic/react";

/* Core CSS required for Ionic components to work properly */
import "@ionic/react/css/core.css";

/* Basic CSS for apps built with Ionic */
import "@ionic/react/css/normalize.css";
import "@ionic/react/css/structure.css";
import "@ionic/react/css/typography.css";

/* Optional CSS utils that can be commented out */
import "@ionic/react/css/padding.css";
import "@ionic/react/css/float-elements.css";
import "@ionic/react/css/text-alignment.css";
import "@ionic/react/css/text-transformation.css";
import "@ionic/react/css/flex-utils.css";
import "@ionic/react/css/display.css";

const IonWrapper = ({ children }) => {
  return (
    <IonApp>
      <IonPage>
        <IonContent>{children}</IonContent>
      </IonPage>
    </IonApp>
  );
};

addDecorator((storyFn) => <IonWrapper>{storyFn()}</IonWrapper>);
Enter fullscreen mode Exit fullscreen mode

This allows me to keep the stories light and focus on just the content of the component I am working on

Setting Up Basic Story For MessageListItem

We need the basic imports for storybook and then we need to import the component that we are working with. We need to import IonList to ensure the IonItem in the MessageListItem is rendered properly.

Create the file, 2-MessageListItem.stories and start to add the following code:

// 2-MessageListItem.stories
import React from "react";
import { action } from "@storybook/addon-actions";
import MessageListItem from "../components/MessageListItem";
import { IonList } from "@ionic/react";
Enter fullscreen mode Exit fullscreen mode

Set the default export for the story to using the MessageListItem component and create the first story

export default {
  title: "MessageListItem",
  component: MessageListItem,
};

export const BasicMessage = () => {
  let message = {
    fromName: "Andrea Cornerston",
    subject: "Last minute ask",
    summary: "Basic Message Summary",
    date: "Yesterday",
    id: 5,
  };
  return (
    <IonList>
      <MessageListItem
        key={message.id}
        message={message}
      />
    </IonList>
  );
};
Enter fullscreen mode Exit fullscreen mode

I have mocked the data message to that we have some content to render in the ListItem, you should have the component rendering in the storybook web application.

Working On Component Actions

Storybook Addon Actions can be used to display data received by event handlers in Storybook.See documentation

Let's set the component up properly so there is no business logic handled in the component, just responding to actions and passing the actions up to the parent.

First the Click Event On The Item

Modify the properties that are passed into the component to also include the click event which is called when the item is clicked. We want the function to return the message object that was clicked.

// src/components/MessageListItem.tsx
interface MessageListItemProps {
  message: Message;
  handleClick: any;
}
Enter fullscreen mode Exit fullscreen mode

We will change the IonItem to handle the event

// src/components/MessageListItem.tsx
<IonItem onClick={()=>handleClick(message)} detail={false}>
  ... OTHER CODE ...
</IonItem>
Enter fullscreen mode Exit fullscreen mode

Now back in our story, we can use the action add-on to handle the response from the click event to know it is working properly

// 2-MessageListItem.stories
<IonList>
  <MessageListItem
    key={m.id}
    message={m}
    handleClick={action("item-clicked")}
    handleFavorite={action("option-handleFavorite")}
    handleDelete={action("option-handleDelete")}
  />
</IonList>
Enter fullscreen mode Exit fullscreen mode

HANDLE CLICK ACTION

Alt Text

Handle Item Option Events

One way to handle multiple actions on a List Item is using the IonOptions that are displayed when you swipe the item. In this example we will support deleting the item or adding it to you favorites. Once again we want to keep this component simple and have the parent respond to the event.

Lets add the additional properties to the component

// src/components/MessageListItem.tsx
interface MessageListItemProps {
  message: Message;
  handleClick: any;
  handleFavorite: any;
  handleDelete: any;
}
Enter fullscreen mode Exit fullscreen mode

We will change the IonItem to handle the events and once again pass back the associated object

// src/components/MessageListItem.tsx
<IonItemSliding>
  <IonItem 
    onClick={()=>handleClick(message)} 
      detail={false}>
      ... OTHER CODE ...
  </IonItem>
  <IonItemOptions side="end">
     <IonItemOption 
       onClick={() => handleFavorite(message)}>
      Favorite
    </IonItemOption>
    <IonItemOption 
       onClick={() => handleDelete(message)} color="danger">
      Delete
    </IonItemOption>
  </IonItemOptions>
</IonItemSliding>
Enter fullscreen mode Exit fullscreen mode

Now when we click the options we get the corresponding events and properties displayed in the actions area in storybook.

Delete Action

Alt Text

Favorite Action

Alt Text

Modified Source Files

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