Skip to content

Commit

Permalink
Merge pull request #1832 from storybooks/ndelangen/restructure-knobs
Browse files Browse the repository at this point in the history
Refactor knobs - no longer include all runtimes
  • Loading branch information
ndelangen authored Sep 11, 2017
2 parents 65d1dda + 8c23357 commit 18f5e34
Show file tree
Hide file tree
Showing 12 changed files with 176 additions and 82 deletions.
59 changes: 40 additions & 19 deletions addons/knobs/README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
# Storybook Addon Knobs

Storybook Addon Knobs allow you to edit React props dynamically using the Storybook UI.
You can also use Knobs as a dynamic variable inside stories in [Storybook](https://storybook.js.org).

[![Greenkeeper badge](https://badges.greenkeeper.io/storybooks/storybook.svg)](https://greenkeeper.io/)
[![Build Status](https://travis-ci.org/storybooks/storybook.svg?branch=master)](https://travis-ci.org/storybooks/storybook)
[![Build Status on CircleCI](https://circleci.com/gh/storybooks/storybook.svg?style=shield)](https://circleci.com/gh/storybooks/storybook)
[![CodeFactor](https://www.codefactor.io/repository/github/storybooks/storybook/badge)](https://www.codefactor.io/repository/github/storybooks/storybook)
[![Known Vulnerabilities](https://snyk.io/test/github/storybooks/storybook/8f36abfd6697e58cd76df3526b52e4b9dc894847/badge.svg)](https://snyk.io/test/github/storybooks/storybook/8f36abfd6697e58cd76df3526b52e4b9dc894847)
[![BCH compliance](https://bettercodehub.com/edge/badge/storybooks/storybook)](https://bettercodehub.com/results/storybooks/storybook) [![codecov](https://codecov.io/gh/storybooks/storybook/branch/master/graph/badge.svg)](https://codecov.io/gh/storybooks/storybook)
[![Storybook Slack](https://storybooks-slackin.herokuapp.com/badge.svg)](https://storybooks-slackin.herokuapp.com/)

Storybook Addon Knobs allow you to edit React props dynamically using the Storybook UI.
You can also use Knobs as a dynamic variable inside stories in [Storybook](https://storybook.js.org).
[![BCH compliance](https://bettercodehub.com/edge/badge/storybooks/storybook)](https://bettercodehub.com/results/storybooks/storybook) [![codecov](https://codecov.io/gh/storybooks/storybook/branch/master/graph/badge.svg)](https://codecov.io/gh/storybooks/storybook)
[![Storybook Slack](https://now-examples-slackin-nqnzoygycp.now.sh/badge.svg)](https://now-examples-slackin-nqnzoygycp.now.sh/)
[![Backers on Open Collective](https://opencollective.com/storybook/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/storybook/sponsors/badge.svg)](#sponsors)

This addon works with Storybook for:
[React](https://github.com/storybooks/storybook/tree/master/app/react).
[React Native](https://github.com/storybooks/storybook/tree/master/app/react-native).
[Vue](https://github.com/storybooks/storybook/tree/master/app/vue).

This is how Knobs look like:

Expand All @@ -37,7 +40,7 @@ Now, write your stories with knobs.

```js
import { storiesOf } from '@storybook/react';
import { withKnobs, text, boolean, number } from '@storybook/addon-knobs';
import { withKnobs, text, boolean, number } from '@storybook/addon-knobs/react';

const stories = storiesOf('Storybook Knobs', module);

Expand All @@ -50,7 +53,7 @@ stories.add('with a button', () => (
<button disabled={boolean('Disabled', false)} >
{text('Label', 'Hello Button')}
</button>
))
));

// Knobs as dynamic variables.
stories.add('as dynamic variables', () => {
Expand All @@ -62,6 +65,20 @@ stories.add('as dynamic variables', () => {
});
```

> In the case of Vue, use these imports:
>
> ```js
> import { storiesOf } from '@storybook/vue';
> import { withKnobs, text, boolean, number } from '@storybook/addon-knobs/vue';
> ```
>
> In the case of React-Native, use these imports:
>
> ```js
> import { storiesOf } from '@storybook/react-native';
> import { withKnobs, text, boolean, number } from '@storybook/addon-knobs/react';
> ```
You can see your Knobs in a Storybook panel as shown below.
![](docs/demo.png)
Expand Down Expand Up @@ -89,7 +106,7 @@ Just like that, you can import any other following Knobs:
Allows you to get some text from the user.

```js
import { text } from '@storybook/addon-knobs';
import { text } from '@storybook/addon-knobs/react';

const label = 'Your Name';
const defaultValue = 'Arunoda Susiripala';
Expand All @@ -102,7 +119,7 @@ const value = text(label, defaultValue);
Allows you to get a boolean value from the user.

```js
import { boolean } from '@storybook/addon-knobs';
import { boolean } from '@storybook/addon-knobs/react';

const label = 'Agree?';
const defaultValue = false;
Expand All @@ -115,7 +132,7 @@ const value = boolean(label, defaultValue);
Allows you to get a number from the user.

```js
import { number } from '@storybook/addon-knobs';
import { number } from '@storybook/addon-knobs/react';

const label = 'Age';
const defaultValue = 78;
Expand All @@ -128,7 +145,7 @@ const value = number(label, defaultValue);
Allows you to get a number from the user using a range slider.

```js
import { number } from '@storybook/addon-knobs';
import { number } from '@storybook/addon-knobs/react';

const label = 'Temperature';
const defaultValue = 73;
Expand All @@ -147,7 +164,7 @@ const value = number(label, defaultValue, options);
Allows you to get a colour from the user.

```js
import { color } from '@storybook/addon-knobs';
import { color } from '@storybook/addon-knobs/react';

const label = 'Color';
const defaultValue = '#ff00ff';
Expand All @@ -160,7 +177,7 @@ const value = color(label, defaultValue);
Allows you to get a JSON object or array from the user.

```js
import { object } from '@storybook/addon-knobs';
import { object } from '@storybook/addon-knobs/react';

const label = 'Styles';
const defaultValue = {
Expand All @@ -177,7 +194,7 @@ const value = object(label, defaultValue);
Allows you to get an array of strings from the user.

```js
import { array } from '@storybook/addon-knobs';
import { array } from '@storybook/addon-knobs/react';

const label = 'Styles';
const defaultValue = ['Red']
Expand All @@ -189,7 +206,7 @@ const value = array(label, defaultValue);
> By default it's a comma, but this can be override by passing a separator variable.
>
> ```js
> import { array } from '@storybook/addon-knobs';
> import { array } from '@storybook/addon-knobs/react';
>
> const label = 'Styles';
> const defaultValue = ['Red'];
Expand All @@ -202,7 +219,7 @@ const value = array(label, defaultValue);
Allows you to get a value from a select box from the user.
```js
import { select } from '@storybook/addon-knobs';
import { select } from '@storybook/addon-knobs/react';
const label = 'Colors';
const options = {
Expand All @@ -222,7 +239,7 @@ const value = select(label, options, defaultValue);
Allow you to get date (and time) from the user.

```js
import { date } from '@storybook/addon-knobs';
import { date } from '@storybook/addon-knobs/react';

const label = 'Event Date';
const defaultValue = new Date('Jan 20 2017');
Expand All @@ -237,7 +254,11 @@ If you feel like this addon is not performing well enough there is an option to
Usage:

```js
story.addDecorator(withKnobsOptions({
import { storiesOf } from '@storybook/react';

const stories = storiesOf('Storybook Knobs', module);

stories.addDecorator(withKnobsOptions({
debounce: { wait: number, leading: boolean}, // Same as lodash debounce.
timestamps: true // Doesn't emit events while user is typing.
}));
Expand Down
1 change: 1 addition & 0 deletions addons/knobs/react.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./dist/react');
55 changes: 55 additions & 0 deletions addons/knobs/src/base.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import KnobManager from './KnobManager';

export const manager = new KnobManager();

export function knob(name, options) {
return manager.knob(name, options);
}

export function text(name, value) {
return manager.knob(name, { type: 'text', value });
}

export function boolean(name, value) {
return manager.knob(name, { type: 'boolean', value });
}

export function number(name, value, options = {}) {
const defaults = {
range: false,
min: 0,
max: 10,
step: 1,
};

const mergedOptions = { ...defaults, ...options };

const finalOptions = {
...mergedOptions,
type: 'number',
value,
};

return manager.knob(name, finalOptions);
}

export function color(name, value) {
return manager.knob(name, { type: 'color', value });
}

export function object(name, value) {
return manager.knob(name, { type: 'object', value });
}

export function select(name, options, value) {
return manager.knob(name, { type: 'select', options, value });
}

export function array(name, value, separator = ',') {
return manager.knob(name, { type: 'array', value, separator });
}

export function date(name, value = new Date()) {
const proxyValue = value ? value.getTime() : null;
return manager.knob(name, { type: 'date', value: proxyValue });
}
65 changes: 10 additions & 55 deletions addons/knobs/src/index.js
Original file line number Diff line number Diff line change
@@ -1,66 +1,21 @@
import { window } from 'global';
import deprecate from 'util-deprecate';
import addons from '@storybook/addons';
import KnobManager from './KnobManager';

import { vueHandler } from './vue';
import { reactHandler } from './react';

const manager = new KnobManager();

export function knob(name, options) {
return manager.knob(name, options);
}

export function text(name, value) {
return manager.knob(name, { type: 'text', value });
}

export function boolean(name, value) {
return manager.knob(name, { type: 'boolean', value });
}

export function number(name, value, options = {}) {
const defaults = {
range: false,
min: 0,
max: 10,
step: 1,
};

const mergedOptions = { ...defaults, ...options };

const finalOptions = {
...mergedOptions,
type: 'number',
value,
};
import { knob, text, boolean, number, color, object, array, date, manager } from './base';

return manager.knob(name, finalOptions);
}

export function color(name, value) {
return manager.knob(name, { type: 'color', value });
}

export function object(name, value) {
return manager.knob(name, { type: 'object', value });
}
export { knob, text, boolean, number, color, object, array, date };

export function select(name, options, value) {
return manager.knob(name, { type: 'select', options, value });
}

export function array(name, value, separator = ',') {
return manager.knob(name, { type: 'array', value, separator });
}

export function date(name, value = new Date()) {
const proxyValue = value ? value.getTime() : null;
return manager.knob(name, { type: 'date', value: proxyValue });
}
deprecate(
() => {},
'Using @storybook/addon-knobs directly is discouraged, please use @storybook/addon-knobs/{{framework}}'
);

// "Higher order component" / wrapper style API
// In 3.3, this will become `withKnobs`, once our decorator API supports it.
// See https://github.com/storybooks/storybook/pull/1527
// generic higher-order component decorator for all platforms - usage is discouraged
// This file Should be removed with 4.0 release
function wrapperKnobs(options) {
const channel = addons.getChannel();
manager.setChannel(channel);
Expand Down
26 changes: 23 additions & 3 deletions addons/knobs/src/react/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
import React from 'react';
import addons from '@storybook/addons';

import WrapStory from './WrapStory';

/**
* Handles a react story
*/
import { knob, text, boolean, number, color, object, array, date, select, manager } from '../base';

export { knob, text, boolean, number, color, object, array, date, select };

export const reactHandler = (channel, knobStore) => getStory => context => {
const initialContent = getStory(context);
const props = { context, storyFn: getStory, channel, knobStore, initialContent };
return <WrapStory {...props} />;
};

function wrapperKnobs(options) {
const channel = addons.getChannel();
manager.setChannel(channel);

if (options) channel.emit('addon:knobs:setOptions', options);

return reactHandler(channel, manager.knobStore);
}

export function withKnobs(storyFn, context) {
return wrapperKnobs()(storyFn)(context);
}

export function withKnobsOptions(options = {}) {
return (storyFn, context) => wrapperKnobs(options)(storyFn)(context);
}
23 changes: 23 additions & 0 deletions addons/knobs/src/vue/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import addons from '@storybook/addons';

import { knob, text, boolean, number, color, object, array, date, select, manager } from '../base';

export { knob, text, boolean, number, color, object, array, date, select };

export const vueHandler = (channel, knobStore) => getStory => context => ({
render(h) {
return h(getStory(context));
Expand Down Expand Up @@ -35,3 +41,20 @@ export const vueHandler = (channel, knobStore) => getStory => context => ({
knobStore.unsubscribe(this.setPaneKnobs);
},
});

function wrapperKnobs(options) {
const channel = addons.getChannel();
manager.setChannel(channel);

if (options) channel.emit('addon:knobs:setOptions', options);

return vueHandler(channel, manager.knobStore);
}

export function withKnobs(storyFn, context) {
return wrapperKnobs()(storyFn)(context);
}

export function withKnobsOptions(options = {}) {
return (storyFn, context) => wrapperKnobs(options)(storyFn)(context);
}
1 change: 1 addition & 0 deletions addons/knobs/vue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./dist/vue');
2 changes: 1 addition & 1 deletion examples/cra-kitchen-sink/src/stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
array,
date,
object,
} from '@storybook/addon-knobs';
} from '@storybook/addon-knobs/react';
import centered from '@storybook/addon-centered';
import { withInfo } from '@storybook/addon-info';

Expand Down
11 changes: 10 additions & 1 deletion examples/crna-kitchen-sink/storybook/stories/Knobs/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import React from 'react';
import { View, Text } from 'react-native';

import { text, number, boolean, color, select, array, date, object } from '@storybook/addon-knobs';
import {
text,
number,
boolean,
color,
select,
array,
date,
object,
} from '@storybook/addon-knobs/react';

export default () => {
const name = text('Name', 'Storyteller');
Expand Down
Loading

0 comments on commit 18f5e34

Please sign in to comment.