How To Use Storybook with Ionic and ReactJS
Tags: ionic, react, storybook, javascript
Published: 2020-06-17 14:59:52

Using [Storybook for React](https://storybook.js.org/docs/guides/guide-react/) with [Ionic Framework](www.ionic.com) is a great way to increase your efficiency and so much more if you want to build real component based solutions in your application. 

{% youtube vHJK3OwDZTA %}

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'](https://storybook.js.org/docs/addons/introduction/) 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
```
Then open a new terminal window and start storybook, make sure you are in the root of the project directory.
```
npm run storybook
```

##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.

```javascript
// .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>);
```
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:
```javascript
// 2-MessageListItem.stories
import React from "react";
import { action } from "@storybook/addon-actions";
import MessageListItem from "../components/MessageListItem";
import { IonList } from "@ionic/react";
```
Set the default export for the story to using the `MessageListItem` component and create the first story
```javascript
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>
  );
};
```
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](https://www.npmjs.com/package/@storybook/addon-actions)


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.
```javascript
// src/components/MessageListItem.tsx
interface MessageListItemProps {
  message: Message;
  handleClick: any;
}
```
We will change the `IonItem` to handle the event
```react
// src/components/MessageListItem.tsx
<IonItem onClick={()=>handleClick(message)} detail={false}>
  ... OTHER CODE ...
</IonItem>
```
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
```javascript
// 2-MessageListItem.stories
<IonList>
  <MessageListItem
    key={m.id}
    message={m}
    handleClick={action("item-clicked")}
    handleFavorite={action("option-handleFavorite")}
    handleDelete={action("option-handleDelete")}
  />
</IonList>
```
####HANDLE CLICK ACTION
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/toyg29b3wlsa819aok4n.png)
### 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
```javascript
// src/components/MessageListItem.tsx
interface MessageListItemProps {
  message: Message;
  handleClick: any;
  handleFavorite: any;
  handleDelete: any;
}
```
We will change the `IonItem` to handle the events and once again pass back the associated object
```react
// 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>
```
Now when we click the options we get the corresponding events and properties displayed in the actions area in storybook.

####Delete Action
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/2hl5m1d4ckddw5w8x8wx.png)
####Favorite Action
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/a3nxkjr19wgaf595bjmm.png)

####Modified Source Files
{% gist https://gist.github.com/aaronksaunders/7db8d84555f1a736a2777825ec8bca73 %}