diff --git a/addons/knobs/README.md b/addons/knobs/README.md
index 4c3788d9d0ae..8869ab26ef48 100644
--- a/addons/knobs/README.md
+++ b/addons/knobs/README.md
@@ -231,6 +231,18 @@ const value = date(label, defaultValue);
> Note: the default value must not change - e.g., do not do `date('Label', new Date())` or `date('Label')`
+### button
+
+Allows you to include a button and associated handler.
+
+```js
+import { button } from '@storybook/addon-knobs';
+
+const label = 'Do Something';
+const handler = () => doSomething('foobar');
+button(label, handler);
+```
+
### withKnobs vs withKnobsOptions
If you feel like this addon is not performing well enough there is an option to use `withKnobsOptions` instead of `withKnobs`.
diff --git a/addons/knobs/src/components/Panel.js b/addons/knobs/src/components/Panel.js
index 429958e71b29..062f660c23a1 100644
--- a/addons/knobs/src/components/Panel.js
+++ b/addons/knobs/src/components/Panel.js
@@ -46,6 +46,7 @@ export default class Panel extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
+ this.handleClick = this.handleClick.bind(this);
this.setKnobs = this.setKnobs.bind(this);
this.reset = this.reset.bind(this);
this.setOptions = this.setOptions.bind(this);
@@ -133,6 +134,10 @@ export default class Panel extends React.Component {
this.setState({ knobs: newKnobs }, this.emitChange(changedKnob));
}
+ handleClick(knob) {
+ this.props.channel.emit('addon:knobs:knobClick', knob);
+ }
+
render() {
const { knobs } = this.state;
const knobsArray = Object.keys(knobs)
@@ -146,7 +151,11 @@ export default class Panel extends React.Component {
return (
`;
diff --git a/examples/cra-kitchen-sink/src/stories/index.js b/examples/cra-kitchen-sink/src/stories/index.js
index 6668fb687166..b067b63ffa14 100644
--- a/examples/cra-kitchen-sink/src/stories/index.js
+++ b/examples/cra-kitchen-sink/src/stories/index.js
@@ -1,4 +1,5 @@
import React from 'react';
+import PropTypes from 'prop-types';
import EventEmiter from 'eventemitter3';
import { storiesOf } from '@storybook/react';
@@ -16,6 +17,7 @@ import {
select,
array,
date,
+ button,
object,
} from '@storybook/addon-knobs';
import centered from '@storybook/addon-centered';
@@ -59,6 +61,23 @@ const InfoButton = () => (
);
+class AsyncItemLoader extends React.Component {
+ constructor() {
+ super();
+ this.state = { items: [] };
+ }
+
+ loadItems() {
+ setTimeout(() => this.setState({ items: ['pencil', 'pen', 'eraser'] }), 1500);
+ }
+
+ render() {
+ button('Load the items', () => this.loadItems());
+ return this.props.children(this.state.items);
+ }
+}
+AsyncItemLoader.propTypes = { children: PropTypes.func.isRequired };
+
storiesOf('Button', module)
.addDecorator(withKnobs)
.add('with text', () => (
@@ -118,6 +137,14 @@ storiesOf('Button', module)
In my backpack, I have:
{items.map(item => - {item}
)}
{salutation}
+
+ PS. My shirt pocket contains:
+
+ {loadedItems => {
+ if (!loadedItems.length) return No items!;
+ return {loadedItems.map(i => - {i}
)}
;
+ }}
+
);
})
diff --git a/examples/vue-kitchen-sink/src/stories/index.js b/examples/vue-kitchen-sink/src/stories/index.js
index 82c9820433c8..9ea6dfdd6ceb 100644
--- a/examples/vue-kitchen-sink/src/stories/index.js
+++ b/examples/vue-kitchen-sink/src/stories/index.js
@@ -14,6 +14,7 @@ import {
select,
color,
date,
+ button,
} from '@storybook/addon-knobs';
import Centered from '@storybook/addon-centered';
@@ -231,6 +232,8 @@ storiesOf('Addon Knobs', module)
: `I'm out of ${fruit}${nice ? ', Sorry!' : '.'}`;
const salutation = nice ? 'Nice to meet you!' : 'Leave me alone!';
+ button('Arbitrary action', action('You clicked it!'));
+
return {
template: `