mirror of
https://github.com/jlengrand/open-wc.git
synced 2026-03-10 08:31:19 +00:00
feat: add demoing storybook
This commit is contained in:
@@ -1 +1 @@
|
||||
../../packages/storybook/README.md
|
||||
../../packages/demoing-storybook/README.md
|
||||
21
packages/demoing-storybook/LICENSE
Normal file
21
packages/demoing-storybook/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Open Web Components
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
173
packages/demoing-storybook/README.md
Normal file
173
packages/demoing-storybook/README.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# Demoing via storybook
|
||||
|
||||
[//]: # (AUTO INSERT HEADER PREPUBLISH)
|
||||
|
||||
For demoing and showcasing different states of your Web Component, we recommend using [storybook](https://storybook.js.org/).
|
||||
|
||||
::: tip
|
||||
This is part of the default [open-wc](https://open-wc.org/) recommendation
|
||||
:::
|
||||
|
||||
# Features
|
||||
- Create API documentation/playground
|
||||
- Show documentation (from markdown) with your element
|
||||
- Show your source code to copy/paste
|
||||
- Helper to setup transpilation for dependencies
|
||||
- Setup that works down to IE11
|
||||
|
||||
|
||||
## Setup
|
||||
```bash
|
||||
npm init open-wc demoing
|
||||
```
|
||||
|
||||
### Manual
|
||||
- `yarn add @open-wc/demoing-storybook --dev`
|
||||
- Copy at minimum the [.storybook](https://github.com/daKmoR/create-open-wc/tree/master/src/generators/demoing-storybook/templates/static/.storybook) folder to `.storybook`
|
||||
- If you want to bring along the examples, you may also copy the `stories` folder.
|
||||
- Add the following scripts to your package.json
|
||||
```js
|
||||
"scripts": {
|
||||
"storybook": "start-storybook -p 9001",
|
||||
"storybook:build": "build-storybook -o _site",
|
||||
"site:build": "npm run storybook:build",
|
||||
},
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Create stories within the `stories` folder.
|
||||
|
||||
```bash
|
||||
npm run storybook
|
||||
```
|
||||
|
||||
### Create a Story
|
||||
|
||||
Create an `*.story.js` (for example `index.stories.js`) file within the `stories` folder.
|
||||
|
||||
```js
|
||||
import {
|
||||
storiesOf,
|
||||
html,
|
||||
} from '@open-wc/demoing-storybook';
|
||||
|
||||
storiesOf('Demo|Example Element', module)
|
||||
.add(
|
||||
'Alternative Header',
|
||||
() => html`
|
||||
<my-el .header=${'Something else'}></my-el>
|
||||
`,
|
||||
);
|
||||
```
|
||||
|
||||
### Create API documentation/playground
|
||||
|
||||
If you have a single element then we can fully generate a usable api documentation/playground.
|
||||
The data will be read from the elements properties.
|
||||
So this should probably be your default story to gives users documentation, api and playground all in one.
|
||||
For additional edge cases you should add more stories.
|
||||
|
||||
Note: you need to provide the Class (not a node or a template)
|
||||
|
||||
```js
|
||||
import { storiesOf, withKnobs, withClassPropertiesKnobs } from '@open-wc/demoing-storybook';
|
||||
|
||||
storiesOf('Demo|Example Element', module)
|
||||
.addDecorator(withKnobs)
|
||||
.add(
|
||||
'Documentation',
|
||||
() => withClassPropertiesKnobs(MyEl),
|
||||
{ notes: { markdown: notes } },
|
||||
)
|
||||
```
|
||||
|
||||
So with a configuration like this you will get this auto generated.
|
||||
|
||||
<img src="https://raw.githubusercontent.com/open-wc/open-wc/master/packages/demoing-storybook/dev_assets/storybook.gif" alt="storybook demo animation" />
|
||||
|
||||
|
||||
For most types this works fine out of the box but if want to provide better knobs you can do so by overriding.
|
||||
```js
|
||||
() => withClassPropertiesKnobs(MyEl, {
|
||||
overrides: el => [
|
||||
// show a color selector
|
||||
{ key: 'headerColor', fn: () => color('headerColor', el.headerColor, 'Element') },
|
||||
// show dropdown
|
||||
{
|
||||
key: 'type',
|
||||
fn: () => select('type', ['small', 'medium', 'large'], el.type, 'Element'),
|
||||
},
|
||||
// show textarea where you can input json
|
||||
{ key: 'complexItems', fn: () => object('complexItems', el.complexItems, 'Inherited') },
|
||||
// move property to a different group
|
||||
{ key: 'locked', group: 'Security' },
|
||||
],
|
||||
}),
|
||||
```
|
||||
|
||||
By default it will create one simple node from the given Class.
|
||||
However for a nicer demo it may be needed to set properties or add more lightdom.
|
||||
You can do so by providing a template.
|
||||
```js
|
||||
() => withClassPropertiesKnobs(MyEl, {
|
||||
template: html`
|
||||
<my-el .header=${'override it'}><p>foo</p></my-el>
|
||||
`,
|
||||
}),
|
||||
```
|
||||
|
||||
### Show documentation (from markdown) with your element
|
||||
|
||||
The documentation will be visible when clicking on "notes" at the top menu.
|
||||
|
||||
```js
|
||||
import notes from '../README.md';
|
||||
|
||||
.add(
|
||||
'Documentation',
|
||||
() => html`
|
||||
<my-el></my-el>
|
||||
`,
|
||||
{ notes: { markdown: notes } },
|
||||
)
|
||||
```
|
||||
|
||||
### Helper to setup transpilation for dependencies
|
||||
|
||||
Whenever you add a dependency that is written in ES modules you will need to enable transpilation for it.
|
||||
Open your configuration and add the new package to the array.
|
||||
Below you see the default settings.
|
||||
|
||||
```js
|
||||
const defaultConfig = require('@open-wc/demoing-storybook/default-storybook-webpack-config.js');
|
||||
|
||||
module.exports = ({ config }) => {
|
||||
return defaultConfig({ config, transpilePackages: ['lit-html', 'lit-element', '@open-wc'] });
|
||||
};
|
||||
```
|
||||
|
||||
### Additional middleware config like a proxy
|
||||
If you need additional configuration for the storybook dev server you can provide them via a config file `.storybook/middleware.js`.
|
||||
```js
|
||||
// example for a proxy middleware to use an api for fetching data to display
|
||||
const proxy = require('http-proxy-middleware');
|
||||
|
||||
module.exports = function(app) {
|
||||
app.use(proxy('/api/', {
|
||||
target: 'http://localhost:9010/',
|
||||
}));
|
||||
};
|
||||
```
|
||||
|
||||
<script>
|
||||
export default {
|
||||
mounted() {
|
||||
const editLink = document.querySelector('.edit-link a');
|
||||
if (editLink) {
|
||||
const url = editLink.href;
|
||||
editLink.href = url.substr(0, url.indexOf('/master/')) + '/master/packages/building-storybook/README.md';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,33 @@
|
||||
module.exports = ({ config, transpilePackages = ['lit-html', 'lit-element', '@open-wc'] }) => {
|
||||
config.module.rules.push({
|
||||
test: [/\.stories\.js$/, /index\.js$/],
|
||||
loaders: [require.resolve('@storybook/addon-storysource/loader')],
|
||||
enforce: 'pre',
|
||||
});
|
||||
|
||||
// this is a separate config for only those packages
|
||||
// the main storybook will use the .babelrc which is needed so storybook itself works in IE
|
||||
config.module.rules.push({
|
||||
test: new RegExp(`node_modules(\\/|\\\\)(${transpilePackages.join('|')})(.*)\\.js$`),
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
plugins: [
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
'@babel/plugin-proposal-object-rest-spread',
|
||||
],
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
useBuiltIns: 'entry',
|
||||
},
|
||||
],
|
||||
],
|
||||
babelrc: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return config;
|
||||
};
|
||||
14
packages/demoing-storybook/demo/.storybook/.babelrc
Normal file
14
packages/demoing-storybook/demo/.storybook/.babelrc
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"plugins": [
|
||||
"@babel/plugin-syntax-dynamic-import",
|
||||
"@babel/plugin-proposal-object-rest-spread"
|
||||
],
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"useBuiltIns": "entry"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
6
packages/demoing-storybook/demo/.storybook/addons.js
Normal file
6
packages/demoing-storybook/demo/.storybook/addons.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import '@storybook/addon-storysource/register';
|
||||
import '@storybook/addon-actions/register';
|
||||
import '@storybook/addon-notes/register';
|
||||
import '@storybook/addon-knobs/register';
|
||||
import '@storybook/addon-links/register';
|
||||
import '@storybook/addon-viewport/register';
|
||||
9
packages/demoing-storybook/demo/.storybook/config.js
Normal file
9
packages/demoing-storybook/demo/.storybook/config.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import { configure } from '@storybook/polymer';
|
||||
import '@storybook/addon-console';
|
||||
|
||||
const req = require.context('../stories', true, /\.stories\.js$/);
|
||||
function loadStories() {
|
||||
req.keys().forEach(filename => req(filename));
|
||||
}
|
||||
|
||||
configure(loadStories, module);
|
||||
@@ -0,0 +1,5 @@
|
||||
const defaultConfig = require('../../default-storybook-webpack-config.js');
|
||||
|
||||
module.exports = ({ config }) => {
|
||||
return defaultConfig({ config, transpilePackages: ['lit-html', 'lit-element', '@open-wc'] });
|
||||
};
|
||||
38
packages/demoing-storybook/demo/README.md
Normal file
38
packages/demoing-storybook/demo/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Example Element
|
||||
|
||||
<style>
|
||||
iframe {
|
||||
width: 100%;
|
||||
border: 1px solid rgba(0,0,0,.1);
|
||||
min-height: 300px;
|
||||
}
|
||||
</style>
|
||||
|
||||
This example element display some information about...
|
||||
|
||||
## Features:
|
||||
- bigger then you would expect
|
||||
- still fast
|
||||
- awesome to learn something
|
||||
|
||||
## How to use
|
||||
|
||||
### Installation
|
||||
```bash
|
||||
yarn add @foo/my-el
|
||||
```
|
||||
|
||||
```js
|
||||
import '@foo/my-el/my-el.js';
|
||||
```
|
||||
|
||||
### Example:
|
||||
|
||||
<iframe src="iframe.html?id=demo-example-element--documentation"></iframe>
|
||||
```html
|
||||
<my-el></my-el>
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
Please look at "Canvas" and select "Knobs" in the addon bar (usually to the right) to see all available properties.
|
||||
124
packages/demoing-storybook/demo/my-el.js
Normal file
124
packages/demoing-storybook/demo/my-el.js
Normal file
@@ -0,0 +1,124 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { LitElement, html, css } from 'lit-element';
|
||||
|
||||
class Base extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
complexOption: { type: Object },
|
||||
complexItems: { type: Array },
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.complexOption = { foo: 'is foo', bar: 'is bar' };
|
||||
this.complexItems = [
|
||||
{ name: 'foo', age: 1000, message: 'knows all' },
|
||||
{ name: 'bar', age: 1, message: 'is new here' },
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export class MyEl extends Base {
|
||||
static get styles() {
|
||||
return css`
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
h2 {
|
||||
color: var(--my-el-header-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
header: { type: String },
|
||||
headerColor: { type: String },
|
||||
locked: { type: Boolean },
|
||||
items: { type: Array },
|
||||
type: { type: String },
|
||||
time: { type: Date },
|
||||
age: { type: Number },
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.header = 'Default Header';
|
||||
this.headerColor = '#ff0000';
|
||||
this.disabled = false;
|
||||
this.items = ['A', 'B', 'C'];
|
||||
this.type = 'medium';
|
||||
this.age = 10;
|
||||
this.time = new Date();
|
||||
// time needs to stay the same so storybook knows can work with them
|
||||
this.time.setHours(0, 0, 0, 0);
|
||||
this.complexOption = { foo: 'is foo', bar: 'is bar' };
|
||||
this.complexItems = [
|
||||
{ name: 'foo', age: 1000, message: 'knows all' },
|
||||
{ name: 'bar', age: 1, message: 'is new here' },
|
||||
];
|
||||
}
|
||||
|
||||
update(oldValues) {
|
||||
super.update(oldValues);
|
||||
if (oldValues.has('type')) {
|
||||
const { type } = this;
|
||||
if (!(type === 'small' || type === 'medium' || type === 'large')) {
|
||||
throw new Error('Type needs to be either small, medium or large');
|
||||
}
|
||||
}
|
||||
if (oldValues.has('headerColor')) {
|
||||
this.style.setProperty('--my-el-header-color', this.headerColor);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<h2>${this.header}</h2>
|
||||
<p>My size is: ${this.type}.</p>
|
||||
<p>I am ${this.locked ? 'locked' : 'free'} since ${this.age} years.</p>
|
||||
<p>Time: ${this.time.toDateString()}</p>
|
||||
<p>List:</p>
|
||||
<ul>
|
||||
${this.items.map(
|
||||
item =>
|
||||
html`
|
||||
<li>${item}</li>
|
||||
`,
|
||||
)}
|
||||
</ul>
|
||||
<p>This is the light dom:</p>
|
||||
<slot></slot>
|
||||
<p>Complex Option:</p>
|
||||
<pre>${JSON.stringify(this.complexOption, null, 2)}</pre>
|
||||
<p>Complex List:</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Age</th>
|
||||
<th>Message</th>
|
||||
</tr>
|
||||
${this.complexItems.map(
|
||||
item =>
|
||||
html`
|
||||
<tr>
|
||||
<td>${item.name}</td>
|
||||
<td>${item.age}</td>
|
||||
<td>${item.message}</td>
|
||||
</tr>
|
||||
`,
|
||||
)}
|
||||
</table>
|
||||
|
||||
<button @click=${() => console.log('my-el button clicked', this)}>
|
||||
log me to Actions/console
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('my-el', MyEl);
|
||||
48
packages/demoing-storybook/demo/stories/index.stories.js
Normal file
48
packages/demoing-storybook/demo/stories/index.stories.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import {
|
||||
storiesOf,
|
||||
html,
|
||||
withKnobs,
|
||||
select,
|
||||
object,
|
||||
color,
|
||||
addParameters,
|
||||
withClassPropertiesKnobs,
|
||||
} from '../../index.js';
|
||||
|
||||
import { MyEl } from '../my-el.js';
|
||||
import notes from '../README.md';
|
||||
|
||||
addParameters({
|
||||
options: {
|
||||
isFullscreen: false,
|
||||
panelPosition: 'right',
|
||||
},
|
||||
});
|
||||
|
||||
storiesOf('Demo|Example Element', module)
|
||||
.addDecorator(withKnobs)
|
||||
.add(
|
||||
'Documentation',
|
||||
() =>
|
||||
withClassPropertiesKnobs(MyEl, {
|
||||
overrides: el => [
|
||||
{ key: 'headerColor', fn: () => color('headerColor', el.headerColor, 'Element') },
|
||||
{
|
||||
key: 'type',
|
||||
fn: () => select('type', ['small', 'medium', 'large'], el.type, 'Element'),
|
||||
},
|
||||
{ key: 'complexItems', fn: () => object('complexItems', el.complexItems, 'Inherited') },
|
||||
{ key: 'locked', group: 'Security' },
|
||||
],
|
||||
template: html`
|
||||
<my-el><p>foo</p></my-el>
|
||||
`,
|
||||
}),
|
||||
{ notes: { markdown: notes } },
|
||||
)
|
||||
.add(
|
||||
'Alternative Header',
|
||||
() => html`
|
||||
<my-el .header=${'Something else'}></my-el>
|
||||
`,
|
||||
);
|
||||
BIN
packages/demoing-storybook/dev_assets/storybook.gif
Normal file
BIN
packages/demoing-storybook/dev_assets/storybook.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
23
packages/demoing-storybook/index.d.ts
vendored
Normal file
23
packages/demoing-storybook/index.d.ts
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
export { html } from 'lit-html';
|
||||
|
||||
// NB: @types/storybook__polymer doesn't yet exist
|
||||
// export { storiesOf, addParameters } from '@storybook/polymer';
|
||||
export { action } from '@storybook/addon-actions';
|
||||
export { linkTo } from '@storybook/addon-links';
|
||||
// NB: @types/storybook__addon-backgrounds is 2 major versions behind
|
||||
// export { withBackgrounds } from '@storybook/addon-backgrounds';
|
||||
export { withNotes } from '@storybook/addon-notes';
|
||||
|
||||
export {
|
||||
withKnobs,
|
||||
text,
|
||||
button,
|
||||
number,
|
||||
select,
|
||||
date,
|
||||
color,
|
||||
array,
|
||||
boolean,
|
||||
} from '@storybook/addon-knobs';
|
||||
|
||||
export { withClassPropertiesKnobs } from './withClassPropertiesKnobs.js';
|
||||
21
packages/demoing-storybook/index.js
Normal file
21
packages/demoing-storybook/index.js
Normal file
@@ -0,0 +1,21 @@
|
||||
export { html } from 'lit-html';
|
||||
|
||||
export { storiesOf, addParameters } from '@storybook/polymer';
|
||||
export { action } from '@storybook/addon-actions';
|
||||
export { linkTo } from '@storybook/addon-links';
|
||||
export { withNotes } from '@storybook/addon-notes';
|
||||
export { document } from 'global';
|
||||
export {
|
||||
withKnobs,
|
||||
text,
|
||||
button,
|
||||
number,
|
||||
select,
|
||||
date,
|
||||
object,
|
||||
color,
|
||||
array,
|
||||
boolean,
|
||||
} from '@storybook/addon-knobs';
|
||||
|
||||
export { withClassPropertiesKnobs } from './withClassPropertiesKnobs.js';
|
||||
55
packages/demoing-storybook/package.json
Normal file
55
packages/demoing-storybook/package.json
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "@open-wc/demoing-storybook",
|
||||
"version": "0.0.1",
|
||||
"description": "Storybook configuration following open-wc recommendations",
|
||||
"author": "open-wc",
|
||||
"homepage": "https://github.com/open-wc/open-wc/",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/open-wc/open-wc.git",
|
||||
"directory": "packages/building-storybook"
|
||||
},
|
||||
"scripts": {
|
||||
"storybook": "start-storybook -p 9001 -c demo/.storybook",
|
||||
"prepublishOnly": "../../scripts/insert-header.js"
|
||||
},
|
||||
"main": "index.js",
|
||||
"files": [
|
||||
"*.js",
|
||||
"*.ts"
|
||||
],
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@storybook/addon-actions": "^5.0.0-rc.8",
|
||||
"@storybook/addon-console": "^1.1.0",
|
||||
"@storybook/addon-knobs": "^5.0.0-rc.8",
|
||||
"@storybook/addon-links": "^5.0.0-rc.8",
|
||||
"@storybook/addon-notes": "^5.0.0-rc.8",
|
||||
"@storybook/addon-storysource": "^5.0.0-rc.8",
|
||||
"@storybook/addon-viewport": "^5.0.0-rc.8",
|
||||
"@storybook/polymer": "^5.0.0-rc.8",
|
||||
"@types/storybook__addon-actions": "^3.4.1",
|
||||
"@types/storybook__addon-backgrounds": "^3.2.1",
|
||||
"@types/storybook__addon-knobs": "^3.4.1",
|
||||
"@types/storybook__addon-links": "^3.3.3",
|
||||
"@types/storybook__addon-notes": "^3.3.3",
|
||||
"@webcomponents/webcomponentsjs": "^2.0.0",
|
||||
"babel-loader": "^8.0.0",
|
||||
"moment": "^2.0.0",
|
||||
"polymer-webpack-loader": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"lit-html": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"lit-element": "^2.0.1",
|
||||
"lit-html": "^1.0.0"
|
||||
}
|
||||
}
|
||||
82
packages/demoing-storybook/withClassPropertiesKnobs.js
Normal file
82
packages/demoing-storybook/withClassPropertiesKnobs.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import { text, number, date, object, array, boolean } from '@storybook/addon-knobs';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { render } from 'lit-html';
|
||||
|
||||
/**
|
||||
* @example
|
||||
* class MyEl extends LitElement { ... }
|
||||
*
|
||||
* .add('Playground', () => {
|
||||
* return withClassPropertiesKnobs(MyEl);
|
||||
* });
|
||||
*
|
||||
* @example
|
||||
* .add('Playground', () => {
|
||||
* return withClassPropertiesKnobs(MyEl, el => ([
|
||||
* { key: 'type', fn: () => select('type', ['small', 'medium', 'large'], el.type, 'Element') },
|
||||
* { key: 'complexItems', fn: () => object('complexItems', el.complexItems, 'Inherited') },
|
||||
* { key: 'locked', group: 'Security' }, // change group of an default Element property
|
||||
* ]));
|
||||
* });
|
||||
*/
|
||||
export function withClassPropertiesKnobs(Klass, { overrides: overrideFunction, template } = {}) {
|
||||
let el;
|
||||
if (template) {
|
||||
const wrapper = document.createElement('div');
|
||||
render(template, wrapper);
|
||||
el = wrapper.children[0]; // eslint-disable-line prefer-destructuring
|
||||
} else {
|
||||
el = new Klass();
|
||||
}
|
||||
|
||||
const overrides = overrideFunction ? overrideFunction(el) : [];
|
||||
|
||||
const elProperties = Object.keys(Klass.properties);
|
||||
const properties = Array.from(elProperties);
|
||||
if (Klass._classProperties) {
|
||||
Array.from(Klass._classProperties.keys()).forEach(propName => {
|
||||
if (!elProperties.includes(propName)) {
|
||||
properties.push(propName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
properties.forEach(propName => {
|
||||
const override = overrides.find(item => item.key === propName);
|
||||
if (override && override.fn) {
|
||||
el[propName] = override.fn();
|
||||
} else {
|
||||
const isElProperty = elProperties.includes(propName);
|
||||
let group = isElProperty ? 'Element' : 'Inherited';
|
||||
if (override && override.group) {
|
||||
group = override.group; // eslint-disable-line prefer-destructuring
|
||||
}
|
||||
const prop = isElProperty ? Klass.properties[propName] : Klass._classProperties.get(propName);
|
||||
if (prop.type.name) {
|
||||
// let method = false;
|
||||
switch (prop.type.name) {
|
||||
case 'String':
|
||||
el[propName] = text(propName, el[propName], group);
|
||||
break;
|
||||
case 'Number':
|
||||
el[propName] = number(propName, el[propName], {}, group);
|
||||
break;
|
||||
case 'Array':
|
||||
el[propName] = array(propName, el[propName], ',', group);
|
||||
break;
|
||||
case 'Boolean':
|
||||
el[propName] = boolean(propName, el[propName], group);
|
||||
break;
|
||||
case 'Object':
|
||||
el[propName] = object(propName, el[propName], group);
|
||||
break;
|
||||
case 'Date':
|
||||
el[propName] = new Date(date(propName, el[propName], group));
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return el;
|
||||
}
|
||||
Reference in New Issue
Block a user