I am making a PWA using light and dark themes, and I want to create my Storybook light and dark themes to reflect those.
So I created a function that will return a new Storybook theme object to me if I pass it a Material UI Theme and a base name.
But how do I pass these 2 objects to Storybook as themes?
I have found out that I am supposed to go to manager.js
and add the following code
import theme from '../src/theme/theme'
import createThemeFromMUITheme from './create-theme-from-mui-theme'
import addons from '@storybook/addons'
addons.setConfig({
theme: createThemeFromMUITheme('light', theme.light),
})
But how do I set theme for both light and dark?
I have tried
import theme from '../src/theme/theme'
import createThemeFromMUITheme from './create-theme-from-mui-theme'
import addons from '@storybook/addons'
addons.setConfig({
theme: {
light: createThemeFromMUITheme('light', theme.light),
dark: createThemeFromMUITheme('dark', theme.dark)
},
})
But that makes the storybook show nothing (it does not fail however)
Help please :-)
EDIT: I have also tried the following
import theme from '../src/theme/theme'
import createThemeFromMUITheme from './create-theme-from-mui-theme'
import addons from '@storybook/addons'
addons.setConfig({
theme: createThemeFromMUITheme('light', theme.light),
})
addons.setConfig({
theme: createThemeFromMUITheme('dark', theme.dark),
})
EDIT #2: The returned theme config object from createThemeFromMUITheme is valid BTW
And if anyone should want the function I made to convert a MUI theme object into a SB theme object - then this is it...
(I did not mess with form colors yet...)
import { create } from '@storybook/theming/create'
const createThemeFromMUITheme = (name, theme) => {
return create({
base: name,
colorPrimary: theme.palette.primary.main,
colorSecondary: theme.palette.secondary.main,
// UI
appBg: theme.palette.background.default,
appContentBg: theme.palette.background.paper,
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.primary,
barSelectedColor: theme.palette.text.secondary,
barBg: theme.palette.background.default,
brandTitle: 'Add your brand title here',
brandUrl: 'https://yourbrandurl.com',
brandImage: 'https://placehold.it/350x150',
})
}
export default createThemeFromMUITheme
Well - the solution was staring me right in the eyes, but as usual I had not read the documentation enough.
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.
And here is what I was aiming at
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.
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'
},
})
})
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 })
},
}
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>
)
}
And here is the hook that makes the update happen ./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
}
And finally here is ./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
}
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.