-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dynamic Themes #5889
Comments
Take a look at this PR to see how @hipstersmoothie did it? #5841 |
My use case is fairly different. I elected to go the route of writing an addon, but currently struggling with getting the theme data to pass to and re-render the iframe with the story wrapped in a So far I've figured out that I can grab the current theme information off the context in a decorator like so: export const withTheme = (story, context) => {
const theme = context.parameters.options.theme.base;
return <ThemeProvider theme={theme}>story()</ThemeProvider>;
}; This doesn't seem to update when the theme is changed. Is there an event I can hook into from an |
I cannot figure out how to retrieve what I set via import React from 'react';
import { useState } from 'react';
import { themes } from '../../themes';
import {
IconButton,
WithTooltip,
TooltipLinkList,
} from '@storybook/components';
import addons, { types, makeDecorator } from '@storybook/addons';
import { FORCE_RE_RENDER } from '@storybook/core-events';
import styled, { ThemeProvider } from 'styled-components';
const ThemeIcon = styled.span`
height: 1rem;
width: 1rem;
display: block;
background: red;
`;
addons.register('storybook/theme-switcher', api => {
addons.add('storybook/theme-switcher', {
title: 'theme-switcher',
type: types.TOOL,
match: ({ viewMode }) => viewMode === 'story',
render: () => <ThemeSwitcher api={api} />,
});
});
export const ThemeSwitcher = ({ api }) => {
const [activeTheme, setTheme] = useState('light');
const [expanded, setExpanded] = useState(false);
const themeList = ['dark', 'light', 'a11y', 'vaporware'].map(i => ({
id: i,
title: i,
onClick: () => {
setTheme(i);
api.setOptions({
i,
theme: themes[i] || themes.light,
});
addons.getChannel().emit(FORCE_RE_RENDER);
},
right: <ThemeIcon />,
}));
return (
<WithTooltip
placement="top"
trigger="click"
tooltipShown={expanded}
onVisibilityChange={s => setExpanded(s)}
tooltip={<TooltipLinkList links={themeList} />}
closeOnClick
>
<IconButton key="theme-switcher">{activeTheme}</IconButton>
</WithTooltip>
);
};
export const withTheme = makeDecorator({
name: 'withTheme',
parameterName: 'theme',
skipIfNoParametersOrOptions: false,
allowDeprecatedUsage: false,
wrapper: (getStory, context) => {
const theme = context.parameters.options.theme;
console.log('context: ', context);
return (
<ThemeProvider theme={theme}>{getStory(context)}</ThemeProvider>
);
},
}); |
I've been following this because I'm very interested in it. When I read the linked issue, I kind of read it as not possible until 5.1 and then by means of an addon. I wonder if the docs got a bit ahead. |
Where did you read this is not possible until 5.1? |
Update: It's kind of a hack, but I pulled it off with const themeList = ['dark', 'light', 'a11y', 'vaporware'].map(i => ({
id: i,
title: i,
onClick: () => {
setTheme(i);
api.setOptions({
i,
theme: themes[i] || themes.light,
});
window.localStorage.setItem('iris-sb-theme', i);
addons.getChannel().emit(FORCE_RE_RENDER);
},
right: <ThemeIcon />,
})); export const withTheme = makeDecorator({
name: 'withTheme',
parameterName: 'theme',
skipIfNoParametersOrOptions: false,
allowDeprecatedUsage: false,
wrapper: (getStory, context) => {
const savedTheme = window.localStorage.getItem('iris-sb-theme');
const theme =
typeof themes[savedTheme] === 'object'
? themes[savedTheme]
: themes.light;
return (
<ThemeProvider theme={theme}>{getStory(context)}</ThemeProvider>
);
},
}); |
Update update: It doesn't persist when switching stories. Still working on that. |
I think @ndelangen has a plan on exposing an API to getOptions from an addon |
@seanmcintyre you might also be interested in hipstersmoothie/storybook-dark-mode#1 |
I've tried something similar. It seems hard to find documentation of the 5.0 API 😅 |
Figured it out. This definitely still feels like a hack though. function bindThemeOverride(api) {
const channel = api.getChannel();
channel.on('storiesConfigured', () => {
setLocalTheme({ api });
});
channel.on('storyChanged', () => {
setLocalTheme({ api });
});
} Full code: import React, { useEffect } from 'react';
import { useState } from 'react';
import { themes } from '../../themes';
import {
IconButton,
WithTooltip,
TooltipLinkList,
} from '@storybook/components';
import addons, { types, makeDecorator } from '@storybook/addons';
import { FORCE_RE_RENDER } from '@storybook/core-events';
import styled, { ThemeProvider } from 'styled-components';
addons.register('storybook/theme-switcher', api => {
addons.add('storybook/theme-switcher', {
title: 'theme-switcher',
type: types.TOOL,
match: ({ viewMode }) => viewMode === 'story',
render: () => <ThemeSwitcher api={api} />,
});
});
export const withTheme = makeDecorator({
name: 'withTheme',
parameterName: 'theme',
skipIfNoParametersOrOptions: false,
allowDeprecatedUsage: false,
wrapper: (getStory, context) => (
<ThemeProvider theme={getLocalTheme()[1]}>
{getStory(context)}
</ThemeProvider>
),
});
export const ThemeSwitcher = ({ api }) => {
const [activeTheme, setTheme] = useState(getLocalTheme()[0]);
const [expanded, setExpanded] = useState(false);
useEffect(() => bindThemeOverride(api), []);
const themeList = ['dark', 'light', 'a11y', 'vaporware'].map(i => ({
id: i,
title: i,
onClick: () => {
setTheme(i);
setLocalTheme({ api, theme: i, rerender: true });
},
right: <ThemeIcon />,
}));
return (
<WithTooltip
placement="top"
trigger="click"
tooltipShown={expanded}
onVisibilityChange={s => setExpanded(s)}
tooltip={<TooltipLinkList links={themeList} />}
closeOnClick
>
<IconButton key="theme-switcher">{activeTheme}</IconButton>
</WithTooltip>
);
};
const ThemeIcon = styled.span`
height: 1rem;
width: 1rem;
display: block;
background: red;
`;
function bindThemeOverride(api) {
const channel = api.getChannel();
channel.on('storiesConfigured', () => {
setLocalTheme({ api });
});
channel.on('storyChanged', () => {
setLocalTheme({ api });
});
}
function setLocalTheme({
api,
theme = getLocalTheme()[0],
rerender = false,
}) {
window.localStorage.setItem('iris-sb-theme', theme);
api.setOptions({
theme: getLocalTheme()[1],
});
if (rerender) {
addons.getChannel().emit(FORCE_RE_RENDER);
}
}
function getLocalTheme() {
const savedTheme = window.localStorage.getItem('iris-sb-theme');
const theme =
typeof themes[savedTheme] === 'object'
? themes[savedTheme]
: themes.light;
return [savedTheme, theme];
} I'm going to clean this up and release an addon that takes an array of themes from a |
Would love official support for this! Thanks @seanmcintyre for the workaround |
This makes sense as an addon to me. It's amazing! Keep it up @seanmcintyre 👏 |
So I personally only want Storybook's theme to change along with my macOS system theme (for when I'm working at night). My app itself already uses the https://gist.github.com/nfarina/fb708f66858d2d3317877ab8adf8d926 Simply add the file to your project and import it in your |
That's really cool @nfarina |
Thanks for sharing that @seanmcintyre. How far away is the addon? |
@seanmcintyre @faizalMo does this addon works for you? https://github.com/tonai/storybook-addon-themes/tree/master/src |
Adding fully dynamic theming in #6806 |
@Yankovsky my use case was slightly different. I wanted a way to easily switch between override CSS files so that our WebComponents showcased in Storybook can be displayed in different styles/themes. I achieved this by adding the CSS using preview-head.html and then switching it with custom storybook addon. |
Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks! |
Hey there, it's me again! I am going close this issue to help our maintainers focus on the current development roadmap instead. If the issue mentioned is still a concern, please open a new ticket and mention this old one. Cheers and thanks for using Storybook! |
Which addon did you use? I am trying to find a solution for swapping out my custom prop and scss imports based on theme for our components (not the Storybook UI theme), ideally on-the-fly. |
@jraeruhl https://github.com/tonai/storybook-addon-themes |
This is a little bit of a hack, because I have not yet been able to correctly use https://github.com/seanmcintyre/addon-themes |
Note: |
Unsure if this is a bug report or a feature request 😅
The current docs state:
I am specifically trying to do this in conjunction with styled-components
<ThemeProvider />
and@storybook/addon-knobs
. However, I have not yet been able to figure out how to do this in even a simple manner. My ultimate goal is to change both the Storybook theme and the theme of my components in tandem.My first attempt was as follows:
config.tsx
System:
@storybook/theming
,@storybook/addon-knobs
The text was updated successfully, but these errors were encountered: