我想将我的 MDX 文档包装在 themeProvider 中,当全局变量(globaltypes)的上下文发生变化时,它会更新。
我遵循了这个:
故事书 Github 和这个讨论
当我这样做时它确实起作用了:
docs: {
container: ({ children, context }) => {
const selectedTheme = themes["brandA"]["dark"];
return (
<DocsContainer context={context}>
<ThemeProvider theme={selectedTheme}>
<GlobalStyles />
{children}
</ThemeProvider>
</DocsContainer>
);
},
但是当我这样做时它不起作用:
const selectedTheme = themes[context.globals.brand][context.globals.mode];
即使它在装饰器中也是这样工作的。
这是我的整个文件:
import React, { useContext } from "react";
import ReactDOM from "react-dom";
window.React = React;
import { ThemeProvider } from "styled-components";
import GlobalStyles from "../src/styles/GlobalStyles.jsx";
import themes from "../src/styles/Themes.js";
import mytheme from "./myTheme.js";
import { DocsContainer } from "@storybook/blocks";
const customViewports = {
laptop: {
name: "Laptop",
styles: {
width: "1280px",
height: "100%",
},
},
desktop: {
name: "Desktop",
styles: {
width: "1440px",
height: "100%",
},
},
widescreen: {
name: "Widescreen",
styles: {
width: "1920px",
height: "100%",
},
},
};
export const themeProviderDecorator = (Story, context) => {
const currentTheme = themes[context.globals.brand][context.globals.mode];
return (
<ThemeProvider theme={currentTheme}>
<GlobalStyles />
<div
style={{
padding: "24px",
backgroundColor: "var(--background-color)",
}}
>
<Story />
</div>
</ThemeProvider>
);
};
/** @type { import('@storybook/react').Preview } */
const preview = {
//controlos da brand e mode
globalTypes: {
brand: {
description: "Select a brand",
defaultValue: "brandDefault",
toolbar: {
// icon: "paintbrush",
title: "🟣 Default Brand",
dynamicTitle: true,
items: [
{
value: "brandDefault",
title: "🟣 Default Brand" /* left: "🟣" */,
},
{ value: "brandA", title: "🟢 Brand A" /* left: "🟢" */ },
{ value: "brandB", title: "🔵 Brand B" /* left: "🔵" */ },
],
},
},
mode: {
description: "Select a mode",
defaultValue: "light",
toolbar: {
title: "🟣 Light",
dynamicTitle: true,
items: [
{ value: "light", title: " Light", icon: "sun" },
{ value: "dark", title: " Dark", icon: "moon" },
//pode ter mais modos além de apenas "light" e "dark", como por exemplo, "high contrast".
],
},
},
},
decorators: [themeProviderDecorator],
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
options: {
title: "React",
},
layout: "fullscreen", //para as stories não terem margins (Assim o decorator com o backgroung color, não fica estranho)
viewport: {
viewports: customViewports,
},
docs: {
container: ({ children, context }) => {
// const selectedTheme = themes[context.globals.brand][context.globals.mode];
const selectedTheme = themes["brandA"]["dark"];
return (
<DocsContainer context={context}>
<ThemeProvider theme={selectedTheme}>
<GlobalStyles />
{children}
</ThemeProvider>
</DocsContainer>
);
},
canvas: {
sourceState: "shown",
},
previewSource: "shown",
theme: myTheme,
},
},
};
export default preview;
注意:对颜色感到抱歉,但这仅用于测试。
始终显示的错误是:
无法读取未定义的属性(读取“品牌”)
也尝试过这个:
const container = ({ children, context, globals }) => {
const { brand, mode } = globals;
const selectedTheme = themes[brand][mode]; // Access brand and mode
// ... (rest of the container function)
};
,这个:
import { useGlobals } from '@storybook/api';
const container = ({ children, context }) => {
const { brand, mode } = useGlobals();
const selectedTheme = themes[brand][mode]; // Access brand and mode
return (
<DocsContainer context={context}>
<ThemeProvider theme={selectedTheme}>
<GlobalStyles />
{children}
</ThemeProvider>
</DocsContainer>
);
};
,这个:
const container = (context, { children }) => {
const selectedTheme =
themes[context.globals.brand][context.globals.mode];
return (
<DocsContainer context={context}>
<ThemeProvider theme={selectedTheme}>
<GlobalStyles />
{children}
</ThemeProvider>
</DocsContainer>);
},
};
还有这个:
const container = (context, children) => {
const selectedTheme =
themes[context.globals.brand][context.globals.mode];
return (
<DocsContainer context={context}>
<ThemeProvider theme={selectedTheme}>
<GlobalStyles />
{children}
</ThemeProvider>
</DocsContainer>);
},
};
但似乎没有任何作用。
这很重要,因为它可以使多品牌 Storybook 的文档变得更好。
我还了解到装饰器和容器中上下文的读取方式不同。
在装饰器中我写道:
console.log(context);
console.log("decorator context: " + context);
console.log("decorator context.globals: " + context.globals);
console.log("decorator context.globals.brand: " + context.globals.brand);
console.log("decorator context.globals.mode: " + context.globals.mode);
console.log(currentTheme);
但是当我像这样在容器中写它时:
console.log(context);
console.log("container context: " + context); //this works
console.log("container context.globals: " + context.globals); //this works
console.log("container context.globals.brand: " + context.globals.brand); //this doesn't
console.log("container context.globals.mode: " + context.globals.mode); //this doesn't
如果我只尝试
console.log(context.globals);
它只在控制台中显示“未定义”。
所以,我不明白为什么我无法获取具有全局变量的上下文(就像在装饰器中一样),因为它正在获取另一种上下文,即 React 上下文,就像 Storybook Github 中所说的那样:
如果您想为 MDX 页面添加一些包装器,或者添加某种其他类型的 React 上下文,会发生什么?
如果没有
context={context}
中的
<DocsContainer context={context}>
,这将不起作用
那么...我如何使用两者并从全局调用我想要的一个来选择品牌和模式?
在探索容器中
console.log(context);
中的结果对象后(我花了一段时间......)。我找到方法了:
export const themeProviderMDX = ({ children, context }) => {
const currentTheme =
themes[context.store.globals.globals.brand][
context.store.globals.globals.mode
];
console.log(context);
return (
<DocsContainer context={context}>
<ThemeProvider theme={currentTheme}>
<GlobalStyles />
{children}
</ThemeProvider>
</DocsContainer>
);
};
[...]
parameters: {
docs: {
container: themeProviderMDX,
},
},
您可以在控制台中看到这一点:
希望这对某人有帮助:)