Storybook 主题插件
主要灵感来自 @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 类color
(string
):主题选择器中标签的颜色default
[已废弃] (boolean
- 可选):是否为默认选中的主题?
themes
参数也接受一个包含以下属性的对象:
default
(string
- 可选):默认选中的主题名称list
(Theme[]
- 必填):主题列表clearable
(boolean
- 可选 - 默认值为true
):用户是否可以清除选中的主题?disable
(boolean
- 可选):为一个故事禁用该插件Decorator
(Component
- 可选):用作装饰器组件的组件(见下文获取更多信息)onChange
((themeName: Theme) => void
- 可选):当主题更改时将执行的回调函数target
(string
- 可选):使用document.querySelector()
选择的目标元素,类将应用于此元素。默认为body
;如果类应应用于documentElement
,则默认为root
。
配置
全局
您可以在 Storybook 的 preview.js
文件中全局配置主题
export const parameters = {
themes: {
default: 'twitter',
list: [
{ name: 'twitter', class: 'theme-twt', color: '#00aced' },
{ name: 'facebook', class: 'theme-fb', color: '#3b5998' }
],
},
};
为了向后兼容,default
(boolean
) 也可以直接设置在 Theme
对象上。这已废弃,因为改变默认主题需要重新定义所有的 Theme
对象,这比较困难。
// deprecated
export const parameters = {
themes: [
{ name: 'twitter', class: 'theme-twt', color: '#00aced', default: true },
{ name: 'facebook', class: 'theme-fb', color: '#3b5998' }
],
};
更多信息请参见 Storybook 文档。
在 Story 中 (Component Story Format)
或者在您的故事文件中这样配置主题
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' },
],
},
},
};
在 Story 中 (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)' },
],
},
});
覆盖单个属性
您也可以只覆盖主题参数中的单个键,例如为一个故事设置不同的默认值
export default {
title: 'CSF|Button',
component: Button,
};
export const withText = () => <Button onClick={action('clicked')}>Hello Button</Button>;
withText.story = {
parameters: {
themes: {
default: 'facebook',
},
},
};
使用装饰器
默认情况下,类将添加到 body
元素或使用 target
配置的元素上。
但在这种情况下,您的主题对其他插件(如 @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' },
],
},
};
在 Story 中 (Component Story Format)
或者在您的故事文件中(适用于该文件中的所有故事)
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' },
],
},
},
};
在 Story 中 (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 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
不使用装饰器 | + | + | + | + | + | + | + | + | + | + | + | |
使用装饰器 | + | + | + | + |