hipstersmoothie / storybook-dark-mode

A storybook addon that lets your users toggle between dark and light mode.
MIT License
428 stars 56 forks source link

Stories with global componentWrapperDecorator are not re-rendered when the dark mode changes #271

Open ftr93 opened 3 months ago

ftr93 commented 3 months ago

Storybook 8.0.2 storybook-dark-mode 4.0.1

In my storybook/angular, I use a global componentWrapperDecorator in my preview.ts:

        componentWrapperDecorator(
            (story) => `<div class="story-container my-root-class {{ isDarkModeEnabled ? 'my-dark-theme-class' : 'my-light-theme-class' }}">${story}</div>`
        )    

The story should be re-rendered every time the dark mode changes, but it doesn't.

Previously with Storybook 6, I fixed it by adding the following code to my preview.ts:

let isDarkModeEnabled = false;

const channel = addons.getChannel();
channel.on(DARK_MODE_EVENT_NAME, (event) => {
    if (event !== isDarkModeEnabled) {
        isDarkModeEnabled = event;
        forceReRender();
    }
});

With Storybook 7, the forceReRender() has been removed. And with its replacement channel.emit(FORCE_RE_RENDER);, the class in the componentWrapperDecorator is not updated anymore.

I tried several things, but nothing worked in all cases. Finally, I found this example of a global componentWrapperDecorator with theme classes using globals.

So I modified the plugin code to additionally write the current dark mode state to the globals:

      React.useEffect(() => {
        const currentStore = store();
        const isDarkModeEnabled = currentStore.current === 'dark';
        if (globals.isDarkModeEnabled !== isDarkModeEnabled) {
            updateGlobals({
                isDarkModeEnabled,
            });
            if (stylePreview) {
                updatePreview(currentStore);
            }
        }
    });

    return (
        <IconButton

My decorator now looks like this:

        componentWrapperDecorator(
            (story) => `<div class="story-container my-root-class {{ isDarkModeEnabled ? 'my-dark-theme-class' : 'my-light-theme-class' }}">${story}</div>`,
            (props) => ({ isDarkModeEnabled: props.globals?.isDarkModeEnabled })
        )    

By that, the stories are re-rendered with my decorator and the correct classes are applied on every change.

But the additional storage feels redundant and I'm wondering if there's a better way.

Thanks a lot in advance :-)