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' }
],
},
};
为了向后兼容,也可以直接在 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)' },
],
},
});
覆盖单个属性
你也可以只覆盖主题参数中的单个键,例如,为单个故事设置不同的默认值
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' },
],
},
};
在故事中(组件故事格式)
或者在你的故事文件中(对于该文件中的所有故事)
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 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
无需装饰器使用 | + | + | + | + | + | + | + | + | + | + | + | |
与装饰器一起使用 | + | + | + | + |