Skip to content

Commit

Permalink
Merge pull request #130 from edx/mroytman/controllable-checkbox
Browse files Browse the repository at this point in the history
feat(checkbox): change CheckBox to be controllable via props
  • Loading branch information
MichaelRoytman authored Jan 25, 2018
2 parents f3aaa58 + 89c257d commit eff815d
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 14 deletions.
68 changes: 61 additions & 7 deletions .storybook/__snapshots__/Storyshots.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ exports[`Storyshots CheckBox basic usage 1`] = `
>
<input
aria-checked={false}
checked={false}
className={
Array [
"form-check-input",
]
}
defaultChecked={false}
disabled={false}
id="asInput1"
name="checkbox"
Expand Down Expand Up @@ -65,12 +65,12 @@ exports[`Storyshots CheckBox call a function 1`] = `
>
<input
aria-checked={false}
checked={false}
className={
Array [
"form-check-input",
]
}
defaultChecked={false}
disabled={false}
id="asInput1"
name="checkbox"
Expand Down Expand Up @@ -98,6 +98,60 @@ exports[`Storyshots CheckBox call a function 1`] = `
</div>
`;

exports[`Storyshots CheckBox controlled 1`] = `
<div>
<button
className="btn btn-light"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Click me to toggle the check box!
</button>
<div
className={
Array [
"form-check",
]
}
>
<input
aria-checked={false}
checked={false}
className={
Array [
"form-check-input",
]
}
disabled={false}
id="asInput1"
name="checkbox"
onChange={[Function]}
type="checkbox"
/>
<label
className={
Array [
"form-check-label",
]
}
htmlFor="asInput1"
id="label-asInput1"
>
</label>
<div
aria-live="polite"
className="form-control-feedback"
id="error-asInput1"
>
<span />
</div>
</div>
</div>
`;

exports[`Storyshots CheckBox default checked 1`] = `
<div
className={
Expand All @@ -108,12 +162,12 @@ exports[`Storyshots CheckBox default checked 1`] = `
>
<input
aria-checked={true}
checked={true}
className={
Array [
"form-check-input",
]
}
defaultChecked={true}
disabled={false}
id="asInput1"
name="checkbox"
Expand Down Expand Up @@ -151,12 +205,12 @@ exports[`Storyshots CheckBox disabled 1`] = `
>
<input
aria-checked={false}
checked={false}
className={
Array [
"form-check-input",
]
}
defaultChecked={false}
disabled={true}
id="asInput1"
name="checkbox"
Expand Down Expand Up @@ -197,12 +251,12 @@ exports[`Storyshots CheckBoxGroup basic usage 1`] = `
>
<input
aria-checked={false}
checked={false}
className={
Array [
"form-check-input",
]
}
defaultChecked={false}
disabled={false}
id="checkbox1"
name="basicCheckBox"
Expand Down Expand Up @@ -237,12 +291,12 @@ exports[`Storyshots CheckBoxGroup basic usage 1`] = `
>
<input
aria-checked={false}
checked={false}
className={
Array [
"form-check-input",
]
}
defaultChecked={false}
disabled={false}
id="checkbox2"
name="basicCheckBox"
Expand Down Expand Up @@ -277,12 +331,12 @@ exports[`Storyshots CheckBoxGroup basic usage 1`] = `
>
<input
aria-checked={false}
checked={false}
className={
Array [
"form-check-input",
]
}
defaultChecked={false}
disabled={false}
id="checkbox3"
name="basicCheckBox"
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions src/CheckBox/CheckBox.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,42 @@ import React from 'react';
import { storiesOf } from '@storybook/react';

import CheckBox from './index';
import Button from '../Button';

class CheckBoxWrapper extends React.Component {
constructor(props) {
super(props);

this.toggleCheckBox = this.toggleCheckBox.bind(this);

this.state = {
checked: false,
};
}

toggleCheckBox() {
this.setState({
checked: !this.state.checked,
});
}

render() {
return (
<div>
<Button
onClick={this.toggleCheckBox}
label="Click me to toggle the check box!"
buttonType="light"
/>
<CheckBox
name="checkbox"
label=""
checked={this.state.checked}
/>
</div>
);
}
}

storiesOf('CheckBox', module)
.add('basic usage', () => (
Expand Down Expand Up @@ -32,4 +68,7 @@ storiesOf('CheckBox', module)
label="check out the console"
onChange={() => console.log('the checkbox changed state')}
/>
))
.add('controlled', () => (
<CheckBoxWrapper />
));
19 changes: 16 additions & 3 deletions src/CheckBox/CheckBox.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ describe('<CheckBox />', () => {

expect(wrapper.find('[name="checkbox"]').exists()).toEqual(true);
expect(wrapper.find('[type="checkbox"]').exists()).toEqual(true);
expect(wrapper.find('[defaultChecked=false]').exists()).toEqual(true);
expect(wrapper.find('[checked=false]').exists()).toEqual(true);
expect(wrapper.find('[aria-checked=false]').exists()).toEqual(true);
});

Expand Down Expand Up @@ -45,11 +45,11 @@ describe('<CheckBox />', () => {
it('checks if start state can be set to checked', () => {
const wrapper = mount(<CheckBox name="checkbox" label="I start checked" checked />);

expect(wrapper.find('[defaultChecked=true]').exists()).toEqual(true);
expect(wrapper.find('[checked=true]').exists()).toEqual(true);
expect(wrapper.find('[aria-checked=true]').exists()).toEqual(true);

wrapper.find('input').simulate('change');
expect(wrapper.find('[defaultChecked=false]').exists()).toEqual(true);
expect(wrapper.find('[checked=false]').exists()).toEqual(true);
expect(wrapper.find('[aria-checked=false]').exists()).toEqual(true);
});

Expand All @@ -61,4 +61,17 @@ describe('<CheckBox />', () => {
wrapper.find('input').simulate('change');
expect(wrapper.props().disabled).toEqual(true);
});

it('overrides state value when props value changes', () => {
const wrapper = mount(<CheckBox name="checkbox" label="I start checked" checked />);
expect(wrapper.find('[checked=true]').exists()).toEqual(true);
expect(wrapper.find('[aria-checked=true]').exists()).toEqual(true);

wrapper.setProps({
checked: false,
});

expect(wrapper.find('[checked=true]').exists()).toEqual(false);
expect(wrapper.find('[aria-checked=true]').exists()).toEqual(false);
});
});
2 changes: 1 addition & 1 deletion src/CheckBox/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ The following parameters should be passed into every checkbox component:
* `label` (`String`): label to be placed next to the checkbox

The following parameters can optionally be passed into a checkbox component:
* `checked` (`Boolean`): `true` if the default state should be checked, `false` otherwise
* `checked` (`Boolean`): `true` if the state should be checked, `false` otherwise. This prop can be used to manage the Checkbox more directly, overriding the default Checkbox checked state.
* `disabled` (`Boolean`): `true` if the checkbox should be disabled, `false` otherwise
* `onChange`: function to be called when the checkbox changes state
* Function will be called with the arguments: `checked` (`Boolean`), `name` (`String`)
Expand Down
11 changes: 9 additions & 2 deletions src/CheckBox/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,21 @@ class Check extends React.Component {
};
}

componentWillReceiveProps(nextProps) {
if (nextProps.checked !== this.props.checked) {
this.setState({
checked: nextProps.checked,
});
}
}

onChange(event) {
this.setState({
checked: !this.state.checked,
});
this.props.onChange(event);
}


render() {
const props = { ...this.props };

Expand All @@ -31,7 +38,7 @@ class Check extends React.Component {
className={props.className}
type="checkbox"
name={props.name}
defaultChecked={this.state.checked}
checked={this.state.checked}
aria-checked={this.state.checked}
onChange={this.onChange}
disabled={props.disabled}
Expand Down

0 comments on commit eff815d

Please sign in to comment.