storybook-dark-mode
一个 Storybook 插件,允许用户在暗模式和亮模式之间切换。
安装
安装以下 npm 模块
npm i --save-dev storybook-dark-mode
或使用 yarn
yarn add -D storybook-dark-mode
然后,将以下内容添加到 .storybook/main.js
中
module.exports = {
addons: ['storybook-dark-mode']
};
从早期版本升级
在 .storybook/main.js
中更改
module.exports = {
- addons: ['storybook-dark-mode/register']
+ addons: ['storybook-dark-mode']
};
配置
通过将以下内容添加到 .storybook/preview.js
文件中,配置暗模式和亮模式
import { themes } from '@storybook/theming';
export const parameters = {
darkMode: {
// Override the default dark theme
dark: { ...themes.dark, appBg: 'black' },
// Override the default light theme
light: { ...themes.normal, appBg: 'red' }
}
};
默认主题
初始颜色方案的优先级顺序
- 如果用户之前设置了颜色主题,则使用该主题
- 在你的 Storybook 中为
current
参数配置的值 - 操作系统颜色方案偏好
设置初始颜色方案后,后续重新加载将使用此值。 要清除缓存的颜色方案,需要在 Chrome 控制台中执行 localStorage.clear()
。
export const parameters = {
darkMode: {
// Set the initial theme
current: 'light'
}
};
暗/亮类
此插件将为管理器应用暗类名和亮类名。 这使你可以轻松地为 Storybook UI 编写支持暗模式的主题覆盖。
可以使用 darkClass
和 lightClass
参数来覆盖在明暗模式之间切换时应用的类名。
export const parameters = {
darkMode: {
darkClass: 'lights-out',
lightClass: 'lights-on'
}
};
还可以传递数组来应用多个类。
export const parameters = {
darkMode: {
darkClass: ['lights-out', 'foo'],
lightClass: ['lights-on', 'bar']
}
};
预览类目标
此插件将把暗/亮类应用到预览 iframe 的 <body>
元素。 这可以通过 classTarget
参数进行配置。 该值将传递到 iframe 内部的 querySelector()
。
如果 <body>
是根据父级的类进行样式设置,则这很有用,在这种情况下可以将其设置为 html
。
export const parameters = {
darkMode: {
classTarget: 'html'
}
};
故事集成
预览类名
如果打开 stylePreview
选项,此插件将把 darkClass
和 lightClass
类应用到预览 iframe。
export const parameters = {
darkMode: {
stylePreview: true
}
};
React
如果你的组件使用自定义主题提供者,可以通过使用提供的钩子来集成它。
import { useDarkMode } from 'storybook-dark-mode';
import { addDecorator } from '@storybook/react';
// your theme provider
import ThemeContext from './theme';
// create a component that uses the dark mode hook
function ThemeWrapper(props) {
// render your custom theme provider
return (
<ThemeContext.Provider value={useDarkMode() ? darkTheme : defaultTheme}>
{props.children}
</ThemeContext.Provider>
);
}
export const decorators = [renderStory => <ThemeWrapper>{renderStory()}</ThemeWrapper>)];
主题旋钮
如果想要让 UI 的暗模式与组件的暗模式分开,请实现此全局装饰器
import { themes } from '@storybook/theming';
// Add a global decorator that will render a dark background when the
// "Color Scheme" knob is set to dark
const knobDecorator = storyFn => {
// A knob for color scheme added to every story
const colorScheme = select('Color Scheme', ['light', 'dark'], 'light');
// Hook your theme provider with some knobs
return React.createElement(ThemeProvider, {
// A knob for theme added to every story
theme: select('Theme', Object.keys(themes), 'default'),
colorScheme,
children: [
React.createElement('style', {
dangerouslySetInnerHTML: {
__html: `html { ${
colorScheme === 'dark' ? 'background-color: rgb(35,35,35);' : ''
} }`
}
}),
storyFn()
]
});
};
export const decorators = [knobDecorator];
事件
还可以通过插件通道监听 DARK_MODE
事件。
import { addons } from '@storybook/preview-api';
import { addDecorator } from '@storybook/react';
import { DARK_MODE_EVENT_NAME } from 'storybook-dark-mode';
// your theme provider
import ThemeContext from './theme';
// get channel to listen to event emitter
const channel = addons.getChannel();
// create a component that listens for the DARK_MODE event
function ThemeWrapper(props) {
// this example uses hook but you can also use class component as well
const [isDark, setDark] = useState(false);
useEffect(() => {
// listen to DARK_MODE event
channel.on(DARK_MODE_EVENT_NAME, setDark);
return () => channel.off(DARK_MODE_EVENT_NAME, setDark);
}, [channel, setDark]);
// render your custom theme provider
return (
<ThemeContext.Provider value={isDark ? darkTheme : defaultTheme}>
{props.children}
</ThemeContext.Provider>
);
}
export const decorators = [renderStory => <ThemeWrapper>{renderStory()}</ThemeWrapper>)];
由于在文档模式下,Storybook 不会显示其工具栏,因此如果想要在文档模式下控制该选项,可以通过编辑 .storybook/preview.js
来触发插件通道中的 UPDATE_DARK_MODE
事件。
import React from 'react';
import { addons } from '@storybook/preview-api';
import { DocsContainer } from '@storybook/addon-docs';
import { themes } from '@storybook/theming';
import {
DARK_MODE_EVENT_NAME,
UPDATE_DARK_MODE_EVENT_NAME
} from 'storybook-dark-mode';
const channel = addons.getChannel();
export const parameters = {
darkMode: {
current: 'light',
dark: { ...themes.dark },
light: { ...themes.light }
},
docs: {
container: props => {
const [isDark, setDark] = React.useState();
const onChangeHandler = () => {
channel.emit(UPDATE_DARK_MODE_EVENT_NAME);
};
React.useEffect(() => {
channel.on(DARK_MODE_EVENT_NAME, setDark);
return () => channel.removeListener(DARK_MODE_EVENT_NAME, setDark);
}, [channel, setDark]);
return (
<div>
<input type="checkbox" onChange={onChangeHandler} />
<DocsContainer {...props} />
</div>
);
}
}
};
贡献者 ✨
感谢这些很棒的人(表情符号键)
这个项目遵循 all-contributors 规范。 欢迎任何形式的贡献!