storybook-addon-react-docgen

A storybook addon to display react docgen info.
This addon is a drop in replacement for the "info" addon's prop table functionality.
Rather than rendering with the component it renders in the addons panel.
Works with typescript too!
There exist other addons that do this, but they didn't work in the same way as the info
addon.
This resulted in complicated configuration changes.
This plugin aims to be painless to switch to.

Installation
yarn add storybook-addon-react-docgen
React Docgen Integration
React Docgen is included as part of the @storybook/addon-docs
package.
If you are using @storybook/addon-docs
then you do not need to set up docgen and can skip the next steps
Typescript DocGen
To use this plugin with a typescript project you need to install react-docgen-typescript-plugin and configure webpack to use it.
Javascript DocGen
To use this plugin with a javascript project you need to install babel-plugin-react-docgen
Usage
Add it in your main.js
addons":
module.exports = {
stories: ['../stories/**/*.stories.js'],
addons: ['@storybook/addon-docs', 'storybook-addon-react-docgen']
};
Then add the withPropsTable
decorator to your preview.js
.
You can pass global options here if you want:
const { addDecorator } = require('@storybook/react');
const { withPropsTable } = require('storybook-addon-react-docgen');
addDecorator(withPropsTable(options));
addDecorator(withPropsTable);
You can use the props
parameter to configure the options for individual stories:
import { storiesOf } from '@storybook/react';
import Other from './Other';
import Component from './Component';
export default {
title: 'Components/Button'
};
export const WithSomeEmoji = () => (
<Component>
<Other />
</Component>
);
WithSomeEmoji.parameters: {
props: {
propTablesExclude: [
Other
]
}
}
or for the entire story:
import { storiesOf } from '@storybook/react';
import Other from './Other';
import Component from './Component';
export default {
title: 'Components/Button',
parameters: {
props: {
propTablesExclude: [
Other
]
}
}
};
export const WithSomeEmoji = () => (
<Component>
<Other />
</Component>
);
Configuration
{
propTables: Array<React.ComponentType>,
propTablesSortOrder: string,
propTablesInclude: Array<React.ComponentType | string>,
propTablesExclude: Array<React.ComponentType | string>,
styles: Object | Function,
maxPropsIntoLine: number,
maxPropObjectKeys: number,
maxPropArrayLength: number,
maxPropStringLength: number,
TableComponent: React.ComponentType,
excludedPropTypes: Array<string>,
}
Rendering a Custom Table
The TableComponent option allows you to define how the prop table should be rendered. Your component will be rendered with the following props.
{
propDefinitions: Array<{
property: string,
propType: Object | string,
required: boolean,
description: string,
defaultValue: any
}>
}
FAQ
Nothing shows up, this is broken!
The way that the packages implement docgen for react means that there are some limitations on how you can import things and write you components.
- Must use default export + named export: The docgen will not be able to pick up a name for the default export so you must also use a named export
import * as React from "react";
interface ColorButtonProps {
color: "blue" | "green";
}
export const ColorButton: React.SFC<ColorButtonProps> = props => (
<button {...props}>
);
export default ColorButton;
- Imports Matter (TypeScript only): The way you import react and use it's types must conform to a few different formate
import * as React from 'react';
export const Button: React.FC<ButtonProps> = () => {};
import React, { FC } from 'react';
export const Button: FC<ButtonProps> = () => {};
- Usage within the story matter: This addon determines what components to display props for by finding all components used in the JSX returned by the story. So if you want prop-types to be displayed for a component, you must return that component in the story function.
import React from 'react';
import { Button } from './Button';
export default {
title: 'Button'
};
const MyFancyExample = () => {
const [count, setCount] = React.useState(0);
return (
<Button
primary={boolean('primary', false)}
onClick={() => setCount(count + 1)}
>
"hello: " {count}
</Button>
);
};
export const BaseStory = () => <MyFancyExample />;
export const BaseStory = () => {
const [count, setCount] = React.useState(0);
return (
<Button
primary={boolean('primary', false)}
onClick={() => setCount(count + 1)}
>
"hello: " {count}
</Button>
);
};
Why are default props so hard to get right? (TypeScript only)
The react
types are magical and you're probably doing too much. Using React.FC
is the quickest way to ramp up the complexity of your components. Once you use that you lose the defaultProps
experience.
Using React.FC
:
interface CardProps {
size?: 'small' | 'large';
}
const Card: React.FC<CardProps> = ({ size }) => <div>{size}</div>;
Card.defaultProps = {
size: 'small'
};
const Usage = () => <Card />;
Without React.FC:
interface CardProps {
size: 'small' | 'large';
}
const Card = ({ size }: CardProps) => <div>{size}</div>;
Card.defaultProps = {
size: 'small'
};
const Usage = () => <Card />;
My components extends from HTML elements and there are way too many props in the panel! How do I get rid of some?
You can add a filter to react-docgen-typescript-plugin
that will omit anything that comes from @types/react
.
.storybook/main.js
{
typescript: {
check: false,
reactDocgen: 'react-docgen-typescript',
reactDocgenTypescriptOptions: {
propFilter: (prop) => {
if (prop.name === 'children') {
return true;
}
if (prop.parent) {
return (
!/@types\/react/.test(prop.parent.fileName) &&
!/@emotion/.test(prop.parent.fileName)
);
}
return true;
},
},
},
}
Inspiration
Code heavily inspired by (copied from):