Stories as documentation

Storybook is much more than a convenient place to isolate your components while they're developed. With add-ons, it's also an effective tool for documenting your components. As your application grows, it's all the more compelling to have something like Storybook in place. Other developers are likely going to have to work with components that you've created. Wouldn't it be nice if they could look at Storybook stories to see the various ways your component can be used?

The last add-on that we'll look at in this chapter is called Info. It provides usage info about the component in a nicely-presented format, in addition to the standard rendered component output.

Let's create a couple of components that we want to document. Instead of writing every story in stories/index.js like you have been throughout this chapter, let's separate your stories into something more consumable:

  • stories/MyButton.story.js
  • stories/MyList.story.js

The stories for the two components that you're about to implement will be separated in their own modules, which will be a little easier to maintain going forward. Another change that you'll have to make in order to support this new file layout is in .storybook/config.js. Here, you'll have to require your two story modules individually:

import { configure } from '@storybook/react'; 
 
function loadStories() { 
  require('../src/stories/MyButton.story'); 
  require('../src/stories/MyList.story'); 
}
configure(loadStories, module);

Let's take a look at the components now. First, there's MyButton:

import React from 'react'; 
import PropTypes from 'prop-types'; 
 
const MyButton = ({ onClick }) => ( 
  <button onClick={onClick}>My Button</button> 
); 
 
MyButton.propATypes = { 
  onClick: PropTypes.func 
}; 
 
export default MyButton; 

You can see that MyButton defines a propTypes property; you'll see why this is important for the Info Storybook add-on shortly. Next, let's look at the MyList component:

import React from 'react'; 
import PropTypes from 'prop-types'; 
 
const Empty = ({ items, children }) => 
  items.length === 0 ? children : null; 
 
const MyList = ({ items }) => ( 
  <section> 
    <Empty items={items}>No items found</Empty> 
    <ul>{items.map((v, i) => <li key={i}>{v}</li>)}</ul> 
  </section> 
); 
 
MyList.propTypes = { 
  items: PropTypes.array 
}; 
 
MyList.defaultProps = { 
  items: [] 
}; 
export default MyList;

This component also defines a propTypes property. It defines a defaultProps property as well so that when the items property isn't provided, it has an empty array by default so that the call to map() still works.

Now you're ready to write stories for these two components. Keeping in mind that you also want these stories to serve as the main source of documentation for your components, you'll use the Info add-on for Storybook to give users more usage information for any given story. Let's start with MyButton.story.js:

import React from 'react'; 
import { storiesOf } from '@storybook/react'; 
import { withInfo } from '@storybook/addon-info'; 
import { action } from '@storybook/addon-actions'; 
import MyButton from '../MyButton'; 
 
storiesOf('MyButton', module) 
  .add( 
    'basic usage', 
    withInfo(' 
      Without passing any properties 
    ')(() => <MyButton />) 
  ) 
  .add( 
    'click handler', 
    withInfo(' 
      Passing an event handler function that's called when 
      the button is clicked 
    ')(() => <MyButton onClick={action('button clicked')} />) 
  ); 

Here, you're documenting MyButton using two stories, each of which show a different way to use the component. The first story shows the basic usage and the second story shows how to pass a click handler property. The new addition to these stories is the call to withInfo(). This function is from the Info Storybook add-on, and you can pass it some text (markdown supported) that goes into more detail about the story. In other words, this is where you document a specific use of your component.

Now let's look at MyList.story.js before we see what the output of the Info add-on looks like in the Storybook UI:

import React from 'react'; 
import { storiesOf } from '@storybook/react'; 
import { withInfo } from '@storybook/addon-info'; 
import MyList from '../MyList'; 
 
storiesOf('MyList', module) 
  .add( 
    'basic usage', 
    withInfo(' 
      Without passing any properties
')(() => <MyList />) ) .add( 'passing an array of items', withInfo(' Passing an array to the items property ')(() => <MyList items={['first', 'second', 'third']} />) );

This looks a lot like the stories defined for MyButton—different docs and components, same overall structure and approach.

Let's take a look at the default usage story for MyButton:

As expected, the button is rendered in the output pane so that users can see what they're working with. In the top-right corner of the output pane, there's an info button. When you click on it, you see all of the extra info provided by calling withInfo() in your story:

This reveals all sorts of information about the story, and the component that you're documenting. From top to bottom, here is what it displays:

  • Component name
  • Story name
  • Usage documentation (provided as an argument to withInfo())
  • Source used to render component
  • Properties available to component (read from propTypes)

The nice thing about the Info add-on is that it shows your users the source used to render the output that they're looking at, and shows the available properties if you provide them as prop types. This means that someone who is trying to understand and use your components can get the information they need without you, the component author, putting in a ton of extra effort.

Let's take a look at the MyList component when it's passed an array of items:

It renders a list of items that it gets through a prop. Let's look at the info for this story now:

By looking at the information about this story, you can see at a glance the props that this component accepts, what their default values are, and the code that was used to generate the example, all in one place. I also like the fact that the info pane is hidden by default, meaning that you can navigate through stories and look for the end result that you need, and only then worry about the details.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.102.225