Storybook Addon Themes
很大程度上受到 @storybook/addon-backgrounds 的启发。
这个 Storybook 主题装饰器可以用来在 Storybook 中的预览中添加自定义的 HTML 类或类。
兼容性
此版本与 storybook 版本 6.0.x
兼容。
安装
npm i -D storybook-addon-themes
入门
然后通过将它添加到 storybook main.js
文件(位于 Storybook 配置目录中)来激活附加组件
module.exports = {
addons: [
// Maybe other addons here...
'storybook-addon-themes'
// Or here...
],
};
有关更多信息,请参阅 storybook 文档。
参数
themes
参数接受一个 Theme
对象数组。
每个 Theme
都是一个具有以下属性的对象
name
(string
):主题名称class
(string | string[]
- 可选):与主题关联的 HTML 类(es)color
(string
):主题选择器中徽章的颜色default
[已弃用] (boolean
- 可选):主题是否默认选中?
themes
参数还接受一个具有以下属性的对象
default
(string
- 可选):默认选中的主题名称list
(Theme[]
- 必填):主题列表clearable
(boolean
- 可选 - 默认值为true
):用户可以清除选定的主题吗?disable
(boolean
- 可选):为故事禁用附加组件Decorator
(Component
- 可选):用作装饰器组件的组件 (请参阅以下内容 以获取更多信息)onChange
((themeName: Theme) => void
- 可选):当主题更改时将执行的回调
配置
全局
你可以在 storybook preview.js
文件中全局配置主题
export const parameters = {
themes: {
default: 'twitter',
list: [
{ name: 'twitter', class: 'theme-twt', color: '#00aced' },
{ name: 'facebook', class: 'theme-fb', color: '#3b5998' }
],
},
};
为了向后兼容,也可以直接在 Theme
对象上设置 default
(boolean
)。这已被弃用,因为由于需要重新定义所有 Theme
对象,因此难以更改默认主题。
// deprecated
export const parameters = {
themes: [
{ name: 'twitter', class: 'theme-twt', color: '#00aced', default: true },
{ name: 'facebook', class: 'theme-fb', color: '#3b5998' }
],
};
有关更多信息,请参阅 storybook 文档。
在故事中(组件故事格式)
或者像这样在你的故事文件中配置主题
export default {
title: 'CSF|Button',
component: Button,
parameters: {
themes: {
default: 'twitter',
list: [
{ name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced' },
{ name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' },
],
},
},
};
如果你只想为特定故事激活附加组件或覆盖主题,你可以编写
export default {
title: 'CSF|Button',
component: Button,
};
export const withText = () => <Button onClick={action('clicked')}>Hello Button</Button>;
withText.story = {
parameters: {
themes: {
default: 'twitter',
list: [
{ name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced' },
{ name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' },
],
},
},
};
在故事中(StoriesOf API)
或者使用旧的 StoriesOf API
import { storiesOf } from '@storybook/react'; // <- or your storybook framework
storiesOf('StoriesOf|Button', module)
.addParameters({
themes: {
default: 'twitter',
list: [
{ name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced' },
{ name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' },
],
},
})
.add('with text', () => <button>Click me</button>);
以及单个故事
import { storiesOf } from '@storybook/react';
storiesOf('StoriesOf|Button', module)
.add('with text', () => <button>Click me</button>, {
themes: {
list: [
{ name: 'red', class: 'theme-red', color: 'rgba(255, 0, 0)' },
],
},
});
覆盖单个属性
你还可以只覆盖 themes
参数中的单个键,例如,为单个故事设置不同的默认值
export default {
title: 'CSF|Button',
component: Button,
};
export const withText = () => <Button onClick={action('clicked')}>Hello Button</Button>;
withText.story = {
parameters: {
themes: {
default: 'facebook',
},
},
};
与装饰器一起使用
默认情况下,这些类将被添加到 body
元素中。
但在这种情况下,你的主题将不会被其他附加组件(如 @storybook/addon-storyshots)看到。
要解决此问题,你可以在你的故事中添加 withThemes
装饰器。
但是,并非所有框架都提供装饰器方法
查看 此处,了解支持的框架列表。
全局
在 preview.js
文件中全局设置装饰器
import { addDecorator } from '@storybook/react'; // <- or your storybook framework
import { withThemes } from 'storybook-addon-themes/react'; // <- or your storybook framework
addDecorator(withThemes);
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
themes: {
default: 'twitter',
list: [
{ name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced' },
{ name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' },
],
},
};
在故事中(组件故事格式)
或者在你的故事文件中(适用于该文件中的所有故事)
export default {
title: 'CSF|Button',
component: Button,
decorators: [ withThemes ],
parameters: {
themes: {
default: 'twitter',
list: [
{ name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced' },
{ name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' },
],
},
},
};
或者只针对特定故事
export const withText = () => <Button onClick={action('clicked')}>Hello Button</Button>;
withText.story = {
decorators: [ withThemes ],
parameters: {
themes: {
default: 'twitter',
list: [
{ name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced' },
{ name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' },
],
},
},
};
在故事中(StoriesOf API)
以及使用旧的 StoriesOf API 的方法
import { storiesOf } from '@storybook/react'; // <- or your storybook framework
import { withThemes } from 'storybook-addon-themes/react';
storiesOf('StoriesOf|Button', module)
.addDecorator(withThemes)
.add('with text', () => <button>Click me</button>);
自定义装饰器
常规
你可以提供一个将用作装饰器的组件,使用 theme
参数中的 Decorator
选项。
装饰器将获取以下属性
theme
:选定的主题,如果没有选择,则为undefined
。themes
:主题列表,如theme
参数的list
选项中提供的那样。themeClasses
:选定主题的格式化主题类(如果选定主题存在class
选项)。themeName
:选定主题的名称(如果没有选择,则等于none
)。
不要忘记使用 children
道具(React/HTML)或 <slot></slot>
元素(Vue/Svelte)渲染故事。
HTML 示例
要管理 HTML storybook 的响应能力,你的装饰器必须返回一个包含两个元素的数组
- 要显示在故事中的 HTML 元素
- 当主题更改时将被调用的更新回调。与装饰器一样,回调将接收相同的道具(没有
children
)。
使用 CSS 文件更改主题的自定义装饰器示例
function getOrCreate(id) {
const elementOnDom = document.getElementById(id);
if (elementOnDom) {
return elementOnDom;
}
const element = document.createElement('link');
element.setAttribute('id', id);
element.setAttribute('rel', 'stylesheet');
return element;
}
function Decorator(props) {
const { children } = props;
function setStyles({ theme, themeName }) {
const link = getOrCreate('theme-stylesheet');
if (!theme) {
link.parentNode && link.parentNode.removeChild(link);
} else {
link.href = themeName === 'facebook' ? 'Button-fb.css' : 'Button-twt.css';
children.appendChild(link);
}
}
setStyles(props);
return [children, setStyles];
}
React 示例
与上面相同的示例,用于 React
function Decorator(props) {
const { children, themeName } = props;
return (
<>
{children}
{themeName === 'twitter' && <link rel="stylesheet" href="twitter.css"/>}
{themeName === 'facebook' && <link rel="stylesheet" href="facebook.css"/>}
</>
);
};
框架支持表
React | React Native | Vue | Angular | Polymer | Mithril | HTML | Marko | Svelte | Riot | Ember | Preact | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
不使用装饰器 | + | + | + | + | + | + | + | + | + | + | + | |
与装饰器一起使用 | + | + | + | + |