Skip to content

Commit a0486b3

Browse files
authored
fix: correct the display of checkbox read-only state (#3328)
* fix(checkbox): adjust read-only styles and add read-only example This removes some unnecessary read-only styles. Read-only just needs to override disabled styles; otherwise it uses default styles (for both default and emphasized). Also adjusts template to include aria-disabled when in the read-only state, so screen readers such as VoiceOver do not announce the input as normal. Note that aria-readonly is not used as it is not announced property and MDN states that it is "not relevant" for input with type checkbox. * fix(fieldgroup): remove incorrect read-only checkbox styles The previous display of the read-only state, without checkboxes and displaying commas did not match up with any guidelines. This update is to make the display of read-only match with what is implemented in React Spectrum. It also includes aria-labeledby in the template so the group is announced by screen readers. * docs(checkbox): add additional tests for combination of some states * docs(fieldgroup): display a checked option in readonly fieldgroup
1 parent 0f50c14 commit a0486b3

File tree

12 files changed

+109
-94
lines changed

12 files changed

+109
-94
lines changed

.changeset/afraid-dogs-worry.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@spectrum-css/checkbox": patch
3+
---
4+
5+
This removes some unnecessary read-only styles. Read-only just needs to override disabled styles. Otherwise it uses the normal styles (for both default and emphasized).

.changeset/gold-deers-smash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@spectrum-css/fieldgroup": minor
3+
---
4+
5+
The previous display of the read-only state checkboxes did not match up with any guidelines. This update removes the read-only specific styles for checkbox within the fieldgroup component, so that the boxes are still displayed and commas are not appended to the label. This includes the removal of `--spectrum-fieldgroup-readonly-delimiter` as it is no longer needed.

components/checkbox/index.css

Lines changed: 11 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,11 @@
1313

1414
@import "./themes/express.css";
1515

16-
/* checkbox/index.css
17-
*
16+
/*
1817
* .spectrum-Checkbox-box::before is the Checkbox "box"
1918
* .spectrum-Checkbox-box::after is the focus indicator
2019
*/
2120

22-
/* Component tokens by t-shirt size */
2321
.spectrum-Checkbox {
2422
/* Color */
2523
--spectrum-checkbox-content-color-default: var(--spectrum-neutral-content-color-default);
@@ -176,41 +174,23 @@
176174
}
177175
}
178176

179-
/*
180-
* Read-Only
181-
*
182-
* readonly is not a valid attribute for input[type="checkbox"]
183-
* so we borrow the immutability of a disabled checkbox
184-
* while using the colors of a default checkbox
185-
*/
177+
/* Read-only */
186178
&.is-readOnly {
187-
border-color: var(--highcontrast-checkbox-color-default, var(--mod-checkbox-control-selected-color-default, var(--spectrum-checkbox-control-selected-color-default)));
188-
189-
&:hover {
190-
.spectrum-Checkbox-box::before {
191-
border-color: var(--highcontrast-checkbox-color-default, var(--mod-checkbox-control-selected-color-default, var(--spectrum-checkbox-control-selected-color-default)));
192-
}
179+
.spectrum-Checkbox-input {
180+
cursor: default;
193181
}
194182

195-
&:active {
196-
.spectrum-Checkbox-box::before {
197-
border-color: var(--highcontrast-checkbox-selected-color-default, var(--mod-checkbox-control-selected-color-default, var(--spectrum-checkbox-control-selected-color-default)));
198-
}
199-
}
200-
}
201-
202-
&.is-readOnly .spectrum-Checkbox-input,
203-
&.is-readOnly .spectrum-Checkbox-input:checked {
204-
&:disabled + .spectrum-Checkbox-box {
205-
&::before {
183+
/* Overrides disabled styles */
184+
.spectrum-Checkbox-input,
185+
.spectrum-Checkbox-input:checked {
186+
&:disabled + .spectrum-Checkbox-box::before {
206187
border-color: var(--highcontrast-checkbox-color-default, var(--mod-checkbox-control-selected-color-default, var(--spectrum-checkbox-control-selected-color-default)));
207188
background-color: var(--highcontrast-checkbox-background-color-default, var(--mod-checkbox-checkmark-color, var(--spectrum-checkbox-checkmark-color)));
208189
}
209-
}
210190

211-
&:disabled ~ .spectrum-Checkbox-label {
212-
forced-color-adjust: none;
213-
color: var(--highcontrast-checkbox-color-default, var(--mod-checkbox-content-color-default, var(--spectrum-checkbox-content-color-default)));
191+
&:disabled ~ .spectrum-Checkbox-label {
192+
color: var(--highcontrast-checkbox-color-default, var(--mod-checkbox-content-color-default, var(--spectrum-checkbox-content-color-default)));
193+
}
214194
}
215195
}
216196

components/checkbox/metadata/metadata.json

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,11 @@
6767
".spectrum-Checkbox.is-invalid.is-indeterminate:hover .spectrum-Checkbox-label",
6868
".spectrum-Checkbox.is-invalid:hover .spectrum-Checkbox-box:before",
6969
".spectrum-Checkbox.is-invalid:hover .spectrum-Checkbox-input:checked + .spectrum-Checkbox-box",
70-
".spectrum-Checkbox.is-readOnly",
70+
".spectrum-Checkbox.is-readOnly .spectrum-Checkbox-input",
7171
".spectrum-Checkbox.is-readOnly .spectrum-Checkbox-input:checked:disabled + .spectrum-Checkbox-box:before",
7272
".spectrum-Checkbox.is-readOnly .spectrum-Checkbox-input:checked:disabled ~ .spectrum-Checkbox-label",
7373
".spectrum-Checkbox.is-readOnly .spectrum-Checkbox-input:disabled + .spectrum-Checkbox-box:before",
7474
".spectrum-Checkbox.is-readOnly .spectrum-Checkbox-input:disabled ~ .spectrum-Checkbox-label",
75-
".spectrum-Checkbox.is-readOnly:active .spectrum-Checkbox-box:before",
76-
".spectrum-Checkbox.is-readOnly:hover .spectrum-Checkbox-box:before",
7775
".spectrum-Checkbox:active .spectrum-Checkbox-box:before",
7876
".spectrum-Checkbox:active .spectrum-Checkbox-input:checked + .spectrum-Checkbox-box:before",
7977
".spectrum-Checkbox:active .spectrum-Checkbox-label",
@@ -234,7 +232,6 @@
234232
"--highcontrast-checkbox-highlight-color-default",
235233
"--highcontrast-checkbox-highlight-color-down",
236234
"--highcontrast-checkbox-highlight-color-focus",
237-
"--highcontrast-checkbox-highlight-color-hover",
238-
"--highcontrast-checkbox-selected-color-default"
235+
"--highcontrast-checkbox-highlight-color-hover"
239236
]
240237
}

components/checkbox/stories/checkbox.stories.js

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@ import { isChecked, isDisabled, isEmphasized, isIndeterminate, isInvalid, isRead
44
import metadata from "../metadata/metadata.json";
55
import packageJson from "../package.json";
66
import { CheckboxGroup } from "./checkbox.test.js";
7-
import { AllVariantsCheckboxGroup, DocsCheckboxGroup, } from "./template.js";
7+
import { AllVariantsCheckboxGroup, DocsCheckboxGroup, Template } from "./template.js";
88

99
/**
1010
* Checkboxes allow users to select multiple items from a list of individual items, or mark one individual item as selected.
1111
*
1212
* ## Usage notes
1313
*
14-
* Checkboxes should not be used on their own. They should always be used within a [field group](/docs/components-field-group--docs). Invalid checkboxes are indicated with an alert [help text](/docs/components-help-text--docs) when included in a Field group.
15-
*
16-
* When the label is too long for the horizontal space available, it wraps to form another line.
14+
* - Checkboxes should not be used on their own. They should always be used within a [field group](/docs/components-field-group--docs).
15+
* - Invalid checkboxes are indicated with an alert [help text](/docs/components-help-text--docs) when included in a field group.
16+
* - For more information about the read-only state, see the [read-only checkboxes](/docs/components-field-group--docs#read-only-checkboxes) section of the field group documentation.
1717
*/
1818
export default {
1919
title: "Checkbox",
@@ -62,6 +62,11 @@ Default.args = {
6262
};
6363
Default.tags = ["!autodocs"];
6464

65+
/**
66+
* Checkboxes can be selected, not selected, or in an indeterminate state. They are in an indeterminate state (shown with a dash icon) when they represent both selected and not selected values.
67+
*
68+
* When the label is too long for the horizontal space available, it wraps to form another line.
69+
*/
6570
export const DefaultVariants = AllVariantsCheckboxGroup.bind({});
6671
DefaultVariants.tags = ["!dev"];
6772
DefaultVariants.parameters = {
@@ -92,6 +97,21 @@ Sizing.parameters = {
9297
chromatic: { disableSnapshot: true },
9398
};
9499

100+
/**
101+
* The class `is-readOnly` is applied to checkboxes in the read-only state. It's worth noting that `<input type="checkbox">` HTML elements do not have a native `readonly` attribute, and the intended behavior is up to the implementation:
102+
* - Read-only checkboxes are immutable, i.e. their checked state cannot be changed.
103+
* - Unlike disabled checkboxes, the checkbox should remain focusable.
104+
*/
105+
export const ReadOnly = Template.bind({});
106+
ReadOnly.storyName = "Read-only";
107+
ReadOnly.args = {
108+
isReadOnly: true,
109+
};
110+
ReadOnly.tags = ["!dev"];
111+
ReadOnly.parameters = {
112+
chromatic: { disableSnapshot: true }
113+
};
114+
95115
// ********* VRT ONLY ********* //
96116
export const WithForcedColors = CheckboxGroup.bind({});
97117
WithForcedColors.args = Default.args;

components/checkbox/stories/checkbox.test.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ export const CheckboxGroup = Variants({
2727
testHeading: "Checked",
2828
isChecked: true,
2929
},
30+
{
31+
testHeading: "Indeterminate",
32+
isIndeterminate: true,
33+
},
3034
{
3135
testHeading: "Invalid",
3236
isInvalid: true,
@@ -36,6 +40,11 @@ export const CheckboxGroup = Variants({
3640
isInvalid: true,
3741
isChecked: true,
3842
},
43+
{
44+
testHeading: "Invalid, indeterminate",
45+
isInvalid: true,
46+
isIndeterminate: true,
47+
},
3948
{
4049
testHeading: "Disabled",
4150
isDisabled: true,
@@ -46,7 +55,8 @@ export const CheckboxGroup = Variants({
4655
isChecked: true,
4756
},
4857
{
49-
testHeading: "Indeterminate",
58+
testHeading: "Disabled, indeterminate",
59+
isDisabled: true,
5060
isIndeterminate: true,
5161
},
5262
{
@@ -58,5 +68,15 @@ export const CheckboxGroup = Variants({
5868
isReadOnly: true,
5969
isChecked: true,
6070
},
71+
{
72+
testHeading: "Read-only, indeterminate",
73+
isReadOnly: true,
74+
isIndeterminate: true,
75+
},
76+
{
77+
testHeading: "Read-only, checked, disabled",
78+
isReadOnly: true,
79+
isChecked: true,
80+
},
6181
]
6282
});

components/checkbox/stories/template.js

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export const Template = ({
5050
typeof size !== "undefined",
5151
[`${rootClass}--emphasized`]: isEmphasized,
5252
["is-indeterminate"]: isIndeterminate,
53-
["is-disabled"]: isDisabled|| isReadOnly,
53+
["is-disabled"]: isDisabled,
5454
["is-invalid"]: isInvalid,
5555
["is-readOnly"]: isReadOnly,
5656
...customClasses.reduce((a, c) => ({ ...a, [c]: true }), {}),
@@ -62,13 +62,19 @@ export const Template = ({
6262
type="checkbox"
6363
class="${rootClass}-input"
6464
aria-labelledby=${ifDefined(ariaLabelledby)}
65+
aria-disabled=${ifDefined(isReadOnly ? "true" : undefined)}
6566
?checked=${isChecked}
66-
?disabled=${isDisabled || isReadOnly}
67+
?disabled=${isDisabled}
6768
title=${ifDefined(title)}
6869
value=${ifDefined(value)}
69-
@change=${function() {
70-
if (isDisabled) return;
71-
updateArgs({ isChecked: !isChecked });
70+
@change=${(e) => {
71+
if (isReadOnly) {
72+
// Make checked value immutable for read-only.
73+
e.preventDefault();
74+
e.target.checked = !e.target.checked;
75+
}
76+
if (isDisabled || isReadOnly) return;
77+
updateArgs?.({ isChecked: e.target.checked });
7278
}}
7379
id=${ifDefined(id ? `${id}-input` : undefined)}
7480
/>
@@ -103,21 +109,19 @@ export const DocsCheckboxGroup = (args, context) => Container({
103109
...args,
104110
context,
105111
iconName: undefined,
112+
label: "Unchecked",
106113
})}
107114
${Template({
108115
...args,
109116
context,
110117
isChecked: true,
118+
label: "Checked",
111119
})}
112120
${Template({
113121
...args,
114122
context,
115123
isIndeterminate: true,
116-
})}
117-
${Template({
118-
...args,
119-
context,
120-
isDisabled: true,
124+
label: "Indeterminate",
121125
})}
122126
${Template({
123127
...args,

components/fieldgroup/index.css

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,8 @@
1111
* governing permissions and limitations under the License.
1212
*/
1313

14-
/* fieldgroup/index.css
15-
*
16-
* fieldgroup contains four component dependences:
17-
* radio, checkbox, helptext, fieldlabel
18-
*
19-
*/
20-
21-
/* custom properties */
2214
.spectrum-FieldGroup {
2315
--spectrum-fieldgroup-margin: var(--spectrum-spacing-300);
24-
--spectrum-fieldgroup-readonly-delimiter: "\002c";
2516
}
2617

2718
/* field group */
@@ -65,17 +56,3 @@
6556
}
6657
}
6758
}
68-
69-
/* read-only checkbox group */
70-
.spectrum-FieldGroup {
71-
.spectrum-Checkbox.is-readOnly {
72-
.spectrum-Checkbox-box {
73-
display: none;
74-
}
75-
76-
/* read-only checkbox fields delimited by commas */
77-
&:not(:last-child) .spectrum-Checkbox-label::after {
78-
content: var(--spectrum-fieldgroup-readonly-delimiter);
79-
}
80-
}
81-
}

components/fieldgroup/metadata/metadata.json

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
"sourceFile": "index.css",
33
"selectors": [
44
".spectrum-FieldGroup",
5-
".spectrum-FieldGroup .spectrum-Checkbox.is-readOnly .spectrum-Checkbox-box",
6-
".spectrum-FieldGroup .spectrum-Checkbox.is-readOnly:not(:last-child) .spectrum-Checkbox-label:after",
75
".spectrum-FieldGroup--horizontal .spectrum-FieldGroupInputLayout",
86
".spectrum-FieldGroup--horizontal .spectrum-FieldGroupInputLayout .spectrum-FieldGroup-item:not(:last-child)",
97
".spectrum-FieldGroup--horizontal .spectrum-FieldGroupInputLayout .spectrum-HelpText",
@@ -13,10 +11,7 @@
1311
".spectrum-FieldGroupInputLayout"
1412
],
1513
"modifiers": [],
16-
"component": [
17-
"--spectrum-fieldgroup-margin",
18-
"--spectrum-fieldgroup-readonly-delimiter"
19-
],
14+
"component": ["--spectrum-fieldgroup-margin"],
2015
"global": ["--spectrum-spacing-300"],
2116
"system-theme": [],
2217
"passthroughs": [],

components/fieldgroup/stories/fieldgroup.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,13 @@ A horizontal group of fields:
5050

5151
### Invalid
5252

53-
An invalid group of fields:
53+
An invalid group of radio buttons or checkboxes is signified by negative help text.
5454

55-
#### Invalid Radio
55+
#### Invalid Radios
5656

5757
<Canvas of={FieldGroupStories.InvalidRadio} />
5858

59-
#### Invalid Checkbox
59+
#### Invalid Checkboxes
6060

6161
<Canvas of={FieldGroupStories.InvalidCheckbox} />
6262

0 commit comments

Comments
 (0)