@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 的入口點 .storybook/preview.js
。
// .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 控制項為您提供圖形控制項來操作元件的屬性。它們對於尋找元件的邊緣案例和在瀏覽器中建立原型非常方便。
通常,您必須手動設定控制項。但是,如果您使用 Typescript,您可以重複使用 Material UI 的元件屬性類型來自動產生故事控制項。作為額外的好處,這也會自動填寫文件標籤中的屬性表。
讓我們以以下的 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 屬性作為 MuiButton
的子元素,並傳遞所有其他屬性。但是,當我們將其渲染到 Storybook 中時,我們的控制項面板只允許我們更改自己宣告的 label 屬性。
這是因為 Storybook 只會將在元件的屬性類型或故事參數中明確宣告的屬性新增到控制項表中。讓我們更新 Storybook 的 Docgen 設定,以便將 Material UI 的 Button 屬性也加入控制項表中。
// .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 屬性,將所有這些屬性新增到控制項中。
// 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
的屬性的控制項。
我們的按鈕現在有 27 個屬性,這可能對您的使用案例來說有點太多了。為了控制哪些屬性是可見的,我們可以使用 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 屬性。
📣 特別感謝 Eric Mudrak 精彩的 Storybook with React & TypeScript 文章,該文章啟發了此技巧。