Skip to content

Commit 99ca3c9

Browse files
authored
UIDS-81 New CheckboxButtonGroup component (#82)
* UIDS-81 New CheckboxButtonGroup component
1 parent 60b3c45 commit 99ca3c9

File tree

24 files changed

+596
-37
lines changed

24 files changed

+596
-37
lines changed

.babelrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"@babel/preset-react"
55
],
66
"plugins": [
7+
"@babel/plugin-transform-runtime",
78
"@babel/plugin-proposal-object-rest-spread",
89
["module-resolver", {
910
"alias": {

jest.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ module.exports = {
7777

7878
// A map from regular expressions to module names that allow to stub out resources with a single module
7979
moduleNameMapper: {
80+
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|mdx)$": "<rootDir>/spec/__mocks__/fileMock.js",
8081
'\\.(css|less|scss)$': '<rootDir>/spec/__mocks__/styleMock.js',
8182
'^src/([^\\.]*)$': "<rootDir>/src/$1",
8283
},

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@
5050
"@babel/cli": "^7.8.4",
5151
"@babel/core": "^7.8.4",
5252
"@babel/plugin-proposal-object-rest-spread": "^7.8.3",
53+
"@babel/plugin-transform-runtime": "^7.12.1",
5354
"@babel/preset-env": "^7.8.4",
55+
"@babel/runtime": "^7.12.5",
5456
"@fortawesome/fontawesome-svg-core": "^1.2.28",
5557
"@fortawesome/free-regular-svg-icons": "^5.13.0",
5658
"@fortawesome/free-solid-svg-icons": "^5.13.0",
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import React, { useState } from 'react';
2+
import { act, create } from 'react-test-renderer';
3+
import PropTypes from 'prop-types';
4+
5+
import CheckboxButtonGroup from 'src/CheckboxButtonGroup';
6+
import CheckboxButton from 'src/CheckboxButton';
7+
8+
const CheckboxButtonGroupComponent = ({ children, defaultValues }) => {
9+
const [value, setValue] = useState(defaultValues);
10+
return (
11+
<CheckboxButtonGroup id="checkbox-question" value={value} onChange={setValue}>
12+
{children}
13+
</CheckboxButtonGroup>
14+
);
15+
};
16+
17+
CheckboxButtonGroupComponent.propTypes = {
18+
defaultValues: PropTypes.array.isRequired,
19+
};
20+
21+
describe('CheckboxButtonGroup', () => {
22+
test('sets initial value properly', () => {
23+
const checkboxButtonGroup = create(
24+
<CheckboxButtonGroupComponent defaultValues={[1, 2]}>
25+
<CheckboxButton id="first" label="First value" value={1} />
26+
<CheckboxButton id="second" label="Second value" value={2} />
27+
</CheckboxButtonGroupComponent>,
28+
);
29+
30+
expect(checkboxButtonGroup).toMatchSnapshot();
31+
});
32+
33+
test('will update array state to check a new item', async () => {
34+
const checkboxButtonGroup = create(
35+
<CheckboxButtonGroupComponent defaultValues={[]}>
36+
<CheckboxButton id="first" label="First value" value={1} />
37+
<CheckboxButton id="second" label="Second value" value={2} />
38+
</CheckboxButtonGroupComponent>,
39+
);
40+
41+
const event = { target: { value: 1, checked: true } };
42+
const firstButton = checkboxButtonGroup.root.findAllByType((CheckboxButton))[0];
43+
44+
await act(async () => firstButton.props.onChange(event));
45+
expect(checkboxButtonGroup).toMatchSnapshot();
46+
});
47+
48+
test('will update array state to uncheck an item', async () => {
49+
const checkboxButtonGroup = create(
50+
<CheckboxButtonGroupComponent defaultValues={[1]}>
51+
<CheckboxButton id="first" label="First value" value={1} />
52+
<CheckboxButton id="second" label="Second value" value={2} />
53+
</CheckboxButtonGroupComponent>,
54+
);
55+
56+
const event = { target: { value: 1, checked: false } };
57+
const firstButton = checkboxButtonGroup.root.findAllByType(CheckboxButton)[0];
58+
59+
await act(async () => firstButton.props.onChange(event));
60+
expect(checkboxButtonGroup).toMatchSnapshot();
61+
});
62+
});
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`CheckboxButtonGroup sets initial value properly 1`] = `
4+
<div
5+
className="CheckboxButtonGroup"
6+
id="checkbox-question"
7+
>
8+
<input
9+
checked={true}
10+
id="first"
11+
label="First value"
12+
onChange={[Function]}
13+
type="checkbox"
14+
value={1}
15+
/>
16+
<input
17+
checked={true}
18+
id="second"
19+
label="Second value"
20+
onChange={[Function]}
21+
type="checkbox"
22+
value={2}
23+
/>
24+
</div>
25+
`;
26+
27+
exports[`CheckboxButtonGroup will update array state to check a new item 1`] = `
28+
<div
29+
className="CheckboxButtonGroup"
30+
id="checkbox-question"
31+
>
32+
<input
33+
checked={true}
34+
id="first"
35+
label="First value"
36+
onChange={[Function]}
37+
type="checkbox"
38+
value={1}
39+
/>
40+
<input
41+
checked={false}
42+
id="second"
43+
label="Second value"
44+
onChange={[Function]}
45+
type="checkbox"
46+
value={2}
47+
/>
48+
</div>
49+
`;
50+
51+
exports[`CheckboxButtonGroup will update array state to uncheck an item 1`] = `
52+
<div
53+
className="CheckboxButtonGroup"
54+
id="checkbox-question"
55+
>
56+
<input
57+
checked={false}
58+
id="first"
59+
label="First value"
60+
onChange={[Function]}
61+
type="checkbox"
62+
value={1}
63+
/>
64+
<input
65+
checked={false}
66+
id="second"
67+
label="Second value"
68+
onChange={[Function]}
69+
type="checkbox"
70+
value={2}
71+
/>
72+
</div>
73+
`;

spec/__mocks__/fileMock.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = {};

spec/__snapshots__/Storyshots.test.js.snap

Lines changed: 82 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -339,15 +339,13 @@ exports[`Storyshots Design System/Form Elements/Form Control Label Checkbox 1`]
339339
className="FormControlLabel"
340340
htmlFor="checkbox"
341341
>
342-
<span
342+
<input
343343
className="FormControlLabel__control"
344-
>
345-
<input
346-
id="checkbox"
347-
name="checkbox"
348-
type="checkbox"
349-
/>
350-
</span>
344+
defaultValue="1"
345+
id="checkbox"
346+
name="checkbox"
347+
type="checkbox"
348+
/>
351349
Labeled checkbox
352350
</label>
353351
</div>
@@ -425,6 +423,82 @@ exports[`Storyshots Design System/Form Elements/Form Group Required 1`] = `
425423
</div>
426424
`;
427425

426+
exports[`Storyshots Design System/Form Elements/Form Group With Checkbox Button Group 1`] = `
427+
<div
428+
style={
429+
Object {
430+
"padding": "1rem",
431+
}
432+
}
433+
>
434+
<div
435+
className="FormGroup FormGroup--bordered"
436+
id="with-checkbox-button-group"
437+
>
438+
<label
439+
className="InputLabel"
440+
htmlFor="checkbox-button-group"
441+
>
442+
Form Group with checkbox button group
443+
<span
444+
className="InputLabel__helper-text"
445+
>
446+
 (
447+
with some helper text
448+
)
449+
</span>
450+
</label>
451+
<div
452+
className="CheckboxButtonGroup"
453+
id="checkbox-button-group"
454+
>
455+
<label
456+
className="FormControlLabel FormControlLabel--bordered"
457+
htmlFor="value-1"
458+
>
459+
<input
460+
checked={false}
461+
className="FormControlLabel__control"
462+
id="value-1"
463+
onChange={[Function]}
464+
type="checkbox"
465+
value="1"
466+
/>
467+
Value 1
468+
</label>
469+
<label
470+
className="FormControlLabel FormControlLabel--bordered"
471+
htmlFor="value-2"
472+
>
473+
<input
474+
checked={false}
475+
className="FormControlLabel__control"
476+
id="value-2"
477+
onChange={[Function]}
478+
type="checkbox"
479+
value="2"
480+
/>
481+
Value 2
482+
</label>
483+
<label
484+
className="FormControlLabel FormControlLabel--bordered"
485+
htmlFor="value-3"
486+
>
487+
<input
488+
checked={false}
489+
className="FormControlLabel__control"
490+
id="value-3"
491+
onChange={[Function]}
492+
type="checkbox"
493+
value="3"
494+
/>
495+
Value 3
496+
</label>
497+
</div>
498+
</div>
499+
</div>
500+
`;
501+
428502
exports[`Storyshots Design System/Form Elements/Form Group With Errors 1`] = `
429503
<div
430504
style={
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
const CheckboxButton = ({
5+
checked,
6+
className,
7+
disabled,
8+
id,
9+
indeterminate,
10+
name,
11+
value,
12+
onChange,
13+
...rest
14+
}) => (
15+
<input
16+
checked={checked}
17+
className={className}
18+
disabled={disabled}
19+
id={id}
20+
indeterminate={indeterminate}
21+
name={name}
22+
type="checkbox"
23+
value={value}
24+
onChange={onChange}
25+
{...rest}
26+
/>
27+
);
28+
29+
CheckboxButton.propTypes = {
30+
checked: PropTypes.bool,
31+
className: PropTypes.string,
32+
disabled: PropTypes.bool,
33+
id: PropTypes.string.isRequired,
34+
indeterminate: PropTypes.bool,
35+
name: PropTypes.string,
36+
value: PropTypes.oneOfType([
37+
PropTypes.number,
38+
PropTypes.string,
39+
]),
40+
onChange: PropTypes.func,
41+
};
42+
43+
CheckboxButton.defaultProps = {
44+
checked: undefined,
45+
className: undefined,
46+
disabled: undefined,
47+
indeterminate: undefined,
48+
name: undefined,
49+
value: undefined,
50+
onChange: undefined,
51+
};
52+
53+
export default CheckboxButton;

src/CheckboxButton/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import CheckboxButton from './CheckboxButton';
2+
3+
export default CheckboxButton;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import React, { Children } from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
import './CheckboxButtonGroup.scss';
5+
6+
export default function CheckboxButtonGroup({
7+
children,
8+
id,
9+
parseInput,
10+
value,
11+
onChange,
12+
}) {
13+
const handleChangeValues = (event) => {
14+
const eventValue = parseInput(event.target.value);
15+
const values = [...value];
16+
17+
if (event.target.checked && !values.includes(eventValue)) {
18+
values.push(eventValue);
19+
} else if (!event.target.checked && values.includes(eventValue)) {
20+
values.splice(values.indexOf(eventValue), 1);
21+
}
22+
23+
if (onChange) {
24+
onChange(values);
25+
}
26+
};
27+
28+
const renderChildElement = (child) => {
29+
const { value: childValue } = child.props;
30+
const checked = value && value.includes(childValue);
31+
return React.cloneElement(
32+
child,
33+
{
34+
checked,
35+
onChange: handleChangeValues,
36+
},
37+
);
38+
};
39+
40+
return (
41+
<div className="CheckboxButtonGroup" id={id}>
42+
{
43+
Children.toArray(children).map(renderChildElement)
44+
}
45+
</div>
46+
);
47+
}
48+
49+
CheckboxButtonGroup.propTypes = {
50+
children: PropTypes.arrayOf(PropTypes.element).isRequired,
51+
id: PropTypes.string.isRequired,
52+
parseInput: PropTypes.func,
53+
value: PropTypes.arrayOf(
54+
PropTypes.oneOfType([
55+
PropTypes.number,
56+
PropTypes.string,
57+
]),
58+
),
59+
onChange: PropTypes.func,
60+
};
61+
62+
CheckboxButtonGroup.defaultProps = {
63+
parseInput: (i) => i,
64+
value: [],
65+
onChange: undefined,
66+
};

0 commit comments

Comments
 (0)