ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Storybook 如何用 Decorator 模拟 Context Provider 并为不同 Story 配置不同 Provider 值

Storybook 如何用 Decorator 模拟 Context Provider 并为不同 Story 配置不同 Provider 值 Storybook 如何用 Decorator 模拟 Context Provider 并为不同 Story 配置不同 Provider 值【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook在 Storybook 中很多组件不是只靠 props 渲染的主题组件从 ThemeProvider 读取当前主题Redux 类应用通过 context 向组件提供应用数据。这类组件在 Storybook 里单独渲染时会因为缺少 Provider 而无法工作。本文的任务是用全局 Decorator 在 Story 外面包一层 mock 的 Provider并通过 story 的parameters让同一个 Provider 对不同 Story 提供不同的值例如 light / dark 两套主题避免给每个 Story 各写一份 decorator。该写法只适用于使用 JSX 的 renderer文档以 React 或 Solid 为例Preact 也属于同一适用范围。下文代码以 React TypeScript 为主路径示例来自文档的 CSF 3 写法。准备条件项目已初始化 Storybook存在.storybook/preview.ts|tsx预览配置文件该文件用于统一配置所有 stories 的渲染行为见 configure 文档。预览文件中要写 JSX所以文件扩展名需要是.tsx或.jsx。文档明确提醒如果项目设置不允许你可能需要把 preview 文件改成.tsx/.jsx扩展名。代码中storybook/your-framework是文档示例的占位写法需要替换为你实际使用的框架包文档给出的例子有react-vite、nextjs、nextjs-vite等。示例中的ThemeProvider来自styled-components如果你使用自己的 Provider 组件换成自己的即可写法不变。第一步在全局 Decorator 中包一层 Provider在.storybook/preview.tsx的decorators中定义一个 decorator用它包住所有 storyimport React from react; // Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc. import type { Preview } from storybook/your-framework; import { ThemeProvider } from styled-components; const preview: Preview { decorators: [ (Story) ( ThemeProvider themedefault {/* Decorators in Storybook also accept a function. Replace Story/ with Story() to enable it */} Story / /ThemeProvider ), ], }; export default preview;定义在 preview 文件里的 decorator 对所有stories 生效全局 decorator。此时所有 Story 都用同一个 Provider 值渲染——如果组件依赖 ThemeProvider 才能取到主题到这里它已经能正常渲染了。第二步让 Decorator 从 parameters 读取值如果只想给个别 Story 换一套 Provider 值为每个 Story 单独定义 decorator 也可以但文档指出当你要给每个组件都创建 light 和 dark 两套主题的 stories 时这种做法会很快变得难以维护。文档推荐的方式是利用 decorator 函数的第二个参数——story context——访问当前 story 的parameters从而只定义一次 Provider按 story 调整它的值。把上面的 decorator 改成下面这样从parameters.theme读取要提供的主题import React from react; // Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc. import type { Preview } from storybook/your-framework; import { ThemeProvider } from styled-components; // themes { light, dark } import * as themes from ../src/themes; const preview: Preview { decorators: [ // Defining the decorator in the preview file applies it to all stories (Story, { parameters }) { // Make it configurable by reading the theme value from parameters const { theme light } parameters; return ( ThemeProvider theme{themes[theme]} Story / /ThemeProvider ); }, ], }; export default preview;执行这段代码前需要明确两处替换import * as themes from ../src/themes是文档示例假设的路径注释themes { light, dark }表示该模块按主题名导出主题对象。实际项目中请替换为你自己导出主题对象的模块路径导出的键名要与后面 story 里写的parameters.theme值对应示例中是light、dark。const { theme light } parameters中的light是文档示例的默认值story 没有配置parameters.theme时Provider 提供themes.light。story context 的完整结构args、argTypes、globals、hooks、parameters、viewMode见 Decorators 文档的 Context for mocking 一节。其中parameters是 story 的静态元数据这里正好用来控制 Provider 值。第三步在不同 Story 上配置不同的 Provider 值现在只需在 story 上声明一个theme参数即可切换 Provider 提供的主题。文档示例Button.stories.ts// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc. import type { Meta, StoryObj } from storybook/your-framework; import { Button } from ./Button; const meta { component: Button, } satisfies Metatypeof Button; export default meta; type Story StoryObjtypeof meta; // Wrapped in light theme export const Basic: Story {}; // Wrapped in dark theme export const Dark: Story { parameters: { theme: dark, }, };对应关系很直接Basic没有声明parameters.theme走 decorator 里的默认值文档标注它是 Wrapped in light themeDark声明了parameters.theme: darkdecorator 读取到dark后提供themes.dark文档标注它是 Wrapped in dark theme。parameters的继承规则见 Parameters 文档story 参数覆盖组件参数、组件参数覆盖全局参数参数是合并的只有同名键被覆盖其他键保留。这意味着你只覆盖theme一个键时全局或组件层配置的其他参数不受影响。验证方式在 Storybook 中打开这两个 story 做判断Basic应在 light 主题下渲染Dark应在 dark 主题下渲染——即各自被对应主题的 Provider 包住这正是文档示例给出的两条注释所描述的结果。如果组件读不到 Provider 数据报错或主题不生效先检查两点decorator 是否定义在 preview 文件或 story / 组件级并正确包裹了Story /story 上parameters.theme的取值是否能在themes模块中找到对应导出的键。另外注意 decorator 的执行顺序全局 decorators 按定义顺序执行然后是组件级 decorators最后是 story 级 decorators。如果你的项目里还有其他全局 decoratorProvider 包装会按上述顺序与其他包装层叠加见 Decorators 文档。限制与可选分支按 Story 单独定义 decorator也可以不给全局 decorator而是在某个 story 上用decorators键定义包装如 button-story-decorator 示例所示。这条路可行但文档明确说它在每个组件都要 light/dark 两套 stories的场景下会迅速变得繁琐因此本文以 parameters 方案为主路径。用 globals 代替 parameters 切换值story context 中的globals是 Storybook 全局值文档指出可以通过 toolbars 特性在 Storybook UI 里直接改这些值。把 decorator 中的parameters.theme换成globals.theme后即可通过工具栏在运行时切换主题而不需要为每个值各写一个 story。文档中 Angular、Vue、Svelte、Web Components 的 provider 示例均采用globals.theme的读法见 decorators 文档。这套Decorator 包 Provider 按 story 调值的手法不限于主题文档原文说它同样适用于提供用户角色user role、mock 数据等任意值。参考Mocking providers本场景的主文档含 Configuring the mock provider 一节Decoratorsstory context 结构、各级 decorator 定义与继承顺序Parameters参数层级与合并规则【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表