Skip to content

Commit

Permalink
fix(Spinner): use aria-label when defined, falling back to srText (#4685
Browse files Browse the repository at this point in the history
)

* fix(Spinner): use aria-label when defined, falling back to srText

* test(Spinner): add tests for aria-label support

* docs: update Spinner API docs

* chore: fix eslint warnings

---------

Co-authored-by: Josh Black <joshblack@users.noreply.github.com>
  • Loading branch information
joshblack and joshblack authored Jun 20, 2024
1 parent e957fef commit 0cf08f3
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 8 deletions.
2 changes: 1 addition & 1 deletion packages/react/src/Spinner/Spinner.docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
},
{
"name": "aria-label",
"type": "string | null",
"type": "string",
"description": "Sets the text conveyed by assistive technologies such as screen readers.",
"deprecated": true
},
Expand Down
11 changes: 6 additions & 5 deletions packages/react/src/Spinner/Spinner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ export type SpinnerProps = {
/** Sets the text conveyed by assistive technologies such as screen readers. Set to `null` if the loading state is displayed in a text node somewhere else on the page. */
srText?: string | null
/** @deprecated Use `srText` instead. */
'aria-label'?: string | null
'aria-label'?: string
} & HTMLDataAttributes &
SxProp

function Spinner({size: sizeKey = 'medium', srText = 'Loading', 'aria-label': ariaLabel, ...props}: SpinnerProps) {
const size = sizeMap[sizeKey]
const hasSrAnnouncement = Boolean(srText || ariaLabel)
const ariaLabelId = useId()
const hasHiddenLabel = srText !== null && ariaLabel === undefined
const labelId = useId()

return (
/* inline-flex removes the extra line height */
Expand All @@ -36,7 +36,8 @@ function Spinner({size: sizeKey = 'medium', srText = 'Loading', 'aria-label': ar
viewBox="0 0 16 16"
fill="none"
aria-hidden
aria-labelledby={ariaLabelId}
aria-label={ariaLabel ?? undefined}
aria-labelledby={hasHiddenLabel ? labelId : undefined}
{...props}
>
<circle
Expand All @@ -56,7 +57,7 @@ function Spinner({size: sizeKey = 'medium', srText = 'Loading', 'aria-label': ar
vectorEffect="non-scaling-stroke"
/>
</svg>
{hasSrAnnouncement ? <VisuallyHidden id={ariaLabelId}>{srText || ariaLabel}</VisuallyHidden> : null}
{hasHiddenLabel ? <VisuallyHidden id={labelId}>{srText}</VisuallyHidden> : null}
</Box>
)
}
Expand Down
9 changes: 7 additions & 2 deletions packages/react/src/__tests__/Spinner.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import axe from 'axe-core'
import type {SpinnerProps} from '..'
import {Spinner} from '..'
import {behavesAsComponent, checkExports} from '../utils/testing'
import {render as HTMLRender} from '@testing-library/react'
import {render as HTMLRender, screen} from '@testing-library/react'

describe('Spinner', () => {
behavesAsComponent({
Expand All @@ -26,12 +26,17 @@ describe('Spinner', () => {
expect(getByLabelText('Custom loading text')).toBeInTheDocument()
})

it('should not label the spinner with with loading text when `srText` is set to `null`', async () => {
it('should not label the spinner with with loading text when `srText` is set to `null`', () => {
const {getByLabelText} = HTMLRender(<Spinner srText={null} />)

expect(() => getByLabelText('Loading')).toThrow()
})

it('should use `aria-label` over `srText` if `aria-label` is provided', () => {
HTMLRender(<Spinner aria-label="Test label" />)
expect(screen.getByLabelText('Test label')).toBeInTheDocument()
})

it('should have no axe violations', async () => {
const {container} = HTMLRender(<Spinner />)
const results = await axe.run(container)
Expand Down

0 comments on commit 0cf08f3

Please sign in to comment.