diff --git a/packages/react-charts/src/components/Skeletons/examples/skeletons.md b/packages/react-charts/src/components/Skeletons/examples/skeletons.md
index 69a6baae290..364d2314609 100644
--- a/packages/react-charts/src/components/Skeletons/examples/skeletons.md
+++ b/packages/react-charts/src/components/Skeletons/examples/skeletons.md
@@ -50,8 +50,7 @@ export const ChartAreaSkeleton: React.FunctionComponent = () => {
<>
@@ -131,8 +130,7 @@ export const ChartBarSkeleton: React.FunctionComponent = () => {
<>
@@ -188,8 +186,7 @@ export const ChartBoxPlotSkeleton: React.FunctionComponent = () => {
<>
@@ -246,8 +243,7 @@ export const ChartBulletSkeleton: React.FunctionComponent = () => {
<>
@@ -300,8 +296,7 @@ export const ChartDonutSkeleton: React.FunctionComponent = () => {
<>
@@ -339,8 +334,7 @@ export const ChartDonutUtilizationSkeleton: React.FunctionComponent = () => {
<>
@@ -388,8 +382,7 @@ export const ChartDonutUtilizationSkeleton: React.FunctionComponent = () => {
<>
@@ -432,8 +425,7 @@ export const ChartLineSkeleton: React.FunctionComponent = () => {
<>
@@ -522,8 +514,7 @@ export const ChartPieSkeleton: React.FunctionComponent = () => {
<>
@@ -570,8 +561,7 @@ export const ChartScatterSkeleton: React.FunctionComponent = () => {
<>
@@ -627,8 +617,7 @@ export const ChartStackSkeleton: React.FunctionComponent = () => {
<>
@@ -684,8 +673,7 @@ export const ChartThresholdSkeleton: React.FunctionComponent = () => {
<>
diff --git a/packages/react-core/src/components/Switch/Switch.tsx b/packages/react-core/src/components/Switch/Switch.tsx
index c428080babf..118d1d23b24 100644
--- a/packages/react-core/src/components/Switch/Switch.tsx
+++ b/packages/react-core/src/components/Switch/Switch.tsx
@@ -12,10 +12,12 @@ export interface SwitchProps
id?: string;
/** Additional classes added to the switch */
className?: string;
- /** Text value for the visible label when on */
+ /** Text value for the visible label */
label?: React.ReactNode;
- /** Text value for the visible label when off */
- labelOff?: React.ReactNode;
+ /** Adds an accessible name to the switch when the label prop is not passed, and must describe the isChecked="true" state. */
+ 'aria-label'?: string;
+ /** Adds an accessible name to the switch via one or more referenced id(s). The computed accessible name must describe the isChecked="true" state. */
+ 'aria-labelledby'?: string;
/** Flag to show if the switch is checked when it is controlled by React state.
* To make the switch uncontrolled instead use the defaultChecked prop, but do not use both.
*/
@@ -30,9 +32,7 @@ export interface SwitchProps
isDisabled?: boolean;
/** A callback for when the switch selection changes. (event, isChecked) => {} */
onChange?: (event: React.FormEvent, checked: boolean) => void;
- /** Adds accessible text to the switch, and should describe the isChecked="true" state. When label is defined, aria-label should be set to the text string that is visible when isChecked is true. */
- 'aria-label'?: string;
- /** Flag to reverse the layout of toggle and label (toggle on right). */
+ /** Flag to reverse the layout of toggle and label (label at start, toggle at end). */
isReversed?: boolean;
/** Value to overwrite the randomly generated data-ouia-component-id.*/
ouiaId?: number | string;
@@ -48,15 +48,18 @@ class Switch extends React.Component undefined as any
};
constructor(props: SwitchProps & OUIAProps) {
super(props);
- if (!props.label && !props['aria-label']) {
+ if (!props.label && !props['aria-label'] && !props['aria-labelledby']) {
// eslint-disable-next-line no-console
- console.error('Switch: Switch requires either a label or an aria-label to be specified');
+ console.error(
+ 'Switch: Switch requires at least one of label, aria-labelledby, or aria-label props to be specified'
+ );
}
this.id = props.id || getUniqueId();
@@ -71,7 +74,6 @@ class Switch extends React.Component onChange(event, event.target.checked)}
{...(defaultChecked !== undefined ? { defaultChecked } : { checked: isChecked })}
disabled={isDisabled}
- aria-labelledby={!isAriaLabelledBy ? null : `${this.id}-${isChecked !== true ? 'off' : 'on'}`}
+ aria-labelledby={isAriaLabelledBy ? ariaLabelledByIds : null}
+ aria-label={ariaLabel}
{...props}
/>
{label !== undefined ? (
@@ -110,19 +120,12 @@ class Switch extends React.Component
{label}
-
- {labelOff !== undefined ? labelOff : label}
-
) : (
diff --git a/packages/react-core/src/components/Switch/__tests__/Switch.test.tsx b/packages/react-core/src/components/Switch/__tests__/Switch.test.tsx
index 79459ccbe14..2be2a8a64aa 100644
--- a/packages/react-core/src/components/Switch/__tests__/Switch.test.tsx
+++ b/packages/react-core/src/components/Switch/__tests__/Switch.test.tsx
@@ -26,15 +26,13 @@ describe('Switch', () => {
});
test('switch is checked', () => {
- const { asFragment } = render(
-
- );
+ const { asFragment } = render();
expect(asFragment()).toMatchSnapshot();
});
test('switch is not checked', () => {
const { asFragment } = render(
-
+
);
expect(asFragment()).toMatchSnapshot();
});
@@ -84,7 +82,7 @@ describe('Switch', () => {
expect(props.onChange).toHaveBeenCalledWith(expect.any(Object), true);
});
- test('should throw console error when no aria-label or label is given', () => {
+ test('should throw console error when no aria-label, aria-labelledby, or label is given', () => {
const myMock = jest.fn();
global.console = { ...global.console, error: myMock };
@@ -93,7 +91,7 @@ describe('Switch', () => {
expect(myMock).toHaveBeenCalled();
});
- test('should not throw console error when label is given but no aria-label', () => {
+ test('should not throw console error when label is given', () => {
const myMock = jest.fn();
global.console = { ...global.console, error: myMock };
@@ -102,7 +100,7 @@ describe('Switch', () => {
expect(myMock).not.toHaveBeenCalled();
});
- test('should not throw console error when aria-label is given but no label', () => {
+ test('should not throw console error when aria-label is given', () => {
const myMock = jest.fn();
global.console = { ...global.console, error: myMock };
@@ -111,6 +109,15 @@ describe('Switch', () => {
expect(myMock).not.toHaveBeenCalled();
});
+ test('should not throw console error when aria-labelledby is given', () => {
+ const myMock = jest.fn();
+ global.console = { ...global.console, error: myMock };
+
+ render();
+
+ expect(myMock).not.toHaveBeenCalled();
+ });
+
test('should apply reversed modifier', () => {
render();
expect(screen.getByLabelText('Switch label').parentElement).toHaveClass('pf-m-reverse');
diff --git a/packages/react-core/src/components/Switch/__tests__/__snapshots__/Switch.test.tsx.snap b/packages/react-core/src/components/Switch/__tests__/__snapshots__/Switch.test.tsx.snap
index 34977741f9b..19a663096e2 100644
--- a/packages/react-core/src/components/Switch/__tests__/__snapshots__/Switch.test.tsx.snap
+++ b/packages/react-core/src/components/Switch/__tests__/__snapshots__/Switch.test.tsx.snap
@@ -14,6 +14,7 @@ exports[`Switch no label switch is checked 1`] = `
checked=""
class="pf-v6-c-switch__input"
id="no-label-switch-is-checked"
+ role="switch"
type="checkbox"
/>
On
-
- Off
-
`;
@@ -133,6 +130,7 @@ exports[`Switch switch is checked and disabled 1`] = `
class="pf-v6-c-switch__input"
disabled=""
id="switch-is-checked-and-disabled"
+ role="switch"
type="checkbox"
/>
On
-
- Off
-
`;
@@ -209,6 +202,7 @@ exports[`Switch switch is not checked and disabled 1`] = `
class="pf-v6-c-switch__input"
disabled=""
id="switch-is-not-checked-and-disabled"
+ role="switch"
type="checkbox"
/>
- On
-
-
On
@@ -285,10 +272,10 @@ exports[`Switch switch with only label is not checked 1`] = `
for="switch-is-not-checked"
>
- Off
-
-
Off
diff --git a/packages/react-core/src/components/Switch/examples/Switch.md b/packages/react-core/src/components/Switch/examples/Switch.md
index 2db584f3fc3..9ab189a9df8 100644
--- a/packages/react-core/src/components/Switch/examples/Switch.md
+++ b/packages/react-core/src/components/Switch/examples/Switch.md
@@ -8,32 +8,40 @@ ouia: true
## Examples
+To keep inline with accessibility guidelines, the label for a switch must never dynamically change. A dynamically changing label (such as one label for an "on" state and another label for an "off" state) can be confusing as the contexts for each label changes as the switch state does. This applies to both visible text labels as well as labels provided via `aria-label`.
+
### Basic
```ts file="./SwitchBasic.tsx"
+
```
### Reversed Layout
```ts file="./SwitchReversed.tsx"
+
```
### Without label
```ts file="./SwitchWithoutLabel.tsx"
+
```
### Checked with label
```ts file="./SwitchCheckedWithLabel.tsx"
+
```
### Disabled
```ts file="./SwitchDisabled.tsx"
+
```
### Uncontrolled
```ts file="./SwitchUncontrolled.tsx"
+
```
diff --git a/packages/react-core/src/components/Switch/examples/SwitchBasic.tsx b/packages/react-core/src/components/Switch/examples/SwitchBasic.tsx
index f0786ad2cbf..3afdc7f068f 100644
--- a/packages/react-core/src/components/Switch/examples/SwitchBasic.tsx
+++ b/packages/react-core/src/components/Switch/examples/SwitchBasic.tsx
@@ -11,8 +11,7 @@ export const SwitchBasic: React.FunctionComponent = () => {
return (
{
return (
(
+
+
+
+
-
-
-
-
);
diff --git a/packages/react-core/src/components/Switch/examples/SwitchReversed.tsx b/packages/react-core/src/components/Switch/examples/SwitchReversed.tsx
index 262249b39a0..cb94a7a8652 100644
--- a/packages/react-core/src/components/Switch/examples/SwitchReversed.tsx
+++ b/packages/react-core/src/components/Switch/examples/SwitchReversed.tsx
@@ -11,8 +11,7 @@ export const SwitchReversed: React.FunctionComponent = () => {
return (
(
-
+
);
diff --git a/packages/react-core/src/components/Switch/examples/SwitchWithoutLabel.tsx b/packages/react-core/src/components/Switch/examples/SwitchWithoutLabel.tsx
index 18f822ca371..18a01776ca8 100644
--- a/packages/react-core/src/components/Switch/examples/SwitchWithoutLabel.tsx
+++ b/packages/react-core/src/components/Switch/examples/SwitchWithoutLabel.tsx
@@ -8,5 +8,12 @@ export const SwitchWithoutLabel: React.FunctionComponent = () => {
setIsChecked(checked);
};
- return ;
+ return (
+
+ );
};
diff --git a/packages/react-core/src/demos/JumpLinks.md b/packages/react-core/src/demos/JumpLinks.md
index 4510ebf6975..d7791146942 100644
--- a/packages/react-core/src/demos/JumpLinks.md
+++ b/packages/react-core/src/demos/JumpLinks.md
@@ -72,8 +72,7 @@ ScrollspyH2 = () => {
setIsVertical(check)}
/>
diff --git a/packages/react-integration/demo-app-ts/src/components/demos/SwitchDemo/SwitchDemo.tsx b/packages/react-integration/demo-app-ts/src/components/demos/SwitchDemo/SwitchDemo.tsx
index 91500a3fff8..955257fadcc 100644
--- a/packages/react-integration/demo-app-ts/src/components/demos/SwitchDemo/SwitchDemo.tsx
+++ b/packages/react-integration/demo-app-ts/src/components/demos/SwitchDemo/SwitchDemo.tsx
@@ -54,7 +54,6 @@ export class SwitchDemo extends Component<{}, SwitchState> {
Message when on}
- labelOff={Message when off
}
onChange={this.handleChangeSimple}
aria-label="Switch"
isChecked={isChecked}
@@ -62,7 +61,6 @@ export class SwitchDemo extends Component<{}, SwitchState> {