storybook-addon-react-docgen
一个用于显示 React Docgen 信息的 Storybook 插件。此插件是“info”插件中 prop 表功能的直接替代品。它不是与组件一起渲染,而是在插件面板中渲染。也支持 Typescript!
存在其他类似的插件,但它们与 info
插件的工作方式不同。这导致了复杂的配置更改。此插件旨在实现无痛切换。
安装
yarn add storybook-addon-react-docgen
React Docgen 集成
React Docgen 已包含在 @storybook/addon-docs
包中。如果您正在使用 @storybook/addon-docs
,则无需设置 docgen,可以跳过后续步骤。
Typescript DocGen
要在 Typescript 项目中使用此插件,您需要安装 react-docgen-typescript-plugin 并配置 webpack 来使用它。
Javascript DocGen
要在 Javascript 项目中使用此插件,您需要安装 babel-plugin-react-docgen
使用方法
在您的 main.js
的 "addons" 中添加它
module.exports = {
stories: ['../stories/**/*.stories.js'],
addons: ['@storybook/addon-docs', 'storybook-addon-react-docgen']
};
然后将 withPropsTable
decorator 添加到您的 preview.js
中。您可以在此处传递全局选项(如果需要)
const { addDecorator } = require('@storybook/react');
const { withPropsTable } = require('storybook-addon-react-docgen');
addDecorator(withPropsTable(options));
// or
addDecorator(withPropsTable);
您可以使用 props
参数为单个故事配置选项
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 // the actual component
]
}
}
或为整个故事配置选项
import { storiesOf } from '@storybook/react';
import Other from './Other';
import Component from './Component';
export default {
title: 'Components/Button',
parameters: {
props: {
propTablesExclude: [
Other // the actual component
]
}
}
};
export const WithSomeEmoji = () => (
<Component>
<Other />
</Component>
);
配置
{
/**
* Components used in story
* Displays Prop Tables with these components
* @default []
*/
propTables: Array<React.ComponentType>,
/**
* Define custom sorting order for the components specifying component names in the desired order.
* Example:
* propTablesSortOrder: ["MyComponent", "FooComponent", "AnotherComponent"]
* @default []
*/
propTablesSortOrder: string,
/**
* Only include prop tables for these components.
* Accepts an array of component classes or functions
* @default null
*/
propTablesInclude: Array<React.ComponentType | string>,
/**
* Exclude Components from being shown in Prop Tables section
* Accepts an array of component classes or functions
* @default []
*/
propTablesExclude: Array<React.ComponentType | string>,
/**
* Overrides styles of addon. The object should follow this shape:
* https://github.com/storybooks/storybook/blob/master/addons/info/src/components/Story.js#L19.
* This prop can also accept a function which has the default stylesheet passed as an argument
*/
styles: Object | Function,
/**
* Max props to display per line in source code
* @default 3
*/
maxPropsIntoLine: number,
/**
* Displays the first 10 characters of the prop name
* @default 3
*/
maxPropObjectKeys: number,
/**
* Displays the first 10 items in the default prop array
* @default 3
*/
maxPropArrayLength: number,
/**
* Displays the first 100 characters in the default prop string
* @default 50
*/
maxPropStringLength: number,
/**
* Override the component used to render the props table
* @default PropTable
*/
TableComponent: React.ComponentType,
/**
* Will exclude any respective properties whose name is included in array.
* Can also specify absolute propType to exclude (see example below)
* Examples:
* excludedPropTypes: ["message"] // propType to exclude
* excludedPropTypes: ["MyComponent.message"] // absolute propType
*
* @default []
*/
excludedPropTypes: Array<string>,
}
渲染自定义表格
TableComponent 选项允许您定义 prop 表应如何渲染。您的组件将使用以下 prop 进行渲染。
{
propDefinitions: Array<{
property: string, // The name of the prop
propType: Object | string, // The prop type. TODO: info about what this object is...
required: boolean, // True if the prop is required
description: string, // The description of the prop
defaultValue: any // The default value of the prop
}>
}
常见问题
什么都没显示,是不是坏了?
这些包为 React 实现 docgen 的方式意味着您在导入和编写组件时存在一些限制。
- 必须使用默认导出 + 命名导出:docgen 无法为默认导出获取名称,因此您还必须使用命名导出
import * as React from "react";
interface ColorButtonProps {
/** Buttons background color */
color: "blue" | "green";
}
/** A button with a configurable background color. */
export const ColorButton: React.SFC<ColorButtonProps> = props => (
<button {...props}>
);
export default ColorButton;
- 导入很重要(仅限 Typescript):您导入 React 和使用其类型的方式必须符合一些不同的格式
// To get React.FC to work
import * as React from 'react';
export const Button: React.FC<ButtonProps> = () => {};
// Without "* as" you can only use like:
import React, { FC } from 'react';
export const Button: FC<ButtonProps> = () => {};
- 故事内部的使用也很重要:此插件通过查找故事返回的 JSX 中使用的所有组件来确定要显示哪些组件的 prop。因此,如果您希望为某个组件显示 prop-types,您必须在故事函数中返回该组件。
import React from 'react';
import { Button } from './Button';
export default {
title: 'Button'
};
/** WILL NOT WORK */
// Since the usage of the component is not in the JSX
// returned by the story function no props are displayed
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 />;
/** WILL WORK */
// The JSX returned by the story uses Button, so we will
// get the props types for button.
export const BaseStory = () => {
const [count, setCount] = React.useState(0);
return (
<Button
primary={boolean('primary', false)}
onClick={() => setCount(count + 1)}
>
"hello: " {count}
</Button>
);
};
为什么默认 prop 如此难以正确处理?(仅限 Typescript)
react
类型很神奇,您可能做了太多事情。使用 React.FC
是快速增加组件复杂性的最快方式。一旦使用它,您就会失去 defaultProps
的体验。
使用 React.FC
interface CardProps {
size?: 'small' | 'large';
}
// The type of size will be "string | undefined"
// You will either have to repeat your default value
// Or write a helper type the figures out what is defaulted
const Card: React.FC<CardProps> = ({ size }) => <div>{size}</div>;
Card.defaultProps = {
size: 'small'
};
// Size is optional to the user
const Usage = () => <Card />;
不使用 React.FC
interface CardProps {
// Key part right here is to make the defaulted prop not optional
// this way in your function it won't be undefined
size: 'small' | 'large';
}
// The type of size will be "string"
const Card = ({ size }: CardProps) => <div>{size}</div>;
// Typescript can use this defaultProps to determine what is optional
// for the user of your component.
Card.defaultProps = {
size: 'small'
};
// Size is optional to the user
const Usage = () => <Card />;
我的组件继承自 HTML 元素,面板中的 prop 太多了!如何去除一些?
您可以向 react-docgen-typescript-plugin
添加过滤器,这将忽略所有来自 @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;
},
},
},
}
灵感来源
代码主要受到以下项目的启发(并复制了部分代码)