Skip to content

Commit

Permalink
Toggle switch a11y take3 (#3510)
Browse files Browse the repository at this point in the history
* Address ToggleSwitch a11y feedback (#3251)

* Address ToggleSwitch a11y feedback

* Remove unused import

* Add changeset

* Fix tests

* Forward extra props to top-level box

* Add changeset

* Remove bad changeset
  • Loading branch information
camertron authored Jul 19, 2023
1 parent 435e6a3 commit bdbcfd1
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 44 deletions.
7 changes: 7 additions & 0 deletions .changeset/cool-hornets-call.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@primer/react': minor
---

Implement accessibility audit feedback for ToggleSwitch

<!-- Changed components: ToggleSwitch -->
5 changes: 5 additions & 0 deletions e2e/components/ToggleSwitch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ test.describe('ToggleSwitch', () => {
'color-contrast': {
enabled: theme !== 'dark_dimmed',
},

// the 'default' preview does not associate a label with the button
'button-name': {
enabled: false,
},
},
})
})
Expand Down
8 changes: 3 additions & 5 deletions src/ToggleSwitch/ToggleSwitch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import Text from '../Text'
import {get} from '../constants'
import {useProvidedStateOrCreate} from '../hooks'
import sx, {BetterSystemStyleObject, SxProp} from '../sx'
import VisuallyHidden from '../_VisuallyHidden'
import {CellAlignment} from '../DataTable/column'

const TRANSITION_DURATION = '80ms'
Expand Down Expand Up @@ -252,7 +251,8 @@ const ToggleSwitch: React.FC<React.PropsWithChildren<ToggleSwitchProps>> = ({
fontSize={size === 'small' ? 0 : 1}
mx={2}
aria-hidden="true"
sx={{position: 'relative'}}
sx={{position: 'relative', cursor: 'pointer'}}
onClick={handleToggleClick}
>
<Box textAlign="right" sx={isOn ? null : hiddenTextStyles}>
On
Expand All @@ -265,13 +265,11 @@ const ToggleSwitch: React.FC<React.PropsWithChildren<ToggleSwitchProps>> = ({
onClick={handleToggleClick}
aria-labelledby={ariaLabelledby}
aria-describedby={ariaDescribedby}
aria-checked={isOn}
role="switch"
aria-pressed={isOn}
checked={isOn}
size={size}
disabled={!acceptsInteraction}
>
<VisuallyHidden>{isOn ? 'On' : 'Off'}</VisuallyHidden>
<Box aria-hidden="true" display="flex" alignItems="center" width="100%" height="100%" overflow="hidden">
<Box
flexGrow={1}
Expand Down
35 changes: 25 additions & 10 deletions src/__tests__/ToggleSwitch.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ it('renders a switch that is turned off', () => {
)
const toggleSwitch = getByLabelText(SWITCH_LABEL_TEXT)

expect(toggleSwitch).toHaveAttribute('aria-checked', 'false')
expect(toggleSwitch).toHaveAttribute('aria-pressed', 'false')
})
it('renders a switch that is turned on', () => {
const {getByLabelText} = render(
Expand All @@ -34,7 +34,7 @@ it('renders a switch that is turned on', () => {
)
const toggleSwitch = getByLabelText(SWITCH_LABEL_TEXT)

expect(toggleSwitch).toHaveAttribute('aria-checked', 'true')
expect(toggleSwitch).toHaveAttribute('aria-pressed', 'true')
})
it('renders a switch that is disabled', async () => {
const user = userEvent.setup()
Expand All @@ -46,9 +46,9 @@ it('renders a switch that is disabled', async () => {
)
const toggleSwitch = getByLabelText(SWITCH_LABEL_TEXT)

expect(toggleSwitch).toHaveAttribute('aria-checked', 'false')
expect(toggleSwitch).toHaveAttribute('aria-pressed', 'false')
await user.click(toggleSwitch)
expect(toggleSwitch).toHaveAttribute('aria-checked', 'false')
expect(toggleSwitch).toHaveAttribute('aria-pressed', 'false')
})
it("renders a switch who's state is loading", async () => {
const user = userEvent.setup()
Expand All @@ -62,9 +62,9 @@ it("renders a switch who's state is loading", async () => {
const loadingSpinner = container.querySelector('svg')

expect(loadingSpinner).toBeDefined()
expect(toggleSwitch).toHaveAttribute('aria-checked', 'false')
expect(toggleSwitch).toHaveAttribute('aria-pressed', 'false')
await user.click(toggleSwitch)
expect(toggleSwitch).toHaveAttribute('aria-checked', 'false')
expect(toggleSwitch).toHaveAttribute('aria-pressed', 'false')
})
it('switches from off to on uncontrolled', async () => {
const user = userEvent.setup()
Expand All @@ -76,9 +76,24 @@ it('switches from off to on uncontrolled', async () => {
)
const toggleSwitch = getByLabelText(SWITCH_LABEL_TEXT)

expect(toggleSwitch).toHaveAttribute('aria-checked', 'false')
expect(toggleSwitch).toHaveAttribute('aria-pressed', 'false')
await user.click(toggleSwitch)
expect(toggleSwitch).toHaveAttribute('aria-checked', 'true')
expect(toggleSwitch).toHaveAttribute('aria-pressed', 'true')
})
it('switches from off to on uncontrolled when clicking on status label', async () => {
const user = userEvent.setup()
const {getByLabelText, getByText} = render(
<>
<div id="switchLabel">{SWITCH_LABEL_TEXT}</div>
<ToggleSwitch aria-labelledby="switchLabel" />
</>,
)
const toggleSwitch = getByLabelText(SWITCH_LABEL_TEXT)
const toggleSwitchStatusLabel = getByText('Off')

expect(toggleSwitch).toHaveAttribute('aria-pressed', 'false')
await user.click(toggleSwitchStatusLabel)
expect(toggleSwitch).toHaveAttribute('aria-pressed', 'true')
})
it('switches from off to on with a controlled prop', async () => {
const user = userEvent.setup()
Expand All @@ -99,9 +114,9 @@ it('switches from off to on with a controlled prop', async () => {
const {getByLabelText} = render(<ControlledSwitchComponent />)
const toggleSwitch = getByLabelText(SWITCH_LABEL_TEXT)

expect(toggleSwitch).toHaveAttribute('aria-checked', 'false')
expect(toggleSwitch).toHaveAttribute('aria-pressed', 'false')
await user.click(toggleSwitch)
expect(toggleSwitch).toHaveAttribute('aria-checked', 'true')
expect(toggleSwitch).toHaveAttribute('aria-pressed', 'true')
})
it('calls onChange when the switch is toggled', async () => {
const user = userEvent.setup()
Expand Down
41 changes: 12 additions & 29 deletions src/__tests__/__snapshots__/ToggleSwitch.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ exports[`renders consistently 1`] = `
text-align: right;
}
.c6 {
.c5 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
Expand All @@ -25,7 +25,7 @@ exports[`renders consistently 1`] = `
align-items: center;
}
.c7 {
.c6 {
color: #ffffff;
line-height: 0;
-webkit-box-flex: 1;
Expand All @@ -48,7 +48,7 @@ exports[`renders consistently 1`] = `
transition-duration: 80ms;
}
.c8 {
.c7 {
color: #656d76;
line-height: 0;
-webkit-box-flex: 1;
Expand Down Expand Up @@ -85,25 +85,13 @@ exports[`renders consistently 1`] = `
flex-direction: row;
}
.c5 {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
-webkit-clip: rect(0,0,0,0);
clip: rect(0,0,0,0);
white-space: nowrap;
border-width: 0;
}
.c1 {
font-size: 14px;
color: #1F2328;
margin-left: 8px;
margin-right: 8px;
position: relative;
cursor: pointer;
}
.c4 {
Expand Down Expand Up @@ -152,7 +140,7 @@ exports[`renders consistently 1`] = `
background-color: hsla(210,24%,88%,1);
}
.c9 {
.c8 {
background-color: #ffffff;
border-width: 1px;
border-style: solid;
Expand Down Expand Up @@ -202,7 +190,7 @@ exports[`renders consistently 1`] = `
}
@media (prefers-reduced-motion) {
.c9 {
.c8 {
-webkit-transition: none;
transition: none;
}
Expand All @@ -217,6 +205,7 @@ exports[`renders consistently 1`] = `
className="c1"
color="fg.default"
fontSize={1}
onClick={[Function]}
>
<div
className="c2"
Expand All @@ -230,29 +219,23 @@ exports[`renders consistently 1`] = `
</div>
</span>
<button
aria-checked={false}
aria-pressed={false}
checked={false}
className="c4"
disabled={false}
onClick={[Function]}
role="switch"
size="medium"
>
<span
className="c5"
>
Off
</span>
<div
aria-hidden="true"
className="c6"
className="c5"
display="flex"
height="100%"
overflow="hidden"
width="100%"
>
<div
className="c7"
className="c6"
color="switchTrack.checked.fg"
>
<svg
Expand All @@ -269,7 +252,7 @@ exports[`renders consistently 1`] = `
</svg>
</div>
<div
className="c8"
className="c7"
color="switchTrack.fg"
>
<svg
Expand All @@ -289,7 +272,7 @@ exports[`renders consistently 1`] = `
<div
aria-hidden="true"
checked={false}
className="c9"
className="c8"
disabled={false}
/>
</button>
Expand Down

0 comments on commit bdbcfd1

Please sign in to comment.