Skip to content

Commit b9f3ca8

Browse files
authored
feature/UIDS-300 Adds indeterminate state handling to CheckboxButton (#306)
* adds indeterminate state handling to CheckboxButton * removes eslint disable rule, updates doc, updates story
1 parent f8422b4 commit b9f3ca8

File tree

6 files changed

+213
-18
lines changed

6 files changed

+213
-18
lines changed

spec/__snapshots__/Storyshots.test.js.snap

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,75 @@ exports[`Storyshots Design System/Card Default 1`] = `
634634
</div>
635635
`;
636636

637+
exports[`Storyshots Design System/CheckboxButton Default 1`] = `
638+
<div
639+
style={
640+
Object {
641+
"padding": "1rem",
642+
}
643+
}
644+
>
645+
<input
646+
checked={true}
647+
className="checkbox"
648+
disabled={false}
649+
id="1"
650+
onChange={[Function]}
651+
type="checkbox"
652+
value={1}
653+
/>
654+
</div>
655+
`;
656+
657+
exports[`Storyshots Design System/CheckboxButton Indeterminate 1`] = `
658+
<div
659+
style={
660+
Object {
661+
"padding": "1rem",
662+
}
663+
}
664+
>
665+
<div
666+
style={
667+
Object {
668+
"display": "flex",
669+
"flexDirection": "column",
670+
"rowGap": "1rem",
671+
}
672+
}
673+
>
674+
<input
675+
checked={false}
676+
id="select-all"
677+
indeterminate={false}
678+
onChange={[Function]}
679+
type="checkbox"
680+
/>
681+
<input
682+
checked={false}
683+
id="1"
684+
onChange={[Function]}
685+
type="checkbox"
686+
value="1"
687+
/>
688+
<input
689+
checked={false}
690+
id="2"
691+
onChange={[Function]}
692+
type="checkbox"
693+
value="2"
694+
/>
695+
<input
696+
checked={false}
697+
id="3"
698+
onChange={[Function]}
699+
type="checkbox"
700+
value="3"
701+
/>
702+
</div>
703+
</div>
704+
`;
705+
637706
exports[`Storyshots Design System/Copy To Clipboard Button Default 1`] = `
638707
<div
639708
style={

src/CheckboxButton/CheckboxButton.jsx

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
import React from 'react';
1+
import React, { useEffect } from 'react';
22
import PropTypes from 'prop-types';
33

4+
export const CHECKED_STATES = {
5+
CHECKED: true,
6+
UNCHECKED: false,
7+
INDETERMINATE: 'indeterminate',
8+
};
9+
410
const CheckboxButton = React.forwardRef(({
511
checked,
612
className,
@@ -11,21 +17,30 @@ const CheckboxButton = React.forwardRef(({
1117
value,
1218
onChange,
1319
...rest
14-
}, ref) => (
15-
<input
16-
checked={checked}
17-
className={className}
18-
disabled={disabled}
19-
id={id}
20-
indeterminate={indeterminate}
21-
name={name}
22-
ref={ref}
23-
type="checkbox"
24-
value={value}
25-
onChange={onChange}
26-
{...rest}
27-
/>
28-
));
20+
}, ref) => {
21+
useEffect(() => {
22+
if (ref && ref.current) {
23+
const checkboxRef = ref.current;
24+
checkboxRef.indeterminate = indeterminate;
25+
}
26+
}, [indeterminate, ref]);
27+
28+
return (
29+
<input
30+
checked={checked}
31+
className={className}
32+
disabled={disabled}
33+
id={id}
34+
indeterminate={indeterminate}
35+
name={name}
36+
ref={ref}
37+
type="checkbox"
38+
value={value}
39+
onChange={onChange}
40+
{...rest}
41+
/>
42+
);
43+
});
2944

3045
CheckboxButton.propTypes = {
3146
checked: PropTypes.bool,
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { ArgsTable, Story, Canvas } from '@storybook/addon-docs/blocks';
2+
import CheckboxButton from './CheckboxButton';
3+
4+
# CheckboxButton
5+
A checkbox control.
6+
7+
<Canvas>
8+
<Story id="design-system-checkboxbutton--default" />
9+
</Canvas>
10+
11+
<ArgsTable of={CheckboxButton} />
12+
13+
## Anatomy
14+
- States: checked, unchecked, & indeterminate
15+
16+
## Stories
17+
18+
### Default
19+
20+
<Canvas>
21+
<Story id="design-system-checkboxbutton--default" />
22+
</Canvas>
23+
24+
### Indeterminate
25+
26+
- When a checkbox "owns" a number of sub-checkboxes and any one or more of the sub-checkboxes have a different state than the others, the owning checkbox is in the indeterminate state (or "partially" checked)
27+
- The indeterminate state of a checkbox is visual only.
28+
- You can’t make a checkbox indeterminate through HTML. There is no indeterminate attribute. It is a property of checkboxes though, which you can change via JavaScript (i.e. we're handling it through refs and `useEffect` on the CheckboxButton component). Please use the `indeterminate` prop to get the desired effect.
29+
30+
<Canvas>
31+
<Story id="design-system-checkboxbutton--indeterminate" />
32+
</Canvas>
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import React, { useRef, useState } from 'react';
2+
import CheckboxButton, { CHECKED_STATES } from 'src/CheckboxButton';
3+
4+
import mdx from './CheckboxButton.mdx';
5+
6+
export default {
7+
title: 'Design System/CheckboxButton',
8+
component: CheckboxButton,
9+
parameters: {
10+
docs: {
11+
page: mdx,
12+
},
13+
},
14+
};
15+
16+
export const Default = () => {
17+
const [isChecked, setIsChecked] = useState(CHECKED_STATES.CHECKED);
18+
19+
const handleChange = () => {
20+
setIsChecked((prev) => !prev);
21+
};
22+
23+
return (
24+
<CheckboxButton
25+
checked={isChecked}
26+
className="checkbox"
27+
disabled={false}
28+
id="1"
29+
type="checkbox"
30+
value={1}
31+
onChange={handleChange}
32+
/>
33+
);
34+
};
35+
36+
export const Indeterminate = () => {
37+
const inputEl = useRef(null);
38+
const [checked, setChecked] = useState([]);
39+
const checkboxes = ['1', '2', '3'];
40+
41+
const onCheckAll = (event) => {
42+
if (event.target.checked) {
43+
setChecked(checkboxes);
44+
} else {
45+
setChecked([]);
46+
}
47+
};
48+
49+
const onCheck = (event, value) => {
50+
if (event.target.checked) {
51+
setChecked([...checked, value]);
52+
} else {
53+
setChecked(checked.filter((item) => item !== value));
54+
}
55+
};
56+
57+
return (
58+
<div style={{ display: 'flex', flexDirection: 'column', rowGap: '1rem' }}>
59+
<CheckboxButton
60+
checked={checked.length === 3}
61+
id="select-all"
62+
indeterminate={checked.length > 0 && checked.length < 3}
63+
ref={inputEl}
64+
onChange={onCheckAll}
65+
/>
66+
{checkboxes.map((item) => (
67+
<CheckboxButton
68+
checked={checked.includes(item)}
69+
id={item}
70+
key={item}
71+
value={item}
72+
onChange={(e) => onCheck(e, item)}
73+
/>
74+
))}
75+
</div>
76+
);
77+
};

src/CheckboxButton/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
import CheckboxButton from './CheckboxButton';
1+
import CheckboxButton, { CHECKED_STATES } from './CheckboxButton';
22

3+
export { CHECKED_STATES };
34
export default CheckboxButton;

src/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Alert, MessageTypes } from 'src/Alert';
22
import Avatar from 'src/Avatar';
33
import Card, { CardSizes } from 'src/Card';
4-
import CheckboxButton from 'src/CheckboxButton';
4+
import CheckboxButton, { CHECKED_STATES } from 'src/CheckboxButton';
55
import CheckboxButtonGroup from 'src/CheckboxButtonGroup';
66
import CopyToClipboard from 'src/CopyToClipboard';
77
import CopyToClipboardButton from 'src/CopyToClipboardButton';
@@ -53,6 +53,7 @@ export {
5353
CardSizes,
5454
CheckboxButton,
5555
CheckboxButtonGroup,
56+
CHECKED_STATES,
5657
CopyToClipboard,
5758
CopyToClipboardButton,
5859
FadeTransition,

0 commit comments

Comments
 (0)