@storybook/addon-themes
要开始,您需要安装 @storybook/addon-themes
。
运行以下脚本安装并注册插件
npx storybook@latest add @storybook/addon-themes
在幕后,这将运行 npx @storybook/auto-config themes
,它应该读取您的项目并尝试使用正确的装饰器配置您的 Storybook。如果直接运行该命令无法解决您的问题,请在 @storybook/auto-config 存储库中提交错误,以便我们进一步改进它。要手动添加此插件,请安装它,然后将其添加到 .storybook/main.ts
中的 addons 数组中。
Material UI 依赖于两种字体才能按预期呈现,Google 的 Roboto
和 Material Icons
。虽然您可以直接从 Google Fonts CDN 加载这些字体,但使用 Storybook 打包字体可以提高性能。
要开始,请将字体安装为依赖项。
yarn add @fontsource/roboto @fontsource/material-icons
然后将 CSS 文件导入到 .storybook/preview.js
中,这是 Storybook 的入口点。
// .storybook/preview.js
import '@fontsource/roboto/300.css';
import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css';
import '@fontsource/material-icons';
在 .storybook/preview.js
中,导入 <CssBaseline />
、<ThemeProvider />
和您的主题,然后使用 withThemeFromJSXProvider
装饰器将它们应用于您的故事,方法是将其添加到 decorators
数组中。
// .storybook/preview.js
import { CssBaseline, ThemeProvider } from '@mui/material';
import { withThemeFromJSXProvider } from '@storybook/addon-themes';
import { lightTheme, darkTheme } from '../src/themes.js';
/* snipped for brevity */
export const decorators = [
withThemeFromJSXProvider({
themes: {
light: lightTheme,
dark: darkTheme,
},
defaultTheme: 'light',
Provider: ThemeProvider,
GlobalStyles: CssBaseline,
}),
];
当您提供多个主题时,Storybook UI 中将出现一个工具栏菜单,用于为您的故事选择所需的主题。
Storybook 控件为您提供了图形化控件来操作组件的 props。它们对于查找组件的边缘情况和在浏览器中进行原型设计非常方便。
通常,您需要手动配置控件。但是,如果您使用的是 Typescript,则可以重用 Material UI 的组件 prop 类型来自动生成故事控件。作为奖励,这还将自动填充文档选项卡中的 prop 表格。
让我们以以下 Button 组件为例。
// button.component.tsx
import React from 'react';
import { Button as MuiButton } from '@mui/material';
export interface ButtonProps {
label: string;
}
export const Button = ({ label, ...rest }: ButtonProps) => <MuiButton {...rest}>{label}</MuiButton>;
在这里,我使用 label prop 作为 MuiButton
的子元素,并通过它传递所有其他 props。但是,当我们将它渲染到 Storybook 中时,我们的控件面板只允许我们更改我们自己声明的 label prop。
这是因为 Storybook 仅将显式声明在组件的 prop 类型或 Story Args 中的 props 添加到控件表中。让我们更新 Storybook 的 Docgen 配置,也将 Material UI 的 Button props 引入控件表中。
// .storybook/main.ts
module.exports = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: ['@storybook/addon-essentials', '@storybook/addon-styling'],
framework: '@storybook/your-framework',
typescript: {
reactDocgen: 'react-docgen-typescript',
reactDocgenTypescriptOptions: {
// Speeds up Storybook build time
compilerOptions: {
allowSyntheticDefaultImports: false,
esModuleInterop: false,
},
// Makes union prop types like variant and size appear as select controls
shouldExtractLiteralValuesFromEnum: true,
// Makes string and boolean types that can be undefined appear as inputs and switches
shouldRemoveUndefinedFromOptional: true,
// Filter out third-party props from node_modules except @mui packages
propFilter: (prop) =>
prop.parent
? !/node_modules\/(?!@mui)/.test(prop.parent.fileName)
: true,
},
},
};
我们还希望更新 .storybook/preview.js
中的参数,以显示控件表的描述和默认列。
// .storybook/preview.js
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
expanded: true, // Adds the description and default columns
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
};
最后,更新 ButtonProps
类型以从 Material UI 的 Button props 扩展,并将所有这些 props 添加到控件中。
// button.component.tsx
import React from 'react';
import {
Button as MuiButton,
ButtonProps as MuiButtonProps,
} from '@mui/material';
export interface ButtonProps extends MuiButtonProps {
label: string;
}
export const Button = ({ label, ...rest }: ButtonProps) => (
<MuiButton {...rest}>{label}</MuiButton>
);
重新启动 Storybook 服务器以使这些配置更改生效。您现在应该看到 Button 也有所有 MuiButton
的 props 的控件了。
我们的按钮现在有 **27 个 props**,对于您的用例来说可能有点多。要控制哪些 props 可见,我们可以使用 TypeScript 的 Pick<type, keys>
和 Omit<type, keys>
实用程序。
// button.component.tsx
import React from 'react';
import {
Button as MuiButton,
ButtonProps as MuiButtonProps,
} from '@mui/material';
// Only include variant, size, and color
type ButtonBaseProps = Pick<MuiButtonProps, 'variant' | 'size' | 'color'>;
// Use all except disableRipple
// type ButtonBaseProps = Omit<MuiButtonProps, "disableRipple">;
export interface ButtonProps extends ButtonBaseProps {
label: string;
}
export const Button = ({ label, ...rest }: ButtonProps) => (
<MuiButton {...rest}>{label}</MuiButton>
);
现在,我们的 Button 将只从 MuiButton
中获取 variant、size 和 color props。
📣 感谢 Eric Mudrak 的精彩文章 Storybook with React & TypeScript,它启发了这条提示。