Skip to content

Commit 5aff418

Browse files
fix(toggle): make props.labelText optional (#13196)
* fix(toggle): make props.labelText optional * docs(toggle): add 'with accessible labels' story * test(toggle): update public api snapshot --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
1 parent 07168bf commit 5aff418

File tree

4 files changed

+45
-21
lines changed

4 files changed

+45
-21
lines changed

packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8528,7 +8528,9 @@ Map {
85288528
"labelB": Object {
85298529
"type": "node",
85308530
},
8531-
"labelText": [Function],
8531+
"labelText": Object {
8532+
"type": "string",
8533+
},
85328534
"onClick": Object {
85338535
"type": "func",
85348536
},

packages/react/src/components/Toggle/Toggle-test.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,15 @@ describe('Toggle', () => {
9292
).toBe(props.labelText);
9393
});
9494

95-
it("doesn't render sideLabel if props.hideLabel and props['aria-labelledby'] are provided", () => {
95+
it("doesn't render sideLabel if props.hideLabel and no props.labelText is provided", () => {
9696
const externalElementId = 'external-element-id';
9797
wrapper.rerender(
98-
<Toggle {...props} hideLabel aria-labelledby={externalElementId} />
98+
<Toggle
99+
{...props}
100+
hideLabel
101+
labelText={null}
102+
aria-labelledby={externalElementId}
103+
/>
99104
);
100105

101106
expect(

packages/react/src/components/Toggle/Toggle.js

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ export function Toggle({
4747

4848
const isSm = size === 'sm';
4949
const sideLabel = hideLabel ? labelText : checked ? labelB : labelA;
50-
const renderSideLabel = !(hideLabel && ariaLabelledby);
51-
const LabelComponent = ariaLabelledby ? 'div' : 'label';
50+
const renderSideLabel = !(hideLabel && !labelText);
51+
const LabelComponent = labelText ? 'label' : 'div';
5252

5353
const wrapperClasses = classNames(
5454
`${prefix}--toggle`,
@@ -76,7 +76,7 @@ export function Toggle({
7676
<div
7777
className={wrapperClasses}
7878
onClick={
79-
ariaLabelledby
79+
!labelText
8080
? (e) => {
8181
// the underlying <button> can only be activated by keyboard as it is visually hidden;
8282
// therefore, if this event's target is the <button>, it had to be triggered by
@@ -152,10 +152,7 @@ Toggle.propTypes = {
152152

153153
/**
154154
* If true, the side labels (props.labelA and props.labelB) will be replaced by
155-
* props.labelText, so that the toggle doesn't render a top label. In order to fully
156-
* hide any labels, you can use props['aria-labelledby'] to refer to another element
157-
* that labels the toggle. props.labelText would no longer be required in that case
158-
* and can therefore be omitted.
155+
* props.labelText (if passed), so that the toggle doesn't render a top label.
159156
*/
160157
hideLabel: PropTypes.bool,
161158

@@ -176,17 +173,11 @@ Toggle.propTypes = {
176173

177174
/**
178175
* Provide the text that will be read by a screen reader when visiting this
179-
* control. This is required unless 'aria-labelledby' is provided instead
176+
* control. This should be provided unless 'aria-labelledby' is set instead
177+
* or you use an external <label> element with its "for" attribute set to the
178+
* toggle's id.
180179
*/
181-
labelText: (props, ...rest) => {
182-
if (!props['aria-labelledby'] && !props.labelText) {
183-
return new Error(
184-
'labelText property is required if no aria-labelledby is provided.'
185-
);
186-
}
187-
188-
return PropTypes.node(props, ...rest);
189-
},
180+
labelText: PropTypes.string,
190181

191182
/**
192183
* Provide an event listener that is called when the control is clicked

packages/react/src/components/Toggle/Toggle.stories.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
*/
77

88
import React from 'react';
9+
10+
import { VStack } from '../Stack';
911
import Toggle from '../Toggle';
1012

1113
export default {
@@ -35,7 +37,7 @@ export const SmallToggle = () => (
3537
);
3638

3739
export const Playground = (args) => (
38-
<Toggle labelA="Off" labelB="On" defaultToggled id="toggle-1" {...args} />
40+
<Toggle labelA="Off" labelB="On" defaultToggled id="toggle-3" {...args} />
3941
);
4042

4143
Playground.argTypes = {
@@ -80,3 +82,27 @@ Playground.argTypes = {
8082
},
8183
},
8284
};
85+
86+
export const WithAcessibleLabels = () => (
87+
<VStack gap={7}>
88+
<Toggle id="toggle-4" labelText="Toggle label" />
89+
90+
<Toggle id="toggle-5" labelText="Toggle label" hideLabel />
91+
92+
<div>
93+
<div id="toggle-6-label" style={{ marginBlockEnd: '0.5rem' }}>
94+
External toggle label
95+
</div>
96+
<Toggle id="toggle-6" aria-labelledby="toggle-6-label" hideLabel />
97+
</div>
98+
99+
<div>
100+
<label
101+
htmlFor="toggle-7"
102+
style={{ display: 'block', marginBlockEnd: '0.5rem' }}>
103+
External toggle label
104+
</label>
105+
<Toggle id="toggle-7" hideLabel />
106+
</div>
107+
</VStack>
108+
);

0 commit comments

Comments
 (0)