主題切換器

一個 Storybook 擴充套件,用於在預覽中切換不同的主題

在 Github 上檢視

Storybook 擴充套件主題

深受 @storybook/addon-backgrounds 的啟發。

這個 Storybook 主題裝飾器可以用於在 Storybook 的預覽中加入自訂的 HTML class 或多個 classes。

Demo

相容性

此版本與 Storybook 版本 6.0.x 相容。

安裝

npm i -D storybook-addon-themes

開始使用

然後將其加入 Storybook 的 main.js 檔案(位於 Storybook 設定目錄中)來啟用擴充套件

module.exports = {
  addons: [
    // Maybe other addons here...
    'storybook-addon-themes'
    // Or here...
  ],
};

請參閱 Storybook 文件 以取得更多資訊。

參數

themes 參數接受 Theme 物件的陣列。

每個 Theme 都是具有以下屬性的物件

  • name (string):主題的名稱
  • class (string | string[] - 選用):與主題相關聯的 HTML class(或多個 classes)
  • color (string):主題選擇器中徽章的顏色
  • default [已棄用] (boolean - 選用):是否預設選取該主題?

themes 參數也接受具有以下屬性的物件

  • default (string - 選用):預設選取的主題名稱
  • list (Theme[] - 必要):主題的列表
  • clearable (boolean - 選用 - 預設為 true):使用者是否可以清除選取的主題?
  • disable (boolean - 選用):為某個 story 停用擴充套件
  • Decorator (Component - 選用):要用作裝飾器元件的元件(更多資訊請參閱下方
  • onChange ((themeName: Theme) => void - 選用):主題變更時將執行的回呼函式
  • target (string - 選用):使用 document.querySelector() 選取將套用 classes 的目標元素。預設為 body,如果 classes 應套用至 documentElement,則為 root

設定

全域

您可以在 Storybook 的 preview.js 檔案中全域設定主題

export const parameters = {
  themes: {
    default: 'twitter',
    list: [
      { name: 'twitter', class: 'theme-twt', color: '#00aced' },
      { name: 'facebook', class: 'theme-fb', color: '#3b5998' }
    ],
  },
};

為了向後相容,也可以直接在 Theme 物件上設定 default (boolean)。由於需要重新定義所有 Theme 物件才能變更預設主題,因此已棄用此做法

// deprecated
export const parameters = {
  themes: [
      { name: 'twitter', class: 'theme-twt', color: '#00aced', default: true },
      { name: 'facebook', class: 'theme-fb', color: '#3b5998' }
  ],
};

請參閱 Storybook 文件 以取得更多資訊。

在 story 中(元件 Story 格式)

或者像這樣在您的 story 檔案中設定主題

export default {
  title: 'CSF|Button',
  component: Button,
  parameters: {
    themes: {
      default: 'twitter',
      list: [
        { name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced' },
        { name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' },
      ],
    },
  },
};

如果您只想為特定的 story 啟用擴充套件或覆寫主題,您可以寫

export default {
  title: 'CSF|Button',
  component: Button,
};

export const withText = () => <Button onClick={action('clicked')}>Hello Button</Button>;
withText.story = {
  parameters: {
    themes: {
      default: 'twitter',
      list: [
        { name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced' },
        { name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' },
      ],
    },
  },
};

在 story 中(StoriesOf API)

或者使用舊的 StoriesOf API

import { storiesOf } from '@storybook/react'; // <- or your storybook framework

storiesOf('StoriesOf|Button', module)
  .addParameters({
    themes: {
      default: 'twitter',
      list: [
        { name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced' },
        { name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' },
      ],
    },
  })
  .add('with text', () => <button>Click me</button>);

以及針對單個 story

import { storiesOf } from '@storybook/react';

storiesOf('StoriesOf|Button', module)
  .add('with text', () => <button>Click me</button>, {
    themes: {
      list: [
        { name: 'red', class: 'theme-red', color: 'rgba(255, 0, 0)' },
      ],
    },
  });

覆寫單個屬性

您也可以僅覆寫 themes 參數上的單個鍵,例如為單個 story 設定不同的預設值

export default {
  title: 'CSF|Button',
  component: Button,
};

export const withText = () => <Button onClick={action('clicked')}>Hello Button</Button>;
withText.story = {
  parameters: {
    themes: {
      default: 'facebook',
    },
  },
};

搭配裝飾器使用

預設情況下,classes 會加入至 body 元素或使用 target 設定的元素。

但是在這種情況下,其他擴充套件(如 @storybook/addon-storyshots)將無法看到您的主題。

若要修正此問題,您可以在您的 stories 中加入 withThemes 裝飾器。

但並非所有框架都提供裝飾器方法

請參閱 這裡 以取得支援框架的列表。

全域

preview.js 檔案中全域設定裝飾器

import { addDecorator } from '@storybook/react'; // <- or your storybook framework
import { withThemes } from 'storybook-addon-themes/react'; // <- or your storybook framework

addDecorator(withThemes);

export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
  themes: {
    default: 'twitter',
    list: [
      { name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced' },
      { name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' },
    ],
  },
};

在 story 中(元件 Story 格式)

或者在您的 story 檔案中(針對該檔案中的所有 stories)

export default {
  title: 'CSF|Button',
  component: Button,
  decorators: [ withThemes ],
  parameters: {
    themes: {
      default: 'twitter',
      list: [
        { name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced' },
        { name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' },
      ],
    },
  },
};

或者僅針對特定的 story

export const withText = () => <Button onClick={action('clicked')}>Hello Button</Button>;
withText.story = {
  decorators: [ withThemes ],
  parameters: {
    themes: {
      default: 'twitter',
      list: [
        { name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced' },
        { name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' },
      ],
    },
  },
};

在 story 中(StoriesOf API)

或者使用舊的 StoriesOf API

import { storiesOf } from '@storybook/react'; // <- or your storybook framework
import { withThemes } from 'storybook-addon-themes/react';

storiesOf('StoriesOf|Button', module)
  .addDecorator(withThemes)
  .add('with text', () => <button>Click me</button>);

自訂裝飾器

一般

您可以使用 theme 參數中的 Decorator 選項,提供將用作裝飾器的元件。

裝飾器將取得以下屬性

  • theme:選取的主題;如果未選取任何主題,則為 undefined
  • themestheme 參數的 list 選項中提供的主題列表。
  • themeClasses:選取主題的格式化主題 classes(如果選取主題上存在 class 選項)。
  • themeName:選取主題的名稱(如果未選取任何主題,則等於 none)。

請勿忘記使用 children 屬性(React/HTML)或 <slot></slot> 元素(Vue/Svelte)來呈現 story。

HTML 範例

若要使用 HTML Storybook 管理反應性,您的裝飾器必須傳回包含兩個元素的陣列

  • 要在 story 中顯示的 HTML 元素
  • 主題變更時將呼叫的更新回呼函式。與裝飾器一樣,回呼函式將接收相同的屬性(不含 children)。

使用 CSS 檔案來變更主題的自訂裝飾器範例

function getOrCreate(id) {
  const elementOnDom = document.getElementById(id);
  if (elementOnDom) {
    return elementOnDom;
  }

  const element = document.createElement('link');
  element.setAttribute('id', id);
  element.setAttribute('rel', 'stylesheet');
  return element;
}

function Decorator(props) {
  const { children } = props;

  function setStyles({ theme, themeName }) {
    const link = getOrCreate('theme-stylesheet');
    if (!theme) {
      link.parentNode && link.parentNode.removeChild(link);
    } else {
      link.href = themeName === 'facebook' ? 'Button-fb.css' : 'Button-twt.css';
      children.appendChild(link);
    }
  }
  setStyles(props);

  return [children, setStyles];
}

React 範例

與上述相同的 React 範例

function Decorator(props) {
  const { children, themeName } = props;
  return (
    <>
      {children}
      {themeName === 'twitter' && <link rel="stylesheet" href="twitter.css"/>}
      {themeName === 'facebook' && <link rel="stylesheet" href="facebook.css"/>}
    </>
  );
};

框架支援表

React React Native Vue Angular Polymer Mithril HTML Marko Svelte Riot Ember Preact
不使用裝飾器的用法 + + + + + + + + + + +
搭配裝飾器使用 + + + +
由以下人員製作
  • huyuqiong
    huyuqiong
適用於
    Angular
    Ember
    HTML
    Marko
    Mithril
    Preact
    Rax
    React
    Riot
    Svelte
    Vue
    Web Components
標籤