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'
}
};
明/暗類別
此外掛程式會將 dark 和 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']
}
};
預覽類別目標
此外掛程式會將 dark/light 類別套用至預覽 iframe 的 <body>
元素。可以使用 classTarget
參數設定。該值將傳遞至 iframe 內的 querySelector()
。
如果 <body>
是根據父類別設定樣式的,則此方法很有用,在這種情況下,它可以設定為 html
。
export const parameters = {
darkMode: {
classTarget: 'html'
}
};
故事整合
預覽類別名稱
如果您開啟 stylePreview
選項,此外掛程式會將 darkClass
和 lightClass
類別套用至預覽 iframe。
export const parameters = {
darkMode: {
stylePreview: true
}
};
React
如果您的元件使用自訂主題供應器,您可以使用提供的 hook 來整合它。
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 不會顯示其工具列,如果您想要在文件模式下控制該選項,也可以透過擴充套件通道觸發 UPDATE_DARK_MODE
事件,方法是編輯您的 .storybook/preview.js
。
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 規範。歡迎任何形式的貢獻!