diff --git a/src/atoms/Checkbox.js b/src/atoms/Checkbox.js new file mode 100644 index 0000000000000000000000000000000000000000..3ca808702e5f662b4f3dfd5bb3aa88e571907694 --- /dev/null +++ b/src/atoms/Checkbox.js @@ -0,0 +1,21 @@ +import React from 'react' +import classnames from 'classnames' +import classes from './Checkbox.local.css' + +const Checkbox = ({ inline, name, value, label, checked, required, onChange }) => ( + <label className={classnames(classes.root, { + [classes.inline]: inline + })}> + <input + className={classes.input} + type="checkbox" + name={name} + value={value} + checked={checked || false} + required={required} + onChange={onChange}/> + {label} + </label> +) + +export default Checkbox diff --git a/src/atoms/Checkbox.local.css b/src/atoms/Checkbox.local.css new file mode 100644 index 0000000000000000000000000000000000000000..596c321cca1958f0d522e148e450a115430d1a5d --- /dev/null +++ b/src/atoms/Checkbox.local.css @@ -0,0 +1,21 @@ +.root { + display: flex; + align-items: center; + cursor: pointer; +} + +.inline { + display: inline-flex; +} + +.inline:not(:last-child) { + margin-right: 1rem; +} + +.root:not(.inline):not(:last-child) { + margin-bottom: 0.5rem; +} + +.input { + margin-right: 0.25rem; +} diff --git a/src/atoms/Checkbox.md b/src/atoms/Checkbox.md new file mode 100644 index 0000000000000000000000000000000000000000..ceffd9ae8740dc66ddeb773f2a4099ef7fee2be6 --- /dev/null +++ b/src/atoms/Checkbox.md @@ -0,0 +1,33 @@ +A checkbox. + +```js +initialState = { checked: null }; + +<Checkbox + name="checkbox" + checked={state.checked} + onChange={event => setState({ checked: event.target.checked })}/> +``` + +A checked checkbox. + +```js +initialState = { checked: true }; + +<Checkbox + name="checkbox-checked" + checked={state.checked} + onChange={event => setState({ checked: event.target.checked })}/> +``` + +A checkbox with a label. + +```js +initialState = { checked: false }; + +<Checkbox + name="checkbox-labelled" + checked={state.checked} + label="Foo" + onChange={event => setState({ checked: event.target.checked })}/> +``` diff --git a/src/atoms/Menu.js b/src/atoms/Menu.js index 69175c0627d609893db3a55c2120302e8f2449c6..ecc67bc8b8f8b3306319605715ebf689efa8d62c 100644 --- a/src/atoms/Menu.js +++ b/src/atoms/Menu.js @@ -2,6 +2,8 @@ import React from 'react' import classnames from 'classnames' import classes from './Menu.local.css' +// TODO: match the width of the container to the width of the widest option? + class Menu extends React.Component { constructor (props) { super(props) diff --git a/src/atoms/Radio.js b/src/atoms/Radio.js index 034bb6ee22a1b10f67bd476c4d913c3a262e3d02..5e131ead8c4bba8257188a5a6cc9ede2ad48215d 100644 --- a/src/atoms/Radio.js +++ b/src/atoms/Radio.js @@ -1,8 +1,11 @@ import React from 'react' +import classnames from 'classnames' import classes from './Radio.local.css' -const Radio = ({ name, value, label, checked, required, onChange }) => ( - <label className={classes.root}> +const Radio = ({ inline, name, value, label, checked, required, onChange }) => ( + <label className={classnames(classes.root, { + [classes.inline]: inline + })}> <input className={classes.input} type="radio" diff --git a/src/atoms/Radio.local.css b/src/atoms/Radio.local.css index 5a5e4d035cd6e083a5ff39090966edad758e3e02..596c321cca1958f0d522e148e450a115430d1a5d 100644 --- a/src/atoms/Radio.local.css +++ b/src/atoms/Radio.local.css @@ -1,13 +1,21 @@ .root { - display: inline-flex; + display: flex; align-items: center; cursor: pointer; } -.root:not(:last-child) { +.inline { + display: inline-flex; +} + +.inline:not(:last-child) { margin-right: 1rem; } +.root:not(.inline):not(:last-child) { + margin-bottom: 0.5rem; +} + .input { margin-right: 0.25rem; } diff --git a/src/atoms/Radio.md b/src/atoms/Radio.md index 21bfdb8e306bc2cebe022a9815c22dc878d18334..bb5df509049864a6819de79a50cc06fb17b98151 100644 --- a/src/atoms/Radio.md +++ b/src/atoms/Radio.md @@ -2,7 +2,7 @@ A radio button. ```js <Radio - name="foo" + name="radio" onChange={event => console.log(event.target.value)}/> ``` @@ -10,7 +10,7 @@ A checked radio button. ```js <Radio - name="radio-bar" + name="radio-checked" checked onChange={event => console.log(event.target.value)}/> ``` @@ -19,7 +19,7 @@ A radio button with a label. ```js <Radio - name="radio-foo" + name="radio-labelled" label="Foo" onChange={event => console.log(event.target.value)}/> ``` diff --git a/src/index.js b/src/index.js index 9df53c4380825068afd5c752c8a274b3d2301511..e80f99f2e4191a9e4825357858f9c38b5dfcc5a0 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,8 @@ +export { default as Checkbox } from './atoms/Checkbox' export { default as Menu } from './atoms/Menu' export { default as Radio } from './atoms/Radio' export { default as Tags } from './atoms/Tags' export { default as AppBar } from './molecules/AppBar' +export { default as CheckboxGroup } from './molecules/CheckboxGroup' +export { default as RadioGroup } from './molecules/RadioGroup' export { default as YesOrNo } from './molecules/YesOrNo' diff --git a/src/molecules/CheckboxGroup.js b/src/molecules/CheckboxGroup.js new file mode 100644 index 0000000000000000000000000000000000000000..8874941661f4f3a47efbb429c25924eecbe3a51f --- /dev/null +++ b/src/molecules/CheckboxGroup.js @@ -0,0 +1,51 @@ +import React from 'react' +import Checkbox from '../atoms/Checkbox' + +class CheckboxGroup extends React.Component { + constructor (props) { + super(props) + + this.state = { + values: props.value || [] + } + } + + handleChange = event => { + const { values } = this.state + + const value = event.target.value + + if (event.target.checked) { + values.push(value) + } else { + values.splice(values.indexOf(value), 1) + } + + this.setState({ values }) + + this.props.onChange(values) + } + + render () { + const { inline, name, options, required } = this.props + const { values } = this.state + + return ( + <div> + {options.map(option => ( + <Checkbox + key={option.value} + name={name} + required={required} + inline={inline} + value={option.value} + label={option.label} + checked={values.includes(option.value)} + onChange={this.handleChange}/> + ))} + </div> + ) + } +} + +export default CheckboxGroup diff --git a/src/molecules/CheckboxGroup.md b/src/molecules/CheckboxGroup.md new file mode 100644 index 0000000000000000000000000000000000000000..7b43e13ec6e62ce36d12f745fc02a7272ca5c0ef --- /dev/null +++ b/src/molecules/CheckboxGroup.md @@ -0,0 +1,54 @@ +A group of checkboxes. + +```js +const options = [ + { + value: 'one', + label: 'One' + }, + { + value: 'two', + label: 'Two' + }, + { + value: 'three', + label: 'Three' + } +]; + +initialState = { value: [] }; + +<CheckboxGroup + name="checkboxgroup" + options={options} + value={state.value} + onChange={value => setState({ value })}/> +``` + +The checkboxes can be displayed inline. + +```js +const options = [ + { + value: 'one', + label: 'One' + }, + { + value: 'two', + label: 'Two' + }, + { + value: 'three', + label: 'Three' + } +]; + +initialState = { value: [] }; + +<CheckboxGroup + name="checkboxgroup-inline" + options={options} + value={state.value} + inline={true} + onChange={value => setState({ value })}/> +``` diff --git a/src/molecules/RadioGroup.js b/src/molecules/RadioGroup.js index a3d7a7937fb429696ee50ab7c7406de1118d77e2..c4fb351e290c4ff82a67538c217dc8dfbed0ef6b 100644 --- a/src/molecules/RadioGroup.js +++ b/src/molecules/RadioGroup.js @@ -11,15 +11,15 @@ class RadioGroup extends React.Component { } handleChange = event => { - this.setState({ - value: event.target.value - }) + const value = event.target.value - this.props.onChange(event) + this.setState({ value }) + + this.props.onChange(value) } render () { - const { name, options, required } = this.props + const { inline, name, options, required } = this.props const { value } = this.state return ( @@ -29,6 +29,7 @@ class RadioGroup extends React.Component { key={option.value} name={name} required={required} + inline={inline} value={option.value} label={option.label} checked={option.value === value} diff --git a/src/molecules/RadioGroup.md b/src/molecules/RadioGroup.md index 67acc7c45debe197bc6031afe6e5aca4bb684ac0..1a753db59c4690d172d23adc208908ff691c9e88 100644 --- a/src/molecules/RadioGroup.md +++ b/src/molecules/RadioGroup.md @@ -18,6 +18,31 @@ const options = [ <RadioGroup options={options} - name="radiogroup-foo" - onChange={event => console.log(event.target.value)}/> + name="radiogroup" + onChange={value => console.log(value)}/> +``` + +The buttons can be displayed inline + +```js +const options = [ + { + value: 'one', + label: 'One' + }, + { + value: 'two', + label: 'Two' + }, + { + value: 'three', + label: 'Three' + } +]; + +<RadioGroup + options={options} + name="radiogroup-inline" + inline={true} + onChange={value => console.log(value)}/> ``` diff --git a/src/molecules/YesOrNo.js b/src/molecules/YesOrNo.js index 206f20fa0ec4eeb5488cbe773462d9440909aafe..8f6c8cf81cd9bbfeb3801f7dbaa580332c8acf0d 100644 --- a/src/molecules/YesOrNo.js +++ b/src/molecules/YesOrNo.js @@ -20,6 +20,7 @@ const YesOrNo = ({ name, value, required, onChange }) => ( options={options} value={value} required={required} + inline={true} onChange={onChange}/> ) diff --git a/src/molecules/YesOrNo.md b/src/molecules/YesOrNo.md index 1feb069fade39daf19e2dbf023b747a4d8fcacb7..1d410998ef2012670f7702352ac4530c31964b9b 100644 --- a/src/molecules/YesOrNo.md +++ b/src/molecules/YesOrNo.md @@ -3,7 +3,7 @@ A group of radio buttons that provides just two options: "Yes" or "No" ```js <YesOrNo name="yesorno" - onChange={event => console.log(event.target.value)}/> + onChange={value => console.log(value)}/> ``` If a value is set, one option is selected. @@ -12,6 +12,6 @@ If a value is set, one option is selected. <YesOrNo name="yesorno-value" value="yes" - onChange={event => console.log(event.target.value)}/> + onChange={value => console.log(value)}/> ```