@instructure/ui-checkbox 11.7.4-snapshot-121 → 11.7.4-snapshot-126
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -1
- package/es/Checkbox/v2/index.js +27 -13
- package/lib/Checkbox/v2/index.js +27 -13
- package/package.json +16 -16
- package/src/Checkbox/v2/index.tsx +41 -21
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/Checkbox/v2/index.d.ts +3 -0
- package/types/Checkbox/v2/index.d.ts.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
-
## [11.7.4-snapshot-
|
|
6
|
+
## [11.7.4-snapshot-126](https://github.com/instructure/instructure-ui/compare/v11.7.3...v11.7.4-snapshot-126) (2026-07-22)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **many:** keep form field messages out of the control's accessible name ([409d077](https://github.com/instructure/instructure-ui/commit/409d077a3095b318fd7d2e150bb01bc2732c9bf5))
|
|
12
|
+
* **many:** only reference rendered messages via aria-describedby ([ccc9516](https://github.com/instructure/instructure-ui/commit/ccc95165ec8dae6d06e001c0a20fcebef1cf9962))
|
|
7
13
|
|
|
8
14
|
|
|
9
15
|
### Features
|
package/es/Checkbox/v2/index.js
CHANGED
|
@@ -68,8 +68,12 @@ let Checkbox = (_dec = withDeterministicId(), _dec2 = withStyleNew(generateStyle
|
|
|
68
68
|
this.state.checked = !!props.defaultChecked;
|
|
69
69
|
}
|
|
70
70
|
this._defaultId = props.deterministicId();
|
|
71
|
+
this._messagesId = props.deterministicId('Checkbox-messages');
|
|
72
|
+
this._labelId = props.deterministicId('Checkbox-label');
|
|
71
73
|
}
|
|
72
74
|
_defaultId;
|
|
75
|
+
_messagesId;
|
|
76
|
+
_labelId;
|
|
73
77
|
_input = null;
|
|
74
78
|
ref = null;
|
|
75
79
|
handleRef = el => {
|
|
@@ -151,6 +155,9 @@ let Checkbox = (_dec = withDeterministicId(), _dec2 = withStyleNew(generateStyle
|
|
|
151
155
|
get isError() {
|
|
152
156
|
return !!this.props.messages?.find(m => m.type === 'error' || m.type === 'newError');
|
|
153
157
|
}
|
|
158
|
+
get hasMessages() {
|
|
159
|
+
return !!this.props.messages && this.props.messages.length > 0;
|
|
160
|
+
}
|
|
154
161
|
get invalid() {
|
|
155
162
|
return !!this.props.messages?.find(m => m.type === 'newError' || m.type === 'error');
|
|
156
163
|
}
|
|
@@ -175,8 +182,18 @@ let Checkbox = (_dec = withDeterministicId(), _dec2 = withStyleNew(generateStyle
|
|
|
175
182
|
focused
|
|
176
183
|
} = this.state;
|
|
177
184
|
error(!(variant === 'simple' && labelPlacement !== 'end'), `[Checkbox] The \`simple\` variant does not support the \`labelPlacement\` property. Use the \`toggle\` variant instead.`);
|
|
185
|
+
|
|
186
|
+
// Give the label its own id so messages stay out of the accessible name.
|
|
187
|
+
const labelContent = _jsxs("span", {
|
|
188
|
+
id: this._labelId,
|
|
189
|
+
children: [label, isRequired && label && _jsxs("span", {
|
|
190
|
+
css: this.invalid ? styles?.requiredInvalid : {},
|
|
191
|
+
"aria-hidden": true,
|
|
192
|
+
children: [' ', "*"]
|
|
193
|
+
})]
|
|
194
|
+
});
|
|
178
195
|
if (variant === 'toggle') {
|
|
179
|
-
return
|
|
196
|
+
return _jsx(ToggleFacade, {
|
|
180
197
|
disabled: disabled,
|
|
181
198
|
size: size,
|
|
182
199
|
hovered: hovered,
|
|
@@ -189,14 +206,10 @@ let Checkbox = (_dec = withDeterministicId(), _dec2 = withStyleNew(generateStyle
|
|
|
189
206
|
,
|
|
190
207
|
themeOverride: themeOverride,
|
|
191
208
|
invalid: this.invalid,
|
|
192
|
-
children:
|
|
193
|
-
css: this.invalid ? styles?.requiredInvalid : {},
|
|
194
|
-
"aria-hidden": true,
|
|
195
|
-
children: [' ', "*"]
|
|
196
|
-
})]
|
|
209
|
+
children: labelContent
|
|
197
210
|
});
|
|
198
211
|
} else {
|
|
199
|
-
return
|
|
212
|
+
return _jsx(CheckboxFacade, {
|
|
200
213
|
disabled: disabled,
|
|
201
214
|
size: size,
|
|
202
215
|
hovered: hovered,
|
|
@@ -206,11 +219,7 @@ let Checkbox = (_dec = withDeterministicId(), _dec2 = withStyleNew(generateStyle
|
|
|
206
219
|
indeterminate: indeterminate,
|
|
207
220
|
themeOverride: themeOverride,
|
|
208
221
|
invalid: this.invalid,
|
|
209
|
-
children:
|
|
210
|
-
css: this.invalid ? styles?.requiredInvalid : {},
|
|
211
|
-
"aria-hidden": true,
|
|
212
|
-
children: [' ', "*"]
|
|
213
|
-
})]
|
|
222
|
+
children: labelContent
|
|
214
223
|
});
|
|
215
224
|
}
|
|
216
225
|
}
|
|
@@ -225,6 +234,7 @@ let Checkbox = (_dec = withDeterministicId(), _dec2 = withStyleNew(generateStyle
|
|
|
225
234
|
margin: "small 0 0",
|
|
226
235
|
css: this.isError && (variant === 'toggle' ? styles?.indentedToggleError : styles?.indentedError),
|
|
227
236
|
children: _jsx(FormFieldMessages, {
|
|
237
|
+
id: this._messagesId,
|
|
228
238
|
messages: messages
|
|
229
239
|
})
|
|
230
240
|
}) : null;
|
|
@@ -268,7 +278,11 @@ let Checkbox = (_dec = withDeterministicId(), _dec2 = withStyleNew(generateStyle
|
|
|
268
278
|
readOnly: readOnly,
|
|
269
279
|
"aria-readonly": readOnly ? true : undefined,
|
|
270
280
|
"aria-checked": indeterminate ? 'mixed' : undefined,
|
|
271
|
-
"aria-invalid": this.invalid ? 'true' : undefined
|
|
281
|
+
"aria-invalid": this.invalid ? 'true' : undefined
|
|
282
|
+
// Keep messages in the description so the accessible name contains only the label.
|
|
283
|
+
,
|
|
284
|
+
"aria-labelledby": this.hasMessages ? props['aria-labelledby'] || this._labelId : props['aria-labelledby'],
|
|
285
|
+
"aria-describedby": [props['aria-describedby'], this.hasMessages ? this._messagesId : null].filter(Boolean).join(' ') || undefined,
|
|
272
286
|
css: styles?.input,
|
|
273
287
|
onClickCapture: e => {
|
|
274
288
|
if (readOnly) {
|
package/lib/Checkbox/v2/index.js
CHANGED
|
@@ -88,8 +88,12 @@ let Checkbox = exports.Checkbox = (_dec = (0, _withDeterministicId.withDetermini
|
|
|
88
88
|
this.state.checked = !!props.defaultChecked;
|
|
89
89
|
}
|
|
90
90
|
this._defaultId = props.deterministicId();
|
|
91
|
+
this._messagesId = props.deterministicId('Checkbox-messages');
|
|
92
|
+
this._labelId = props.deterministicId('Checkbox-label');
|
|
91
93
|
}
|
|
92
94
|
_defaultId;
|
|
95
|
+
_messagesId;
|
|
96
|
+
_labelId;
|
|
93
97
|
_input = null;
|
|
94
98
|
ref = null;
|
|
95
99
|
handleRef = el => {
|
|
@@ -171,6 +175,9 @@ let Checkbox = exports.Checkbox = (_dec = (0, _withDeterministicId.withDetermini
|
|
|
171
175
|
get isError() {
|
|
172
176
|
return !!this.props.messages?.find(m => m.type === 'error' || m.type === 'newError');
|
|
173
177
|
}
|
|
178
|
+
get hasMessages() {
|
|
179
|
+
return !!this.props.messages && this.props.messages.length > 0;
|
|
180
|
+
}
|
|
174
181
|
get invalid() {
|
|
175
182
|
return !!this.props.messages?.find(m => m.type === 'newError' || m.type === 'error');
|
|
176
183
|
}
|
|
@@ -195,8 +202,18 @@ let Checkbox = exports.Checkbox = (_dec = (0, _withDeterministicId.withDetermini
|
|
|
195
202
|
focused
|
|
196
203
|
} = this.state;
|
|
197
204
|
(0, _console.logError)(!(variant === 'simple' && labelPlacement !== 'end'), `[Checkbox] The \`simple\` variant does not support the \`labelPlacement\` property. Use the \`toggle\` variant instead.`);
|
|
205
|
+
|
|
206
|
+
// Give the label its own id so messages stay out of the accessible name.
|
|
207
|
+
const labelContent = (0, _jsxRuntime.jsxs)("span", {
|
|
208
|
+
id: this._labelId,
|
|
209
|
+
children: [label, isRequired && label && (0, _jsxRuntime.jsxs)("span", {
|
|
210
|
+
css: this.invalid ? styles?.requiredInvalid : {},
|
|
211
|
+
"aria-hidden": true,
|
|
212
|
+
children: [' ', "*"]
|
|
213
|
+
})]
|
|
214
|
+
});
|
|
198
215
|
if (variant === 'toggle') {
|
|
199
|
-
return (0, _jsxRuntime.
|
|
216
|
+
return (0, _jsxRuntime.jsx)(_index2.ToggleFacade, {
|
|
200
217
|
disabled: disabled,
|
|
201
218
|
size: size,
|
|
202
219
|
hovered: hovered,
|
|
@@ -209,14 +226,10 @@ let Checkbox = exports.Checkbox = (_dec = (0, _withDeterministicId.withDetermini
|
|
|
209
226
|
,
|
|
210
227
|
themeOverride: themeOverride,
|
|
211
228
|
invalid: this.invalid,
|
|
212
|
-
children:
|
|
213
|
-
css: this.invalid ? styles?.requiredInvalid : {},
|
|
214
|
-
"aria-hidden": true,
|
|
215
|
-
children: [' ', "*"]
|
|
216
|
-
})]
|
|
229
|
+
children: labelContent
|
|
217
230
|
});
|
|
218
231
|
} else {
|
|
219
|
-
return (0, _jsxRuntime.
|
|
232
|
+
return (0, _jsxRuntime.jsx)(_index.CheckboxFacade, {
|
|
220
233
|
disabled: disabled,
|
|
221
234
|
size: size,
|
|
222
235
|
hovered: hovered,
|
|
@@ -226,11 +239,7 @@ let Checkbox = exports.Checkbox = (_dec = (0, _withDeterministicId.withDetermini
|
|
|
226
239
|
indeterminate: indeterminate,
|
|
227
240
|
themeOverride: themeOverride,
|
|
228
241
|
invalid: this.invalid,
|
|
229
|
-
children:
|
|
230
|
-
css: this.invalid ? styles?.requiredInvalid : {},
|
|
231
|
-
"aria-hidden": true,
|
|
232
|
-
children: [' ', "*"]
|
|
233
|
-
})]
|
|
242
|
+
children: labelContent
|
|
234
243
|
});
|
|
235
244
|
}
|
|
236
245
|
}
|
|
@@ -245,6 +254,7 @@ let Checkbox = exports.Checkbox = (_dec = (0, _withDeterministicId.withDetermini
|
|
|
245
254
|
margin: "small 0 0",
|
|
246
255
|
css: this.isError && (variant === 'toggle' ? styles?.indentedToggleError : styles?.indentedError),
|
|
247
256
|
children: (0, _jsxRuntime.jsx)(_latest.FormFieldMessages, {
|
|
257
|
+
id: this._messagesId,
|
|
248
258
|
messages: messages
|
|
249
259
|
})
|
|
250
260
|
}) : null;
|
|
@@ -288,7 +298,11 @@ let Checkbox = exports.Checkbox = (_dec = (0, _withDeterministicId.withDetermini
|
|
|
288
298
|
readOnly: readOnly,
|
|
289
299
|
"aria-readonly": readOnly ? true : undefined,
|
|
290
300
|
"aria-checked": indeterminate ? 'mixed' : undefined,
|
|
291
|
-
"aria-invalid": this.invalid ? 'true' : undefined
|
|
301
|
+
"aria-invalid": this.invalid ? 'true' : undefined
|
|
302
|
+
// Keep messages in the description so the accessible name contains only the label.
|
|
303
|
+
,
|
|
304
|
+
"aria-labelledby": this.hasMessages ? props['aria-labelledby'] || this._labelId : props['aria-labelledby'],
|
|
305
|
+
"aria-describedby": [props['aria-describedby'], this.hasMessages ? this._messagesId : null].filter(Boolean).join(' ') || undefined,
|
|
292
306
|
css: styles?.input,
|
|
293
307
|
onClickCapture: e => {
|
|
294
308
|
if (readOnly) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@instructure/ui-checkbox",
|
|
3
|
-
"version": "11.7.4-snapshot-
|
|
3
|
+
"version": "11.7.4-snapshot-126",
|
|
4
4
|
"description": " styled HTML input type='checkbox' component.",
|
|
5
5
|
"author": "Instructure, Inc. Engineering and Product Design",
|
|
6
6
|
"module": "./es/index.js",
|
|
@@ -16,27 +16,27 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@babel/runtime": "^7.29.7",
|
|
18
18
|
"keycode": "^2",
|
|
19
|
-
"@instructure/console": "11.7.4-snapshot-
|
|
20
|
-
"@instructure/emotion": "11.7.4-snapshot-
|
|
21
|
-
"@instructure/shared-types": "11.7.4-snapshot-
|
|
22
|
-
"@instructure/ui-
|
|
23
|
-
"@instructure/ui-
|
|
24
|
-
"@instructure/ui-
|
|
25
|
-
"@instructure/ui-
|
|
26
|
-
"@instructure/ui-
|
|
27
|
-
"@instructure/ui-utils": "11.7.4-snapshot-
|
|
28
|
-
"@instructure/
|
|
29
|
-
"@instructure/
|
|
30
|
-
"@instructure/ui-
|
|
19
|
+
"@instructure/console": "11.7.4-snapshot-126",
|
|
20
|
+
"@instructure/emotion": "11.7.4-snapshot-126",
|
|
21
|
+
"@instructure/shared-types": "11.7.4-snapshot-126",
|
|
22
|
+
"@instructure/ui-dom-utils": "11.7.4-snapshot-126",
|
|
23
|
+
"@instructure/ui-form-field": "11.7.4-snapshot-126",
|
|
24
|
+
"@instructure/ui-svg-images": "11.7.4-snapshot-126",
|
|
25
|
+
"@instructure/ui-themes": "11.7.4-snapshot-126",
|
|
26
|
+
"@instructure/ui-utils": "11.7.4-snapshot-126",
|
|
27
|
+
"@instructure/ui-react-utils": "11.7.4-snapshot-126",
|
|
28
|
+
"@instructure/uid": "11.7.4-snapshot-126",
|
|
29
|
+
"@instructure/ui-view": "11.7.4-snapshot-126",
|
|
30
|
+
"@instructure/ui-icons": "11.7.4-snapshot-126"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@testing-library/jest-dom": "^6.9.1",
|
|
34
34
|
"@testing-library/react": "16.3.2",
|
|
35
35
|
"@testing-library/user-event": "^14.6.1",
|
|
36
36
|
"vitest": "^4.1.9",
|
|
37
|
-
"@instructure/ui-
|
|
38
|
-
"@instructure/ui-color-utils": "11.7.4-snapshot-
|
|
39
|
-
"@instructure/ui-
|
|
37
|
+
"@instructure/ui-babel-preset": "11.7.4-snapshot-126",
|
|
38
|
+
"@instructure/ui-color-utils": "11.7.4-snapshot-126",
|
|
39
|
+
"@instructure/ui-axe-check": "11.7.4-snapshot-126"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"react": ">=18 <=19"
|
|
@@ -84,8 +84,12 @@ class Checkbox extends Component<CheckboxProps, CheckboxState> {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
this._defaultId = props.deterministicId!()
|
|
87
|
+
this._messagesId = props.deterministicId!('Checkbox-messages')
|
|
88
|
+
this._labelId = props.deterministicId!('Checkbox-label')
|
|
87
89
|
}
|
|
88
90
|
private readonly _defaultId: string
|
|
91
|
+
private readonly _messagesId: string
|
|
92
|
+
private readonly _labelId: string
|
|
89
93
|
private _input: HTMLInputElement | null = null
|
|
90
94
|
|
|
91
95
|
ref: Element | null = null
|
|
@@ -186,6 +190,10 @@ class Checkbox extends Component<CheckboxProps, CheckboxState> {
|
|
|
186
190
|
)
|
|
187
191
|
}
|
|
188
192
|
|
|
193
|
+
get hasMessages() {
|
|
194
|
+
return !!this.props.messages && this.props.messages.length > 0
|
|
195
|
+
}
|
|
196
|
+
|
|
189
197
|
get invalid() {
|
|
190
198
|
return !!this.props.messages?.find(
|
|
191
199
|
(m) => m.type === 'newError' || m.type === 'error'
|
|
@@ -217,6 +225,22 @@ class Checkbox extends Component<CheckboxProps, CheckboxState> {
|
|
|
217
225
|
`[Checkbox] The \`simple\` variant does not support the \`labelPlacement\` property. Use the \`toggle\` variant instead.`
|
|
218
226
|
)
|
|
219
227
|
|
|
228
|
+
// Give the label its own id so messages stay out of the accessible name.
|
|
229
|
+
const labelContent = (
|
|
230
|
+
<span id={this._labelId}>
|
|
231
|
+
{label}
|
|
232
|
+
{isRequired && label && (
|
|
233
|
+
<span
|
|
234
|
+
css={this.invalid ? styles?.requiredInvalid : {}}
|
|
235
|
+
aria-hidden={true}
|
|
236
|
+
>
|
|
237
|
+
{' '}
|
|
238
|
+
*
|
|
239
|
+
</span>
|
|
240
|
+
)}
|
|
241
|
+
</span>
|
|
242
|
+
)
|
|
243
|
+
|
|
220
244
|
if (variant === 'toggle') {
|
|
221
245
|
return (
|
|
222
246
|
<ToggleFacade
|
|
@@ -232,16 +256,7 @@ class Checkbox extends Component<CheckboxProps, CheckboxState> {
|
|
|
232
256
|
themeOverride={themeOverride}
|
|
233
257
|
invalid={this.invalid}
|
|
234
258
|
>
|
|
235
|
-
{
|
|
236
|
-
{isRequired && label && (
|
|
237
|
-
<span
|
|
238
|
-
css={this.invalid ? styles?.requiredInvalid : {}}
|
|
239
|
-
aria-hidden={true}
|
|
240
|
-
>
|
|
241
|
-
{' '}
|
|
242
|
-
*
|
|
243
|
-
</span>
|
|
244
|
-
)}
|
|
259
|
+
{labelContent}
|
|
245
260
|
</ToggleFacade>
|
|
246
261
|
)
|
|
247
262
|
} else {
|
|
@@ -257,16 +272,7 @@ class Checkbox extends Component<CheckboxProps, CheckboxState> {
|
|
|
257
272
|
themeOverride={themeOverride}
|
|
258
273
|
invalid={this.invalid}
|
|
259
274
|
>
|
|
260
|
-
{
|
|
261
|
-
{isRequired && label && (
|
|
262
|
-
<span
|
|
263
|
-
css={this.invalid ? styles?.requiredInvalid : {}}
|
|
264
|
-
aria-hidden={true}
|
|
265
|
-
>
|
|
266
|
-
{' '}
|
|
267
|
-
*
|
|
268
|
-
</span>
|
|
269
|
-
)}
|
|
275
|
+
{labelContent}
|
|
270
276
|
</CheckboxFacade>
|
|
271
277
|
)
|
|
272
278
|
}
|
|
@@ -286,7 +292,7 @@ class Checkbox extends Component<CheckboxProps, CheckboxState> {
|
|
|
286
292
|
: styles?.indentedError)
|
|
287
293
|
}
|
|
288
294
|
>
|
|
289
|
-
<FormFieldMessages messages={messages} />
|
|
295
|
+
<FormFieldMessages id={this._messagesId} messages={messages} />
|
|
290
296
|
</View>
|
|
291
297
|
) : null
|
|
292
298
|
}
|
|
@@ -338,6 +344,20 @@ class Checkbox extends Component<CheckboxProps, CheckboxState> {
|
|
|
338
344
|
aria-readonly={readOnly ? true : undefined}
|
|
339
345
|
aria-checked={indeterminate ? 'mixed' : undefined}
|
|
340
346
|
aria-invalid={this.invalid ? 'true' : undefined}
|
|
347
|
+
// Keep messages in the description so the accessible name contains only the label.
|
|
348
|
+
aria-labelledby={
|
|
349
|
+
this.hasMessages
|
|
350
|
+
? props['aria-labelledby'] || this._labelId
|
|
351
|
+
: props['aria-labelledby']
|
|
352
|
+
}
|
|
353
|
+
aria-describedby={
|
|
354
|
+
[
|
|
355
|
+
props['aria-describedby'],
|
|
356
|
+
this.hasMessages ? this._messagesId : null
|
|
357
|
+
]
|
|
358
|
+
.filter(Boolean)
|
|
359
|
+
.join(' ') || undefined
|
|
360
|
+
}
|
|
341
361
|
css={styles?.input}
|
|
342
362
|
onClickCapture={(e) => {
|
|
343
363
|
if (readOnly) {
|