且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何在 Storybook 6.0 中自定义深色和浅色主题

更新时间:2023-01-27 17:31:01

嗯 - 解决方案就在我眼前,但像往常一样,我没有充分阅读文档.

Well - the solution was staring me right in the eyes, but as usual I had not read the documentation enough.

我确实安装了 storybook-dark-theme,我的想法是完全集成 MUI,这样浅色主题将显示我的应用程序浅色主题,而深色主题将显示我的深色主题 - 组件也是如此.

I did have storybook-dark-theme installed, and my idea was to have MUI fully integrated so that the light theme will show my application light theme, and the dark theme will show my dark theme - and the components likewise.

通过这种方式,明暗主题切换器变得完全全局化.

This way the light/dark theme switcher becomes fully global.

不过,它确实比你写的要多一点.

It does however take a little more than what you wrote.

这就是我的目标

您可以从这里下载所有内容使用解决方案链接到我的 github

And you can download all of it from here Link to my github with the solution

当故事书第一次渲染时,它将使用默认主题,因此您会在使用踢球覆盖的明/暗主题之前看到一些闪烁.

When storybook renders first time it will use the default theme, and as such you will get some flickering before the light/dark themes you have been overriding with kicks in.

第一次点击将使其保持浅色主题,但现在它将使用您的浅色主题.

First click will make it remain light theme, but now it will use YOUR light theme.

解决这个问题的方法是在manager.ts中使用light主题进行初始化

The solution to this is to initialize with the light theme in manager.ts like so

import { addons } from '@storybook/addons'
import { theme as appTheme } from '../src/theme/theme'
import { createThemeFromMuiTheme } from './utils/create-theme-from-mui-theme'

addons.setConfig({
    theme: createThemeFromMuiTheme({
        theme: appTheme.light,
        options: {
            base: 'light',
            brandTitle: 'Storybook with MUI',
            brandUrl: 'https://www.github.com/IgorSzyporyn/storybook-with-mui'
        },
    })
})

然后在 preview.ts 中,您需要像这样设置明/暗覆盖

And then in preview.ts you need to then set the light/dark overrides like so

import React from 'react'
import { addDecorator } from '@storybook/react'
import { theme as appTheme } from '../src/theme/theme'
import { WithMuiTheme } from './components/WithMuiTheme'
import { createThemeFromMuiTheme } from './utils/create-theme-from-mui-theme'

addDecorator((story) => <WithMuiTheme>{story()}</WithMuiTheme>)

export const parameters = {
    exportedParameter: 'exportedParameter',
    darkMode: {
        current: 'light',
        light: createThemeFromMuiTheme({ theme: appTheme.light, asStorybookTheme: false }),
        dark: createThemeFromMuiTheme({ theme: appTheme.dark, asStorybookTheme: false })
    },
}

让你的装饰器包装器像这样./components/WithMuiThemeProps

And have your decorator wrapper made like this ./components/WithMuiThemeProps

import React from 'react'
import { MuiThemeProvider, CssBaseline, StylesProvider } from '@material-ui/core'
import { useThemeType } from '../hooks/UseThemeType'
import { theme } from '../../src/theme/theme'

type WithMuiThemeProps = {
    children: React.ReactNode
}

export const WithMuiTheme = ({ children }: WithMuiThemeProps) => {
    const themeType = useThemeType()

    return (
        <MuiThemeProvider theme={theme[themeType]}>
            <CssBaseline />
            <StylesProvider injectFirst>{children}</StylesProvider>
        </MuiThemeProvider>
    )
}

这是使更新发生的钩子./hooks/UseThemeType.ts

import React from 'react'
import addons from '@storybook/addons'

const channel = addons.getChannel()

export const useThemeType = () => {
    const [isDark, setDark] = React.useState(false)

    React.useEffect(() => {
        channel.on('DARK_MODE', setDark)
        return () => channel.off('DARK_MODE', setDark)
    }, [channel, setDark])

    const paletteType = isDark ? 'dark' : 'light'

    return paletteType
}

最后是 ./utils/create-theme-from-mui-theme.ts

import { create } from '@storybook/theming/create'
import { Theme } from '@material-ui/core'

type CreateThemFromMuiTheme = {
    theme: Theme
    options?: Object
    asStorybookTheme?: boolean
}

export const createThemeFromMuiTheme = ({
    theme,
    options = {},
    asStorybookTheme = true,
}: CreateThemFromMuiTheme) => {
    const themeValue = {
        colorPrimary: theme.palette.primary.main,
        colorSecondary: theme.palette.secondary.main,

        // UI
        appBg: theme.palette.background.paper,
        appContentBg: theme.palette.background.default,
        appBorderColor: theme.palette.background.paper,
        appBorderRadius: theme.shape.borderRadius,

        // Typography
        fontBase: theme.typography.fontFamily,
        fontCode: 'monospace',

        // Text colors
        textColor: theme.palette.text.primary,
        textInverseColor: theme.palette.text.secondary,

        // Toolbar default and active colors
        barTextColor: theme.palette.text.secondary,
        barSelectedColor: theme.palette.secondary.main,
        barBg: theme.palette.background.default,

        // Form color
        inputBg: 'transparent',
        inputBorder: 'silver',
        inputTextColor: theme.palette.text.primary,
        inputBorderRadius: theme.shape.borderRadius,

        ...options,
    }

    return asStorybookTheme ? create(themeValue) : themeValue
}

现在您将能够使用完全集成到 Storybook 中的主题 - 当您切换 Storybook 主题时,Storybook 本身将更改为主题并且您使用的组件也会发生变化.

And now you will be able to use the theme fully integrated into Storybook - when you switch storybook theme then Storybook itself will change into the theme AND the components you use will change as well.