hipstersmoothie / storybook-dark-mode

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

Allow target attribute to be changed #168

Open bruceharrison1984 opened 2 years ago

bruceharrison1984 commented 2 years ago

Currently we have classTarget to specify what element to append a class to. It would be nice to specify the attribute that is being overridden.

In my case, my main theme is set via a data-theme attribute on the html element. I am using DaisyUI for my frontend.

AFAIK, there isn't a way to customize the classTarget to point it at another attribute.

bruceharrison1984 commented 2 years ago

After a trivial amount of playing around, the following global decorator lets you override the target attribute. It would still be nice if this was a first class feature though!

// preview.js
import { useDarkMode } from 'storybook-dark-mode';
import { useEffect } from 'react';

export const decorators = [
  (Story) => {
    const isDarkMode = useDarkMode();
    useEffect(() => document.documentElement.dataset.theme = isDarkMode ? "dark" : "light", [isDarkMode]);
    return <Story />;
  }
];
raybrownco commented 1 year ago

Important note: if you're using the code snippet above in conjunction with the @storybook/testing-react addon, you'll need to mock window.matchMedia before importing useDarkMode from storybook-dark-mode. You can do this like so:

// .storybook/decorators/mock-window-matchmedia.js
Object.defineProperty(window, 'matchMedia', {
  writable: true,
  value: query => ({
    matches: false,
    media: query,
    onchange: null,
    addListener: () => {}, // deprecated
    removeListener: () => {}, // deprecated
    addEventListener: () => {},
    removeEventListener: () => {},
    dispatchEvent: () => {},
  }),
})
// .storybook/decorators/dark-mode.js:
import './mock-window-matchmedia'; // ⚠️ Important! Import this first
import { useDarkMode } from 'storybook-dark-mode';
import { useEffect } from 'react';

export default function decorateContrastMode(Story) {
  const isDarkMode = useDarkMode();
  useEffect(() => document.documentElement.dataset.theme = isDarkMode ? "dark" : "light", [isDarkMode]);
  return <Story />;
}
// .storybook/preview.js:
import decorateContrastMode from './decorators/contrast-mode'

export const decorators = [decorateContrastMode]
hipstersmoothie commented 1 year ago

If anyone wants to submit a PR to add this decorator to the package feel free to ping me!