npm package: @rize-finance/storybook-addon-dynamic-theme
Description: Match the Storybook UI to the component theme
Author: greg@rizemoney.com
Latest version: 0.0.5

# @rize-finance/storybook-addon-dynamic-theme

Match the [Storybook](https://storybook.js.org/) UI theme with your dynamic, context-based component theme.

![change theme](./docs/change-theme.gif)

This addon works together with the excellent [contexts addon](https://github.com/storybookjs/storybook/tree/master/addons/contexts) to apply your custom theme's colors to the Storybook UI theme.

To use it:

- set up the [contexts addon](https://github.com/storybookjs/storybook/tree/master/addons/contexts) as described in its [README](https://github.com/storybookjs/storybook/blob/master/addons/contexts/README.md).

- install this addon

  ```
  yarn add -D @rize-finance/storybook-addon-dynamic-theme
  ```

- register via the addons section of `main.js`

* use the addParameters api to set the theme and define the themeTranslation

## Example

`.storybook/main.js`

```javascript
module.exports = {
  stories: ["../src/**/*.stories.tsx"],
  addons: [
    "@storybook/addon-contexts/register",
    "@rize-finance/storybook-addon-dynamic-theme"
  ]
};
```

`.storybook/contexts.js`

```javascript
import { ThemeProvider } from "emotion-theming";
import { themes } from "../src/style/theme"; // your custom themes

export const themeTitle = "Themes";
export const themePropName = "theme";
export const themeDefault = themes.light;

export const contexts = [
  {
    icon: "mirror", // a icon displayed in the Storybook toolbar to control contextual props
    title: themeTitle, // an unique name of a contextual environment
    components: [
      // an array of components that is going to be injected to wrap stories
      ThemeProvider
    ],
    params: [
      // an array of params contains a set of predefined `props` for `components`
      {
        name: "Light Theme",
        props: { [themePropName]: themes.light },
        default: true
      },
      { name: "Dark Theme", props: { [themePropName]: themes.dark } }
    ],
    options: {
      deep: true, // pass the `props` deeply into all wrapping components
      disable: false, // disable this contextual environment completely
      cancelable: false // allow this contextual environment to be opt-out optionally in toolbar
    }
  }
];
```

`.storybook/preview.js`

```javascript
import React from "react";
import { addDecorator, addParameters } from "@storybook/react";
import { create } from "@storybook/theming";
import { withContexts } from "@storybook/addon-contexts/react";
import { translateTheme } from "@rize-finance/storybook-addon-dynamic-theme";
import { contexts, themeTitle, themePropName, themeDefault } from "./contexts";

const baseTheme = create({
  brandTitle: "Components"
});

const themeTranslation = {
  contextPath: [themeTitle, themePropName],
  static: baseTheme,
  dynamic: {
    base: "colors.base",
    colorPrimary: "colors.main",
    appContentBg: "colors.background",
    appBg: "colors.lineWeak",
    colorSecondary: "colors.secondary",
    appBorderColor: "colors.lineStrong",
    appBorderRadius: "borderRadius",
    fontBase: "fontBase",
    fontCode: "monospace",
    textColor: "colors.text",
    textInverseColor: "colors.textInverse",
    barTextColor: "colors.text",
    barSelectedColor: "colors.textStrong",
    barBg: "colors.line",
    inputBg: "colors.background",
    inputBorder: "colors.line",
    inputTextColor: "colors.text"
  }
};

const theme = translateTheme(themeDefault, themeTranslation);

addParameters({
  options: { theme },
  themeTranslation
});
addDecorator(withContexts(contexts));
```

`src/style/theme.js`

```javascript
const light = {
  colors: {
    base: "light",
    main: "red",
    secondary: "blue",
    background: "white",
    lineWeak: "#eee",
    line: "#ddd",
    lineStrong: "#ccc",
    text: "#222",
    textStrong: "#111",
    textInverse: "#eee"
  },
  borderRadius: 0,
  fontBase: '"Open Sans", sans-serif'
};

const dark = {
  colors: {
    base: "dark",
    main: "blue",
    secondary: "red",
    background: "black",
    lineWeak: "#111",
    line: "#222",
    lineStrong: "#333",
    text: "#ddd",
    textStrong: "#eee",
    textInverse: "#111"
  },
  borderRadius: 0,
  fontBase: '"Open Sans", sans-serif'
};

export const themes = {
  light,
  dark
};
```

The important part is the `themeTranslation` object.

- `contextPath` : locates the theme within the full context
- `static` : parts of the [Storybook theme](https://storybook.js.org/docs/configurations/theming/) which don't change with context
- `dynamic` : mapping from custom theme to [Storybook theme](https://storybook.js.org/docs/configurations/theming/)

  - key is a [Storybook theme](https://storybook.js.org/docs/configurations/theming/) property
  - value is the [lodash get](https://lodash.com/docs/4.17.15#get) path to locate the desired property from the custom theme