Skip to content

Commit

Permalink
Update: Checkbox and CheckboxGroup components
Browse files Browse the repository at this point in the history
  • Loading branch information
nimishjha committed May 17, 2018
1 parent 82d64c6 commit d1b3ce6
Show file tree
Hide file tree
Showing 10 changed files with 493 additions and 16 deletions.
3 changes: 3 additions & 0 deletions docs/components/Layout/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import TextareaExample from '../../examples/TextareaExample';
import TextEllipsisExample from '../../examples/TextEllipsisExample';
import AlertExample from '../../examples/AlertExample';
import CheckboxExample from '../../examples/CheckboxExample';
import CheckboxGroupExample from '../../examples/CheckboxGroupExample';
import RadioExample from '../../examples/RadioExample';
import SelectExample from '../../examples/SelectExample';
import DatePickerExample from '../../examples/DatePickerExample';
Expand Down Expand Up @@ -74,6 +75,7 @@ const componentsBySection = {
'list-picker',
'user-list-picker',
'checkbox',
'checkbox-group',
'radio',
'select',
'date-picker',
Expand Down Expand Up @@ -174,6 +176,7 @@ class PageLayout extends React.Component {
<ListPickerExample />
<UserListPickerExample />
<CheckboxExample />
<CheckboxGroupExample />
<RadioExample />
<SelectExample />
<DatePickerExample />
Expand Down
58 changes: 43 additions & 15 deletions docs/examples/CheckboxExample.jsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,69 @@
import _ from 'lodash';
import React from 'react';
import Example from '../components/Example';
import { Checkbox } from '../../src';

const onChange = (value, event, name) => {
_.noop();
};

class CheckboxExample extends React.PureComponent {
render() {
return <Checkbox label="Agree to the terms and conditions." />;
return (
<Checkbox
name="Name goes here"
label="Label goes here"
value="Value goes here"
dts="data-test-selector-goes-here"
onChange={onChange}
/>
);
}
}

const exampleProps = {
componentName: 'Checkbox',
designNotes: (
<p>
<span className="text-bold">Checkbox</span> used for making one or more selections from multiple options.
</p>
),
exampleCodeSnippet: '<Checkbox label="Agree to the terms and conditions." />',
notes: (
<p>
See <a href="https://github.com/luqin/react-icheck">React iCheck Documentation</a>
</p>
),
notes: '',
exampleCodeSnippet: `<Checkbox
name="Name goes here"
label="Label goes here"
value="Value goes here"
checked={true}
disabled={true}
dts="data-test-selector-goes-here"
/>`,
propTypeSectionArray: [
{
label: '',
propTypes: [
{
propType: 'name',
type: 'string',
},
{
propType: 'label',
type: 'node',
note: 'Usually fine to rely on a string but can pass HTML e.g. for a url.',
type: 'string',
},
{
propType: 'value',
type: 'string',
note: 'Required.',
},
{
propType: 'checked',
type: 'bool',
},
{
propType: 'disabled',
type: 'bool',
},
{
propType: 'dts',
type: 'string',
},
{
propType: 'onChange',
type: 'func',
type: 'function',
},
],
},
Expand Down
64 changes: 64 additions & 0 deletions docs/examples/CheckboxGroupExample.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import _ from 'lodash';
import React from 'react';
import Example from '../components/Example';
import { CheckboxGroup, Checkbox } from '../../src';

const onChangeGroup = (value, event, name) => {
_.noop();
};

const onChangeIndividual = (value, event, name) => {
_.noop();
};

class CheckboxGroupExample extends React.PureComponent {
render() {
return (
<CheckboxGroup name="movies" value={['terminator', 'predator']} onChange={onChangeGroup}>
<Checkbox label="The Terminator" value="terminator" onChange={onChangeIndividual} />
<Checkbox label="Predator" value="predator" />
<Checkbox label="The Sound of Music" value="soundofmusic" />
</CheckboxGroup>
);
}
}

const exampleProps = {
componentName: 'CheckboxGroup',
notes: 'Contains individual checkboxes. The state of child checkboxes is held in an array.',
exampleCodeSnippet: `<CheckboxGroup name="movies" value={['terminator', 'predator']} onChange={onChangeGroup}>
<Checkbox label="The Terminator" value="terminator" onChange={onChangeIndividual} />
<Checkbox label="Predator" value="predator" />
<Checkbox label="The Sound of Music" value="soundofmusic" />
</CheckboxGroup>`,
propTypeSectionArray: [
{
label: '',
propTypes: [
{
propType: 'name',
type: 'string',
},
{
propType: 'value',
type: 'arrayOf(string: value, ...)',
note: "The strings must be the values of the group's child Checkboxes",
},
{
propType: 'children',
type: '<Checkbox /> elements',
},
{
propType: 'onChange',
type: 'Function',
},
],
},
],
};

export default () => (
<Example {...exampleProps}>
<CheckboxGroupExample />
</Example>
);
89 changes: 89 additions & 0 deletions src/components/adslot-ui/Checkbox/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React from 'react';
import PropTypes from 'prop-types';
import { expandDts } from 'lib/utils';

import './styles.scss';

class Checkbox extends React.Component {
static getDerivedStateFromProps(newProps, prevState) {
let isChecked = prevState.checked;
let isDisabled = prevState.disabled;
if (newProps.checked) isChecked = newProps.checked;
if (newProps.disabled) isDisabled = newProps.disabled;
return {
checked: isChecked,
disabled: isDisabled,
};
}

constructor(props) {
super(props);
this.state = {
checked: props.checked,
disabled: props.disabled,
};
this.onChangeDefault = this.onChangeDefault.bind(this);
if (this.props.onChange) {
this.onChange = this.props.onChange.bind(this);
}
}

onChangeDefault(event) {
const isChecked = Boolean(event.target.checked);
const isDisabled = Boolean(event.target.disabled);
this.setState(() => ({
checked: isChecked,
disabled: isDisabled,
}));
if (this.onChange) {
this.onChange(event, this.props.name);
}
}

render() {
const { name, value, label, dts } = this.props;
const optional = {
id: this.props.id ? this.props.id : null,
className: this.props.className ? this.props.className : null,
};
if (this.props['data-name']) {
optional['data-name'] = this.props['data-name'];
}
return (
<label className="checkbox-single">
<input
type="checkbox"
name={name}
value={value}
onChange={this.onChangeDefault}
disabled={this.state.disabled}
checked={this.state.checked}
{...expandDts(dts)}
{...optional}
/>
{label ? <span>{label}</span> : null}
</label>
);
}
}

Checkbox.propTypes = {
id: PropTypes.string,
className: PropTypes.string,
'data-name': PropTypes.string,
name: PropTypes.string,
label: PropTypes.node,
value: PropTypes.string,
dts: PropTypes.string,
disabled: PropTypes.bool,
checked: PropTypes.bool,
onChange: PropTypes.func,
};

Checkbox.defaultProps = {
dts: '',
disabled: false,
checked: false,
};

export default Checkbox;
106 changes: 106 additions & 0 deletions src/components/adslot-ui/Checkbox/index.spec.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import React from 'react';
import { shallow, mount } from 'enzyme';
import sinon from 'sinon';
import Checkbox from '.';

describe('Checkbox', () => {
it('should render with props', () => {
const component = shallow(
<Checkbox label="The Terminator" name="movies" value="terminator" dts="checkbox-terminator" />
);
const checkboxElement = component.find('input[type="checkbox"]');
const labelElement = component.find('label');
expect(labelElement.text()).to.equal('The Terminator');
expect(checkboxElement).to.have.length(1);
expect(checkboxElement.prop('name')).to.equal('movies');
expect(checkboxElement.prop('value')).to.equal('terminator');
expect(checkboxElement.prop('checked')).to.equal(false);
expect(checkboxElement.prop('data-test-selector')).to.equal('checkbox-terminator');
});

it('should render with just label', () => {
const component = shallow(<Checkbox label="Label goes here" />);
const checkboxElement = component.find('input[type="checkbox"]');
expect(checkboxElement).to.have.length(1);
const labelElement = component.find('label');
expect(labelElement.text()).to.equal('Label goes here');
});

it('should render with just onChange', () => {
const onChangeHandler = sinon.spy();
const component = shallow(<Checkbox onChange={onChangeHandler} />);
const checkboxElement = component.find('input[type="checkbox"]');
const event = { target: { checked: true, disabled: false } };
checkboxElement.simulate('change', event);
expect(onChangeHandler.callCount).to.equal(1);
expect(component.state()).to.eql({ checked: true, disabled: false });
});

it('should render with id, className, and data-name', () => {
const onChangeHandler = sinon.spy();
const component = shallow(
<Checkbox onChange={onChangeHandler} id="checkboxId" className="checkboxClass" data-name="checkboxName" />
);
const checkboxElement = component.find('input[type="checkbox"]');
const event = { target: { checked: true, disabled: false } };
checkboxElement.simulate('change', event);
expect(onChangeHandler.callCount).to.equal(1);
expect(component.state()).to.eql({ checked: true, disabled: false });
});

it('should handle change event', () => {
const onChangeHandler = sinon.spy();
const component = shallow(
<Checkbox
label="The Terminator"
name="movies"
value="terminator"
onChange={onChangeHandler}
dts="checkbox-terminator"
/>
);
const checkboxElement = component.find('input[type="checkbox"]');
const event = { target: { checked: true, disabled: true } };
checkboxElement.simulate('change', event);
expect(onChangeHandler.callCount).to.equal(1);
expect(component.state()).to.eql({ checked: true, disabled: true });
});

it('should render without a label', () => {
const component = shallow(<Checkbox name="movies" value="terminator" />);
const labelElement = component.find('label');
expect(labelElement.text()).to.equal('');
});

it('should render with checked and disabled states', () => {
const component = shallow(
<Checkbox
name="movies"
value="terminator"
checked={true} // eslint-disable-line
disabled={true} // eslint-disable-line
/>
);
expect(component.state()).to.eql({ checked: true, disabled: true });
});

it('should handle change events without a custom onChange handler', () => {
const component = shallow(<Checkbox name="movies" value="terminator" />);
const checkboxElement = component.find('input[type="checkbox"]');
const event = { target: { checked: true, disabled: false } };
checkboxElement.simulate('change', event);
expect(component.state()).to.eql({ checked: true, disabled: false });
});

it('should handle props changes', () => {
const component = mount(<Checkbox name="movies" value="terminator" />);
component.setProps({ checked: true, disabled: true });
expect(component.state()).to.eql({ checked: true, disabled: true });
});

it('should handle props changes when no relevant props are given', () => {
const component = mount(<Checkbox name="movies" value="terminator" />);
component.setProps({ foo: 'bar' });
expect(component.state()).to.eql({ checked: false, disabled: false });
});
});
14 changes: 14 additions & 0 deletions src/components/adslot-ui/Checkbox/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.checkbox-single {
line-height: 16px;
display: block;

span {
margin: 0 0 0 5px;
}

input {
margin: 0;
line-height: 16px;
display: inline-block;
}
}
Loading

0 comments on commit d1b3ce6

Please sign in to comment.