Skip to content

Commit

Permalink
Merge pull request #15 from kadirahq/refactor-panel
Browse files Browse the repository at this point in the history
Refactor panel
  • Loading branch information
arunoda committed Sep 7, 2016
2 parents 614b0fe + fba08b4 commit 41d8d82
Show file tree
Hide file tree
Showing 13 changed files with 207 additions and 206 deletions.
8 changes: 4 additions & 4 deletions example/stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ storiesOf('Button', module)
.add('default view', () => (
<Button
onClick={ action('button clicked') }
width={number('width(px)', 70)}
disabled={boolean('disabled', false)}
style={object('style', { width: '70px' })}
disabled={ boolean('Disabled', false) }
width={ number('Width', 100) }
style={ object('Style', { backgroundColor: '#FFF' }) }
>
{text('Label', 'Hello')}
{ text('Label', 'Hello Man') } World
</Button>
))
.add('Story without any knobs', () => (
Expand Down
6 changes: 2 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,8 @@
},
"dependencies": {
"babel-runtime": "^6.5.0",
"brace": "^0.8.0",
"js-beautify": "^1.6.3",
"react-ace": "^3.5.0",
"tosource": "^1.0.0"
"deep-equal": "^1.0.1",
"react-textarea-autosize": "^4.0.5",
},
"main": "dist/index.js",
"engines": {
Expand Down
11 changes: 11 additions & 0 deletions src/KnobStore.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export default class KnobStore {
constructor() {
this.store = {};
this.callbacks = [];
}

has(key) {
Expand All @@ -9,6 +10,7 @@ export default class KnobStore {

set(key, value) {
this.store[key] = value;
this.callbacks.forEach(cb => cb());
}

get(key) {
Expand All @@ -22,4 +24,13 @@ export default class KnobStore {
reset() {
this.store = {};
}

subscribe(cb) {
this.callbacks.push(cb);
}

unsubscribe(cb) {
const index = this.callbacks.indexOf(cb);
this.callbacks.splice(index, 1);
}
}
136 changes: 43 additions & 93 deletions src/components/Panel.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import PropForm from './PropForm';
import tosource from 'tosource';
import { js_beautify as beautify } from 'js-beautify'; // eslint-disable-line camelcase
import Types from './types';

const styles = {
panel: {
Expand Down Expand Up @@ -38,118 +38,68 @@ const styles = {
export default class Panel extends React.Component {
constructor(props) {
super(props);
this._handleChange = this.handleChange.bind(this);
this._setFields = this.setFields.bind(this);
this._checkUrlAndsetFields = this.checkUrlAndsetFields.bind(this);
this._reset = this.reset.bind(this);
this.handleChange = this.handleChange.bind(this);
this.setKnobs = this.setKnobs.bind(this);
this.reset = this.reset.bind(this);

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

componentWillMount() {
this.props.channel.on('addon:knobs:setFields', this._checkUrlAndsetFields);
}

componentDidMount() {
this.stopOnStory = this.api.onStory(() => {
this.api.setQueryParams({ knobs: null });
});
this.state = { knobs: {} };
this.loadedFromUrl = false;
this.props.channel.on('addon:knobs:setKnobs', this.setKnobs);
}

componentWillUnmount() {
this.props.channel.removeListener('addon:knobs:setFields', this._setFields);
this.stopOnStory();
this.props.channel.removeListener('addon:knobs:setKnobs', this.setKnobs);
}

setFields(_fields) {
const fields = _fields;
setKnobs(knobs) {
const queryParams = {};
for (const f in fields) {
if (fields.hasOwnProperty(f)) {
queryParams[`knob-${f}`] = fields[f].value;
if (fields[f].type === 'object') {
fields[f].value = beautify(tosource(fields[f].value));
}
const { api, channel } = this.props;

Object.keys(knobs).forEach((name) => {
const knob = knobs[name];
// For the first time, get values from the URL and set them.
if (!this.loadedFromUrl) {
const urlValue = api.getQueryParam(`knob-${name}`);
knob.value = Types[knob.type].deserialize(urlValue);
channel.emit('addon:knobs:knobChange', knob);
}
}
this.api.setQueryParams(queryParams);
this.setState({ fields });
}

checkUrlAndsetFields(_fields) {
const fields = _fields;
for (const name in fields) {
if (fields.hasOwnProperty(name)) {
let urlValue = this.api.getQueryParam(`knob-${name}`);
if (urlValue !== undefined) {
// When saved in url the information of whether number or string or bool is lost
// so have to convert numbers and booleans back.
switch (fields[name].type) {
case 'boolean':
urlValue = (urlValue === 'true');
break;
case 'number':
urlValue = Number(urlValue);
break;
default:
}

fields[name].value = urlValue;
this.props.channel.emit('addon:knobs:knobChange', { name, value: urlValue });
}
}
}
this.props.channel.removeListener('addon:knobs:setFields', this._checkUrlAndsetFields);
this.props.channel.on('addon:knobs:setFields', this._setFields);
this.setFields(fields);
queryParams[`knob-${name}`] = Types[knob.type].serialize(knob.value);
});

this.loadedFromUrl = true;
api.setQueryParams(queryParams);
this.setState({ knobs });
}

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

handleChange(change) {
const { name, value, type } = change;

const fields = this.state.fields;
const changedField = {};
changedField[name] = { ...fields[name], ...{ value } };
const newFields = { ...fields, ...changedField };
this.setState({ fields: newFields });

let formatedValue = value;
switch (type) {
case 'object':
try {
formatedValue = eval(`(${value})`); // eslint-disable-line no-eval
} catch (e) {
return;
}
break;
case 'number':
try {
formatedValue = Number(value);
} catch (e) {
return;
}
break;
default:
formatedValue = value;
}
handleChange(changedKnob) {
const { api, channel } = this.props;
const { knobs } = this.state;
const { name, type, value } = changedKnob;
const newKnobs = { ...knobs };
newKnobs[name] = {
...newKnobs[name],
...changedKnob
};

this.setState({ knobs: newKnobs });

const queryParams = {};
queryParams[`knob-${name}`] = formatedValue;
this.api.setQueryParams(queryParams);
queryParams[`knob-${name}`] = Types[type].serialize(value);

this.props.channel.emit('addon:knobs:knobChange', { name, value: formatedValue });
api.setQueryParams(queryParams);
channel.emit('addon:knobs:knobChange', changedKnob);
}

render() {
const fields = this.state.fields;
const fieldsArray = Object.keys(fields).map(key => (fields[key]));
const { knobs } = this.state;
const knobsArray = Object.keys(knobs).map(key => (knobs[key]));

if (fieldsArray.length === 0) {
if (knobsArray.length === 0) {
return (
<div style={styles.noKnobs}>NO KNOBS</div>
);
Expand All @@ -158,9 +108,9 @@ export default class Panel extends React.Component {
return (
<div>
<div style={styles.panel}>
<PropForm fields={fieldsArray} onFieldChange={this._handleChange} />
<PropForm knobs={knobsArray} onFieldChange={this.handleChange} />
</div>
<button style={styles.resetButton} onClick={this._reset}>RESET</button>
<button style={styles.resetButton} onClick={this.reset}>RESET</button>
</div>
);
}
Expand Down
55 changes: 11 additions & 44 deletions src/components/PropField.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
import React from 'react';
import AceEditor from 'react-ace';
import TextType from './types/Text';
import NumberType from './types/Number';
import BooleanType from './types/Boolean';
import ObjectType from './types/Object';
import TypeMap from './types';

import 'brace/mode/javascript';
import 'brace/theme/github';

const TypeMap = {
'text': TextType,
'number': NumberType,
'boolean': BooleanType,
'object': ObjectType,
};

const InvalidType = () => (<span>Invalid Type</span>);

const stylesheet = {
Expand All @@ -25,10 +15,11 @@ const stylesheet = {
label: {
display: 'table-cell',
boxSizing: 'border-box',
verticalAlign: 'middle',
paddingRight: '5px',
verticalAlign: 'top',
paddingRight: 5,
paddingTop: 5,
textAlign: 'right',
width: '20px',
width: 20,
fontSize: '13px',
color: 'rgb(68, 68, 68)',
},
Expand All @@ -44,46 +35,28 @@ stylesheet.checkbox = {
width: 'auto',
};

stylesheet.objectInputLabel = {
...stylesheet.label,
verticalAlign: 'top',
};

export default class PropField extends React.Component {
constructor(props) {
super(props);
this._onChange = this.onChange.bind(this);
this._onChangeBool = this.onChangeBool.bind(this);
this._onChangeObj = this.onChangeObj.bind(this);
}

onChange(e) {
this.props.onChange(e.target.value);
}

onChangeBool(e) {
this.props.onChange(e.target.checked);
}

onChangeObj(value) {
this.props.onChange(value);
}

render() {
const { value, name, type, onChange } = this.props;
const { onChange, knob } = this.props;

let InputType = TypeMap[type] || InvalidType;
const labelStyles =
type === 'object' ? stylesheet.objectInputLabel : stylesheet.label;
let InputType = TypeMap[knob.type] || InvalidType;

return (
<div style={stylesheet.field}>
<label htmlFor={this.props.name} style={labelStyles}>
{`${this.props.name}`}
<label htmlFor={knob.name} style={stylesheet.label}>
{`${knob.name}`}
</label>
<InputType
value={value}
name={name}
knob={knob}
onChange={onChange}
/>
</div>
Expand All @@ -92,12 +65,6 @@ export default class PropField extends React.Component {
}

PropField.propTypes = {
name: React.PropTypes.string.isRequired,
onChange: React.PropTypes.func.isRequired,
type: React.PropTypes.oneOf(['text', 'object', 'number', 'boolean']),
value: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number,
React.PropTypes.bool,
]).isRequired,
knob: React.PropTypes.object,
};
19 changes: 10 additions & 9 deletions src/components/PropForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,22 @@ export default class propForm extends React.Component {
}

render() {
const fields = this.props.fields;
const knobs = this.props.knobs;

fields.sort(function (a, b) {
knobs.sort(function (a, b) {
return a.name > b.name;
});

return (
<form style={stylesheet.propForm}>
{fields.map(field => (
{knobs.map(knob => (
<PropField
key={field.name}
name={field.name}
type={field.type}
value={field.value}
onChange={this._onFieldChange.bind(null, field.name, field.type)}
key={knob.name}
name={knob.name}
type={knob.type}
value={knob.value}
knob={knob}
onChange={this._onFieldChange.bind(null, knob.name, knob.type)}
/>
))}
</form>
Expand All @@ -54,6 +55,6 @@ export default class propForm extends React.Component {
propForm.displayName = 'propForm';

propForm.propTypes = {
fields: React.PropTypes.array.isRequired,
knobs: React.PropTypes.array.isRequired,
onFieldChange: React.PropTypes.func.isRequired,
};
Loading

0 comments on commit 41d8d82

Please sign in to comment.