storybook-addon-themes icon indicating copy to clipboard operation
storybook-addon-themes copied to clipboard

Theme support for mdx story files

Open Poimen opened this issue 3 years ago • 1 comments

We have a number of themes and aligned to this a number of document only pages that just give details about the design. The idea is that a theme will have a document only pages in mdx format - for instance, the colors, but the colors are preset and are not changeable, they are there for reference to developers. Hence having a story is "overkill" as we would use the story variant as a "theme" to switch the different colors.

We where hoping to use the theme switcher to trigger a change of the color documentation. However, theme switcher doesn't seem to trigger on the theme change so there is no decorator invoked.

We attempted to use the onChange function but this doesn't appear to work in the sense that there is no this context provided so we cannot access anything.

Is there a way of using theme switcher with mdx files?

Poimen avatar May 11 '21 16:05 Poimen

Using Storybook 7, found out that the theme selector addon was present on the toolbar in documents (.mdx) but wasn't working. A workaround for this is to make use of the URL GET parameter which is triggering the theme switch: globals=theme:Theme1

Changes in preview.js In my case, I was relying on data-attibute which was applied on the <html> in each story/doc etc like this: [data-theme="AAA"]. In case you defined your themes inside the decorator, try to have them in a const in order to use it in your .mdx files, and pass the object to your decorator like so:

import { withThemeByDataAttribute } from '@storybook/addon-themes';

export const themeConfig = {
  themes: {
    Theme1: 'AAA',
    Theme2: 'BBB',
    Theme3: 'CCC',
  },
  defaultTheme: 'Theme1',
};

export const decorators = [
  withThemeByDataAttribute({
    ...themeConfig,
    attributeName: 'data-theme',
  }),
];

Then create a themeSwitcher.js inside your assets where you need to import the themeConfig from your preview.js and use the MutationObserver to observe the URL changes. Once the URL is changed, switch the theme by extracting the value from your themeConfig object, based on the key taken from URL and set the data-theme attribute on your document.

import { themeConfig } from '../../.storybook/preview';

function themeSwitcher() {
  let previousUrl = '';
  const observer = new MutationObserver(function() {
    if (window.location.href !== previousUrl) {
        switchTheme();
      }
  });
  const config = {subtree: true, childList: true};

  observer.observe(document, config);
}

function findGetParameter(parameterName) {
  var result = null,
      tmp = [];
  location.search
      .substr(1)
      .split("&")
      .forEach(function (item) {
        tmp = item.split("=");
        if (tmp[0] === parameterName) result = decodeURIComponent(tmp[1]);
      });
  return result;
}

function switchTheme(){
  const requestTheme = findGetParameter("globals");
  if (requestTheme === null)
    return;

  const themeName = requestTheme.split(":")[1];
  
  if (document.documentElement.dataset.theme === themeConfig.themes[themeName])
    return;
    
  document.documentElement.setAttribute('data-theme', themeConfig.themes[themeName]);
}

themeSwitcher()

Then all you need to do, is to import the themeSwitcher in your mdx files.

import '../assets/themeSwitcher';

iftimiedaniel avatar Oct 23 '23 21:10 iftimiedaniel