storybook-addon-themes
Version:
A storybook addon to switch between different themes for your preview
68 lines (55 loc) • 2.07 kB
text/typescript
import addons, { Listener } from '@storybook/addons';
import { ADDON_ID, CHANGE } from '../constants';
import { DecoratorProps, Theme, ThemeConfig } from '../models';
import { getSelectedTheme, getSelectedThemeName } from '../shared';
import { getHtmlClasses } from './shared';
const channel = addons.getChannel();
let prevCallback: Listener;
function subscribe(callback: Listener) {
if (prevCallback) {
channel.removeListener(CHANGE, prevCallback);
}
if (callback) {
channel.on(CHANGE, callback);
}
prevCallback = callback;
}
function getOrCreate(id: string): HTMLDivElement {
const elementOnDom = document.getElementById(id) as HTMLDivElement;
if (elementOnDom) {
return elementOnDom;
}
const element = document.createElement('div') as HTMLDivElement;
element.setAttribute('id', id);
return element;
}
function createCallback(list: Theme[], callback: Function) {
return (themeName: string) => {
const theme = getSelectedTheme(list, themeName);
const themeClasses = getHtmlClasses(theme);
callback({theme, themes: list, themeClasses, themeName});
}
}
export function ThemeDecorator(config: ThemeConfig, element: string|Node) {
const { Decorator, list, default: defaultTheme } = config;
const lastValue = channel.last(CHANGE);
const themeName = (lastValue && lastValue[0]) || getSelectedThemeName(list, defaultTheme);
const theme = getSelectedTheme(list, themeName);
const themeClasses = getHtmlClasses(theme);
const wrapper = getOrCreate(ADDON_ID);
if (element instanceof Node) {
wrapper.innerHTML = '';
wrapper.appendChild(element);
} else {
wrapper.innerHTML = element;
}
if (Decorator) {
// @ts-ignore
const [decorator, callback] = Decorator({children: wrapper, theme, themes: list, themeClasses, themeName});
subscribe(createCallback(list, callback));
return decorator;
}
wrapper.setAttribute('class', themeClasses);
subscribe(createCallback(list, ({ themeClasses }: DecoratorProps) => wrapper.setAttribute('class', themeClasses)));
return wrapper;
}