storybook-addon-themes
Advanced tools
Comparing version 6.0.1 to 6.1.0
@@ -9,5 +9,12 @@ # Changelog | ||
## [6.1.0] - 2021-04-05 | ||
### Added | ||
- Default as string parameter in config. ([@norin89](https://github.com/https://github.com/norin89) in [#43](https://github.com/tonai/storybook-addon-themes/pull/43)) | ||
- Add HTML support for decorators. ([@tonai](https://github.com/https://github.com/tonai) in [#44](https://github.com/tonai/storybook-addon-themes/pull/44)) | ||
- Allow to set target element to apply classes to. ([@pixtron](https://github.com/https://github.com/pixtron) in [#45](https://github.com/tonai/storybook-addon-themes/pull/45)) | ||
- Update Storybook addon metadata. ([@coderkevin](https://github.com/https://github.com/coderkevin) in [#46](https://github.com/tonai/storybook-addon-themes/pull/46)) | ||
## [6.0.1] - 2020-12-08 | ||
### Added | ||
- Remove peer dependencies. ([@https://github.com/rcline](https://github.com/https://github.com/rcline) in [#40](https://github.com/tonai/storybook-addon-themes/pull/40)) | ||
- Remove peer dependencies. ([@rcline](https://github.com/https://github.com/rcline) in [#40](https://github.com/tonai/storybook-addon-themes/pull/40)) | ||
- Remove @types/vue + security fix. | ||
@@ -14,0 +21,0 @@ |
@@ -60,7 +60,8 @@ "use strict"; | ||
var Decorator = config.Decorator, | ||
list = config.list; | ||
list = config.list, | ||
defaultTheme = config["default"]; | ||
var _useState = (0, _react.useState)(function () { | ||
var lastValue = channel.last(_constants.CHANGE); | ||
return lastValue && lastValue[0] || (0, _shared.getSelectedThemeName)(list); | ||
return lastValue && lastValue[0] || (0, _shared.getSelectedThemeName)(list, defaultTheme); | ||
}), | ||
@@ -67,0 +68,0 @@ _useState2 = _slicedToArray(_useState, 2), |
@@ -11,3 +11,3 @@ "use strict"; | ||
function getHtmlClasses(theme) { | ||
return theme && theme["class"] ? theme["class"] instanceof Array ? theme["class"].join(' ') : theme["class"] : undefined; | ||
return theme && theme["class"] ? theme["class"] instanceof Array ? theme["class"].join(' ') : theme["class"] : ''; | ||
} |
@@ -35,3 +35,3 @@ "use strict"; | ||
return { | ||
themeName: lastValue && lastValue[0] || (0, _shared.getSelectedThemeName)(this.config.list) | ||
themeName: lastValue && lastValue[0] || (0, _shared.getSelectedThemeName)(this.config.list, this.config["default"]) | ||
}; | ||
@@ -38,0 +38,0 @@ }, |
@@ -93,5 +93,7 @@ "use strict"; | ||
clearable = _getConfigFromApi.clearable, | ||
list = _getConfigFromApi.list; | ||
list = _getConfigFromApi.list, | ||
target = _getConfigFromApi.target, | ||
defaultTheme = _getConfigFromApi["default"]; | ||
var selectedThemeName = (0, _shared.getSelectedThemeName)(list, state.selected); | ||
var selectedThemeName = (0, _shared.getSelectedThemeName)(list, defaultTheme, state.selected); | ||
var availableThemeSelectorItems = []; | ||
@@ -116,3 +118,4 @@ var selectedTheme; | ||
selectedTheme: selectedTheme, | ||
themes: list | ||
themes: list, | ||
target: target | ||
}; | ||
@@ -209,2 +212,3 @@ }); | ||
selectedTheme = _getDisplayableState.selectedTheme, | ||
target = _getDisplayableState.target, | ||
themes = _getDisplayableState.themes; | ||
@@ -215,2 +219,3 @@ | ||
selectedTheme: selectedTheme, | ||
target: target, | ||
themes: themes | ||
@@ -217,0 +222,0 @@ }), /*#__PURE__*/_react["default"].createElement(_components.WithTooltip, { |
@@ -7,4 +7,5 @@ import React from 'react'; | ||
themes: Theme[]; | ||
target?: string; | ||
} | ||
export declare const ThemeStory: React.FC<Props>; | ||
export {}; |
@@ -35,4 +35,7 @@ "use strict"; | ||
selectedTheme = props.selectedTheme, | ||
target = props.target, | ||
themes = props.themes; | ||
(0, _react.useEffect)(function () { | ||
var targetEl; | ||
var iframe = _global.document.getElementById(iframeId); | ||
@@ -45,12 +48,28 @@ | ||
var iframeDocument = iframe.contentDocument || iframe.contentWindow.document; | ||
var body = iframeDocument.body; // Add selected theme class(es). | ||
switch (target) { | ||
case 'root': | ||
case 'html': | ||
targetEl = iframeDocument.documentElement; | ||
break; | ||
default: | ||
if (!target || target === 'body') { | ||
targetEl = iframeDocument.body; | ||
} else { | ||
targetEl = iframeDocument.documentElement.querySelector(target); | ||
} | ||
break; | ||
} // Add selected theme class(es). | ||
if (selectedTheme && selectedTheme["class"]) { | ||
if (typeof selectedTheme["class"] === 'string') { | ||
body.classList.add(selectedTheme["class"]); | ||
targetEl.classList.add(selectedTheme["class"]); | ||
} else { | ||
var _body$classList; | ||
var _targetEl$classList; | ||
// string[] | ||
(_body$classList = body.classList).add.apply(_body$classList, _toConsumableArray(selectedTheme["class"])); | ||
(_targetEl$classList = targetEl.classList).add.apply(_targetEl$classList, _toConsumableArray(selectedTheme["class"])); | ||
} | ||
@@ -64,8 +83,8 @@ } | ||
if (typeof theme["class"] === 'string') { | ||
body.classList.remove(theme["class"]); | ||
targetEl.classList.remove(theme["class"]); | ||
} else { | ||
var _body$classList2; | ||
var _targetEl$classList2; | ||
// string[] | ||
(_body$classList2 = body.classList).remove.apply(_body$classList2, _toConsumableArray(theme["class"])); | ||
(_targetEl$classList2 = targetEl.classList).remove.apply(_targetEl$classList2, _toConsumableArray(theme["class"])); | ||
} | ||
@@ -72,0 +91,0 @@ }); |
@@ -6,4 +6,6 @@ import { Decorator } from './Decorator'; | ||
Decorator?: Decorator; | ||
default?: string; | ||
list: Theme[]; | ||
onChange?: (themeName: Theme) => void; | ||
target?: string; | ||
} |
@@ -5,3 +5,3 @@ import { API } from '@storybook/api'; | ||
export declare function getConfig(parameters: ThemeConfig | Theme[]): ThemeConfig; | ||
export declare function getSelectedThemeName(list: Theme[], currentSelectedValue?: string): string; | ||
export declare function getSelectedThemeName(list: Theme[], defaultTheme?: string, currentSelectedValue?: string): string; | ||
export declare function getSelectedTheme(list: Theme[], themeName: string): Theme; |
@@ -36,3 +36,3 @@ "use strict"; | ||
function getSelectedThemeName(list, currentSelectedValue) { | ||
function getSelectedThemeName(list, defaultTheme, currentSelectedValue) { | ||
if (!list.length) { | ||
@@ -52,2 +52,6 @@ return 'none'; | ||
if (defaultTheme) { | ||
return defaultTheme; | ||
} | ||
if (list.find(function (i) { | ||
@@ -64,4 +68,2 @@ return i["default"]; | ||
; | ||
function getSelectedTheme(list, themeName) { | ||
@@ -68,0 +70,0 @@ return list.find(function (_ref) { |
{ | ||
"name": "storybook-addon-themes", | ||
"version": "6.0.1", | ||
"version": "6.1.0", | ||
"description": "A storybook addon to switch between different themes for your preview", | ||
@@ -18,3 +18,5 @@ "keywords": [ | ||
"vue", | ||
"vuejs" | ||
"vuejs", | ||
"storybook-addon", | ||
"style" | ||
], | ||
@@ -86,3 +88,9 @@ "homepage": "https://github.com/tonai/storybook-addon-themes", | ||
"access": "public" | ||
}, | ||
"storybook": { | ||
"displayName": "Theme switcher", | ||
"unsupportedFrameworks": [ | ||
"react-native" | ||
] | ||
} | ||
} |
220
README.md
@@ -5,3 +5,3 @@ # Storybook Addon Themes | ||
This Storybook Theme Decorator can be used to add a custom HTML class or classes to the the preview in [Storybook](https://storybook.js.org). | ||
This Storybook Theme Decorator can be used to add a custom HTML class or classes to the preview in [Storybook](https://storybook.js.org). | ||
@@ -24,3 +24,3 @@ ![Demo](media/demo.gif) | ||
```js | ||
```jsx | ||
module.exports = { | ||
@@ -41,16 +41,18 @@ addons: [ | ||
Each `Theme` is an object with the following properties : | ||
Each `Theme` is an object with the following properties: | ||
* `name` (`string`): Name of the theme | ||
* `class` (`string | string[]` - optionnal): HTML class(es) associated with the theme | ||
* `class` (`string | string[]` - optional): HTML class(es) associated with the theme | ||
* `color` (`string`): The color of the badge in the theme selector | ||
* `default` (`boolean` - optionnal): Is the theme selected by default ? | ||
* `default` [_deprecated_] (`boolean` - optional): Is the theme selected by default? | ||
The `themes` parameter also accept an object with the following properties : | ||
The `themes` parameter also accept an object with the following properties: | ||
* `default` (`string` - optional): Name of theme selected by default | ||
* `list` (`Theme[]` - required): The list of themes | ||
* `clearable` (`boolean` - optionnal - default is `true`): Can the user clear the selected theme ? | ||
* `disable` (`boolean` - optionnal): Disable the addon for a story | ||
* `Decorator` (`Component` - optionnal): A component to use as the decorator component ([see below](#custom-decorator) for more informations) | ||
* `onChange` (`(themeName: Theme) => void` - optionnal): A callback that will be executed when the theme changes | ||
* `clearable` (`boolean` - optional - default is `true`): Can the user clear the selected theme ? | ||
* `disable` (`boolean` - optional): Disable the addon for a story | ||
* `Decorator` (`Component` - optional): A component to use as the decorator component ([see below](#custom-decorator) for more information) | ||
* `onChange` (`(themeName: Theme) => void` - optional): A callback that will be executed when the theme changes | ||
* `target` (`string` - optional): Target element selected with `document.querySelector()` to which classes are applied. Defaults to `body`, `root` if classes should be applied to `documentElement`. | ||
@@ -63,8 +65,24 @@ ## Configuration | ||
```js | ||
```jsx | ||
export const parameters = { | ||
themes: { | ||
default: 'twitter', | ||
list: [ | ||
{ name: 'twitter', class: 'theme-twt', color: '#00aced' }, | ||
{ name: 'facebook', class: 'theme-fb', color: '#3b5998' } | ||
], | ||
}, | ||
}; | ||
``` | ||
For backward compatibility `default` (`boolean`) can also be set directly on `Theme` object. | ||
**This has been deprecated** because of the difficulty of changing the default theme due to the need to redefine all `Theme` objects. | ||
```jsx | ||
// deprecated | ||
export const parameters = { | ||
themes: [ | ||
{ name: 'twitter', class: 'theme-twt', color: '#00aced', default: true }, | ||
{ name: 'facebook', class: 'theme-fb', color: '#3b5998' } | ||
] | ||
{ name: 'twitter', class: 'theme-twt', color: '#00aced', default: true }, | ||
{ name: 'facebook', class: 'theme-fb', color: '#3b5998' } | ||
], | ||
}; | ||
@@ -79,3 +97,3 @@ ``` | ||
```js | ||
```jsx | ||
export default { | ||
@@ -85,6 +103,9 @@ title: 'CSF|Button', | ||
parameters: { | ||
themes: [ | ||
{ name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced', default: true }, | ||
{ name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' }, | ||
], | ||
themes: { | ||
default: 'twitter', | ||
list: [ | ||
{ name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced' }, | ||
{ name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' }, | ||
], | ||
}, | ||
}, | ||
@@ -96,3 +117,3 @@ }; | ||
```js | ||
```jsx | ||
export default { | ||
@@ -106,6 +127,9 @@ title: 'CSF|Button', | ||
parameters: { | ||
themes: [ | ||
{ name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced', default: true }, | ||
{ name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' }, | ||
], | ||
themes: { | ||
default: 'twitter', | ||
list: [ | ||
{ name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced' }, | ||
{ name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' }, | ||
], | ||
}, | ||
}, | ||
@@ -119,3 +143,3 @@ }; | ||
```js | ||
```jsx | ||
import { storiesOf } from '@storybook/react'; // <- or your storybook framework | ||
@@ -125,6 +149,9 @@ | ||
.addParameters({ | ||
themes: [ | ||
{ name: 'twitter', class: 'theme-twt', color: '#00aced', default: true }, | ||
{ name: 'facebook', class: 'theme-fb', color: '#3b5998' }, | ||
], | ||
themes: { | ||
default: 'twitter', | ||
list: [ | ||
{ name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced' }, | ||
{ name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' }, | ||
], | ||
}, | ||
}) | ||
@@ -136,3 +163,3 @@ .add('with text', () => <button>Click me</button>); | ||
```js | ||
```jsx | ||
import { storiesOf } from '@storybook/react'; | ||
@@ -142,5 +169,7 @@ | ||
.add('with text', () => <button>Click me</button>, { | ||
themes: [ | ||
{ name: 'red', class: 'theme-red', color: 'rgba(255, 0, 0)' }, | ||
], | ||
themes: { | ||
list: [ | ||
{ name: 'red', class: 'theme-red', color: 'rgba(255, 0, 0)' }, | ||
], | ||
}, | ||
}); | ||
@@ -150,5 +179,24 @@ | ||
### Overwriting single properties | ||
You can also only override a single key on the themes parameter, for instance to set a different default value for a single story: | ||
```jsx | ||
export default { | ||
title: 'CSF|Button', | ||
component: Button, | ||
}; | ||
export const withText = () => <Button onClick={action('clicked')}>Hello Button</Button>; | ||
withText.story = { | ||
parameters: { | ||
themes: { | ||
default: 'facebook', | ||
}, | ||
}, | ||
}; | ||
``` | ||
## Usage with decorator | ||
By default the classes will be added to the `body` element. | ||
By default the classes will be added to the `body` element or the element configured with `target`. | ||
@@ -167,3 +215,3 @@ But in this case your theme will not be visible by other addons (like [@storybook/addon-storyshots](https://github.com/storybookjs/storybook/tree/next/addons/storyshots)). | ||
```js | ||
```jsx | ||
import { addDecorator } from '@storybook/react'; // <- or your storybook framework | ||
@@ -176,6 +224,9 @@ import { withThemes } from 'storybook-addon-themes/react'; // <- or your storybook framework | ||
actions: { argTypesRegex: "^on[A-Z].*" }, | ||
themes: [ | ||
{ name: 'twitter', class: 'theme-twt', color: '#00aced', default: true }, | ||
{ name: 'facebook', class: 'theme-fb', color: '#3b5998' } | ||
] | ||
themes: { | ||
default: 'twitter', | ||
list: [ | ||
{ name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced' }, | ||
{ name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' }, | ||
], | ||
}, | ||
}; | ||
@@ -188,3 +239,3 @@ ``` | ||
```js | ||
```jsx | ||
export default { | ||
@@ -195,6 +246,9 @@ title: 'CSF|Button', | ||
parameters: { | ||
themes: [ | ||
{ name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced', default: true }, | ||
{ name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' }, | ||
], | ||
themes: { | ||
default: 'twitter', | ||
list: [ | ||
{ name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced' }, | ||
{ name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' }, | ||
], | ||
}, | ||
}, | ||
@@ -206,3 +260,3 @@ }; | ||
```js | ||
```jsx | ||
export const withText = () => <Button onClick={action('clicked')}>Hello Button</Button>; | ||
@@ -212,6 +266,9 @@ withText.story = { | ||
parameters: { | ||
themes: [ | ||
{ name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced', default: true }, | ||
{ name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' }, | ||
], | ||
themes: { | ||
default: 'twitter', | ||
list: [ | ||
{ name: 'twitter', class: ['theme-twt', 'light-mode'], color: '#00aced' }, | ||
{ name: 'facebook', class: ['theme-fb', 'dark-mode'], color: '#3b5998' }, | ||
], | ||
}, | ||
}, | ||
@@ -225,3 +282,3 @@ }; | ||
```js | ||
```jsx | ||
import { storiesOf } from '@storybook/react'; // <- or your storybook framework | ||
@@ -237,2 +294,4 @@ import { withThemes } from 'storybook-addon-themes/react'; | ||
#### General | ||
You can provide a component that will be used as decorator using the `Decorator` option in the `theme` parameter. | ||
@@ -247,10 +306,50 @@ | ||
Don't forget to render the story using the `children` prop (for React) or the `<slot></slot>` element (for Vue.js). | ||
Don't forget to render the story using the `children` prop (React/HTML) or the `<slot></slot>` element (Vue/Svelte). | ||
#### HTML example | ||
To manage reactivity with the HTML storybook your decorator must return an array containing two elements : | ||
* the HTML element to display in the story | ||
* An update callback that will be called when the theme change. Like the decorator, the callback will receive the same props (without `children`). | ||
Example of a customized decorator that use a CSS file for changing the theme: | ||
```js | ||
import { withThemes } from 'storybook-addon-themes/react'; | ||
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 example | ||
Same example as above for React: | ||
```js | ||
function Decorator(props) { | ||
const { children, themeName } = props; | ||
@@ -265,17 +364,2 @@ return ( | ||
}; | ||
export default { | ||
title: 'CSF|Button', | ||
component: Button, | ||
decorators: [ withThemes ], | ||
parameters: { | ||
themes: { | ||
Decorator, | ||
list: [ | ||
{ name: 'twitter', color: '#00aced', default: true }, | ||
{ name: 'facebook', color: '#3b5998' }, | ||
], | ||
}, | ||
}, | ||
}; | ||
``` | ||
@@ -288,2 +372,2 @@ | ||
|Usage without decorator |+| |+|+|+|+|+|+|+|+|+|+| | ||
|Usage with decorator |+| |+| | | | | |+| | | | | ||
|Usage with decorator |+| |+| | | |+| |+| | | | |
@@ -8,3 +8,3 @@ import { Theme } from '../models'; | ||
: theme.class | ||
: undefined; | ||
: ''; | ||
} |
@@ -26,3 +26,3 @@ import addons from '@storybook/addons'; | ||
return { | ||
themeName: (lastValue && lastValue[0]) || getSelectedThemeName(this.config.list) | ||
themeName: (lastValue && lastValue[0]) || getSelectedThemeName(this.config.list, this.config.default) | ||
}; | ||
@@ -29,0 +29,0 @@ }, |
@@ -7,4 +7,6 @@ import { Decorator } from './Decorator'; | ||
Decorator?: Decorator, | ||
default?: string, | ||
list: Theme[], | ||
onChange?: (themeName: Theme) => void | ||
onChange?: (themeName: Theme) => void, | ||
target?: string | ||
} |
@@ -20,2 +20,3 @@ import { API } from '@storybook/api'; | ||
: parameters; | ||
return { | ||
@@ -27,3 +28,3 @@ ...defaultOptions, | ||
export function getSelectedThemeName(list: Theme[], currentSelectedValue?: string): string { | ||
export function getSelectedThemeName(list: Theme[], defaultTheme?: string, currentSelectedValue?: string): string { | ||
if (!list.length) { | ||
@@ -41,2 +42,6 @@ return 'none'; | ||
if (defaultTheme) { | ||
return defaultTheme; | ||
} | ||
if (list.find(i => i.default)) { | ||
@@ -47,3 +52,3 @@ return list.find(i => i.default).name; | ||
return 'none'; | ||
}; | ||
} | ||
@@ -50,0 +55,0 @@ export function getSelectedTheme(list: Theme[], themeName: string): Theme { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
221653
84
1731
350
1