Skip to content

Commit

Permalink
Merge pull request #5 from kadirahq/save-state-in-url
Browse files Browse the repository at this point in the history
Save state in url
  • Loading branch information
roonyh committed Aug 30, 2016
2 parents 73c09b2 + 9acc3fa commit bce4fd9
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 56 deletions.
49 changes: 45 additions & 4 deletions src/components/Panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,49 @@ export default class Panel extends React.Component {
this._handleChange = this.handleChange.bind(this);
this._setFields = this.setFields.bind(this);
this._reset = this.reset.bind(this);
this._setInitialFields = this.setInitialFields.bind(this);
this._indicateReady = this.indicateReady.bind(this);

this.state = { fields: {} };
this.api = this.props.api;
}

componentWillMount() {
const urlState = this.api.getQueryParam('knobs');

if (urlState && urlState.length > 0) {
this.initialFields = JSON.parse(urlState);
}

if (this.initialFields) {
this.props.channel.on('addon:knobs:helloFromStory', this._setInitialFields);
this.setState({ fields: this.initialFields });
} else {
this.props.channel.on('addon:knobs:helloFromStory', this._indicateReady);
}
}

componentDidMount() {
this.props.channel.on('addon:knobs:setFields', this._setFields);
this.props.api.onStory(() => {
this.setState({ fields: false });
this.stopOnStory = this.api.onStory(() => {
this.setState({ fields: {} });
});

this.props.channel.on('addon:knobs:setFields', this._setFields);
this.props.channel.emit('addon:knobs:helloFromPanel');
}

componentWillUnmount() {
this.props.channel.removeListener('addon:knobs:setFields', this._setFields);
this.props.channel.removeListener('addon:knobs:helloFromStory', this._indicateReady);
this.props.channel.removeListener('addon:knobs:helloFromStory', this._setInitialFields);
this.api.setQueryParams({ knobs: null });
this.stopOnStory();
}

setInitialFields() {
this.props.channel.emit('addon:knobs:initialFields', this.initialFields);
this.props.channel.removeListener('addon:knobs:helloFromStory', this._setInitialFields);
this.props.channel.on('addon:knobs:helloFromStory', this._indicateReady);
}

setFields(_fields) {
Expand All @@ -54,6 +89,11 @@ export default class Panel extends React.Component {
}
}
this.setState({ fields });
this.api.setQueryParams({ knobs: JSON.stringify(fields) });
}

indicateReady() {
this.props.channel.emit('addon:knobs:panelReady');
}

reset() {
Expand All @@ -68,8 +108,9 @@ export default class Panel extends React.Component {
changedField[name] = { ...fields[name], ...{ value } };
const newFields = { ...fields, ...changedField };
this.setState({ fields: newFields });
this.api.setQueryParams({ knobs: JSON.stringify(newFields) });

this.props.channel.emit('addon:knobs:propChange', change);
this.props.channel.emit('addon:knobs:knobChange', change);
}

render() {
Expand Down
4 changes: 2 additions & 2 deletions src/components/PropField.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ const stylesheet = {
stylesheet.textarea = {
...stylesheet.input,
height: '100px',
}
};

stylesheet.checkbox = {
...stylesheet.input,
width: 'auto',
}
};

export default class PropField extends React.Component {
constructor(props) {
Expand Down
61 changes: 42 additions & 19 deletions src/components/Wrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,66 @@ import React from 'react';
export default class Wrap extends React.Component {
constructor(props) {
super(props);
this.store = this.props.store;
this._initialPropsReceived = this.initialPropsReceived.bind(this);
this._knobChanged = this.knobChanged.bind(this);
this._resetKnobs = this.resetKnobs.bind(this);
this._knobsAreReset = false;
this.gotHello = () => {
this.props.channel.emit('addon:knobs:helloFromStory');
};
this.setPanelFields = () => {
this.props.channel.emit('addon:knobs:setFields', this.store);
};
}

componentDidMount() {
this.props.knobsReset();
this.props.channel.on('addon:knobs:propChange', this._knobChanged);
this.props.channel.on('addon:knobs:initialFields', this._initialPropsReceived);
this.props.channel.on('addon:knobs:knobChange', this._knobChanged);
this.props.channel.on('addon:knobs:reset', this._resetKnobs);
}

componentDidUpdate() {
if (this._knobsAreReset) {
this.props.knobsReset();
this._knobsAreReset = false;
}
this.props.channel.on('addon:knobs:helloFromPanel', this.gotHello);
this.props.channel.on('addon:knobs:panelReady', this.setPanelFields);
this.props.channel.emit('addon:knobs:helloFromStory');
}

componentWillUnmount() {
this.props.channel.removeListener('addon:knobs:propChange', this._knobChanged);
this.props.channel.removeListener('addon:knobs:initialFields', this._initialPropsReceived);
this.props.channel.removeListener('addon:knobs:knobChange', this._knobChanged);
this.props.channel.removeListener('addon:knobs:reset', this._resetKnobs);
this.props.channel.removeListener('addon:knobs:helloFromPanel', this.gotHello);
this.props.channel.removeListener('addon:knobs:panelReady', this.setPanelFields);
}

initialPropsReceived(initialProps) {
Object.keys(initialProps).forEach(change => {
const { name, value } = initialProps[change];
this.knobChanged({ name, value });
});
}

knobChanged(change) {
const success = this.props.knobChanged(change);
if (success) {
// Only update if the knob change is valid
this.forceUpdate();
const { name, value } = change;
const { type } = this.store[name];

let formatedValue = value;
if (type === 'object') {
try {
formatedValue = eval(`(${value})`); // eslint-disable-line no-eval
} catch (e) {
return;
}
}

this.store[name].value = formatedValue;
this.forceUpdate();
}

resetKnobs() {
this._knobsAreReset = true;
this.props.resetKnobs();
Object.keys(this.store).forEach(field => {
delete(this.store[field]);
});
this.forceUpdate();
this.setPanelFields();
}

render() {
Expand All @@ -48,8 +73,6 @@ export default class Wrap extends React.Component {
Wrap.propTypes = {
context: React.PropTypes.object,
storyFn: React.PropTypes.func,
knobChanged: React.PropTypes.func,
knobsReset: React.PropTypes.func,
resetKnobs: React.PropTypes.func,
channel: React.PropTypes.object,
store: React.PropTypes.object,
};
36 changes: 5 additions & 31 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import addons from '@kadira/storybook-addons';
import Panel from './components/Panel';
import Wrap from './components/Wrap';

let knobStore = {};

function register() {
addons.register('kadirahq/storybook-addon-knobs', api => {
Expand All @@ -12,12 +11,14 @@ function register() {
addons.addPanel('kadirahq/storybook-addon-knobs', {
title: 'Knobs',
render: () => {
return <Panel channel={channel} api={api} />;
return <Panel channel={channel} api={api} key="knobs-panel" />;
},
});
});
}

let knobStore = {};

function createKnob(name, value, type) {
if (knobStore[name]) {
return knobStore[name].value;
Expand All @@ -29,39 +30,12 @@ function createKnob(name, value, type) {

function wrap(storyFn) {
const channel = addons.getChannel();
let localKnobStore = {};

const knobChanged = change => {
const { name, value } = change;
const { type } = localKnobStore[name];

let formatedValue = value;
if (type === 'object') {
try {
formatedValue = eval(`(${value})`); // eslint-disable-line no-eval
} catch (e) {
return false;
}
}

localKnobStore[name].value = formatedValue;
return true;
};

const knobsReset = () => {
channel.emit('addon:knobs:setFields', localKnobStore);
};

const resetKnobs = () => {
knobStore = localKnobStore = {};
};
const localKnobStore = {};

return context => {
// Change the global knobStore to the one local to this story
knobStore = localKnobStore;

channel.emit('addon:knobs:setFields', localKnobStore);
return <Wrap {...{ context, storyFn, channel, knobChanged, knobsReset, resetKnobs }} />;
return <Wrap {...{ context, storyFn, channel, store: localKnobStore }} />;
};
}

Expand Down

0 comments on commit bce4fd9

Please sign in to comment.