生产环境Storybook中Material-UI组件代码显示WithStyles而非组件名
我基于Material-UI (v4)组件用Storybook构建设计系统文档,执行生产构建命令:
$ npm run build-storybook $ npx http-server storybook-static -o
之后,代码块里显示的是WithStyles而非组件真实名称。比如预期显示:
<Button color="default" onClick={() => {}} size="medium" variant="contained" > Hello World </Button>
实际显示成了:
<WithStyles color="default" onClick={function noRefCheck(){}} size="medium" variant="contained" > Hello World </WithStyles>
在Storybook Discord咨询后得到回复:
Storybook uses react-element-to-jsx-string to generate the source blocks, and that library relies on element's displayNames. It looks like you're using MUI, which does not assign displayName in production environments.
但不确定具体解决方法,想问有没有办法让Storybook生产构建时正确生成Material-UI组件的代码块?
解决方法
1. 手动为MUI组件添加displayName
在导入MUI组件的位置,手动为组件设置displayName:
import { Button } from '@material-ui/core'; Button.displayName = 'Button';
如果是自己封装的MUI组件,在导出前设置:
const CustomButton = withStyles(styles)(Button); CustomButton.displayName = 'CustomButton'; export default CustomButton;
2. 让Storybook生产构建使用MUI开发版本
修改Storybook的Webpack配置,在生产构建时加载MUI未压缩的开发版本(保留displayName)。在.storybook/main.js中添加:
module.exports = { webpackFinal: async (config) => { config.resolve.alias = { ...config.resolve.alias, '@material-ui/core': '@material-ui/core/es', }; return config; }, };
3. 自定义react-element-to-jsx-string的转换规则
覆盖Storybook默认的代码生成逻辑,手动映射WithStyles包装的组件到真实名称。在.storybook/preview.js中添加:
import { addDecorator } from '@storybook/react'; import reactElementToJSXString from 'react-element-to-jsx-string'; import { Source } from '@storybook/addon-docs'; addDecorator((storyFn) => { const element = storyFn(); const customJSX = reactElementToJSXString(element, { displayName: (elem) => { // 针对WithStyles包装的组件,手动返回对应名称 if (elem.type.displayName?.startsWith('WithStyles')) { // 可根据组件特征判断,比如这里假设是Button return 'Button'; // 多组件场景可扩展判断逻辑,比如从组件props或内部结构区分 } return elem.type.displayName || elem.type.name || 'Component'; }, }); // 结合Source组件展示自定义生成的JSX代码 return <Source code={customJSX} />; });
4. 手动指定文档中的代码示例
如果使用@storybook/addon-docs,可以在Story中直接手动编写代码示例,不依赖自动生成:
import { Button } from '@material-ui/core'; import { Source } from '@storybook/addon-docs'; export default { title: 'Components/Button', component: Button, }; export const Default = () => <Button>Hello World</Button>; Default.parameters = { docs: { source: { code: `<Button color="default" onClick={() => {}} size="medium" variant="contained" > Hello World </Button>`, }, }, };
内容的提问来源于stack exchange,提问作者Nicholas Pappas

