diff --git a/.agents/skills/a11y-fix/SKILL.md b/.agents/skills/a11y-fix/SKILL.md index 627bd43edb..42e9aefb27 100644 --- a/.agents/skills/a11y-fix/SKILL.md +++ b/.agents/skills/a11y-fix/SKILL.md @@ -70,7 +70,14 @@ The a11y test suite generated in Step 5 should now pass. Fix any failures you in ### Step 8: Update audit artifacts -1. **`A11y.mdx`** — for each resolved issue, mark it ✅ and move the detail under a **Resolved** subsection (keep the original note for traceability). Update the **Summary** lines (✅/⚠️/❌) to reflect the new state. Wrap the whole "Accessibility issues" section in a details/summary and rename it to "Past accessibility issues". +1. **`A11y.mdx`** — for each resolved issue, mark it ✅ and keep the original note for traceability. Update the **Summary** lines (✅/⚠️/❌) to reflect the new state. Wrap the whole "Accessibility issues" section in a `
` block and rename it to "Past accessibility issues". Within that section, format each issue as a list item whose first line is the issue title (with ✅/⚠️/❌) and whose details (description, resolution, or status) go in a **nested sub-list** below it, e.g.: + ```mdx + - ✅ Missing ARIA props on button: + - The following ARIA props were defined but NOT passed to the native `
## Dependencies diff --git a/packages/ui/src/components/Button/__stories__/index.stories.tsx b/packages/ui/src/components/Button/__stories__/index.stories.tsx index 8c8f511e4d..f8f7672b12 100644 --- a/packages/ui/src/components/Button/__stories__/index.stories.tsx +++ b/packages/ui/src/components/Button/__stories__/index.stories.tsx @@ -7,9 +7,9 @@ export default { parameters: { a11yStatus: { perceivable: false, - operable: false, + operable: true, understandable: true, - robust: false, + robust: true, }, }, } as Meta diff --git a/packages/ui/src/components/Button/__tests__/__snapshots__/index.test.tsx.snap b/packages/ui/src/components/Button/__tests__/__snapshots__/index.test.tsx.snap index 984a070d55..848c78bd64 100644 --- a/packages/ui/src/components/Button/__tests__/__snapshots__/index.test.tsx.snap +++ b/packages/ui/src/components/Button/__tests__/__snapshots__/index.test.tsx.snap @@ -6,10 +6,8 @@ exports[`button > render as an anchor with href prop 1`] = ` data-testid="testing" > Scaleway diff --git a/packages/ui/src/components/Button/__tests__/a11y.test.tsx b/packages/ui/src/components/Button/__tests__/a11y.test.tsx index 1d3db99907..3c67f0361c 100644 --- a/packages/ui/src/components/Button/__tests__/a11y.test.tsx +++ b/packages/ui/src/components/Button/__tests__/a11y.test.tsx @@ -1,28 +1,73 @@ import { PencilIcon } from '@ultraviolet/icons/PencilIcon' import { consoleThemesMap } from '@ultraviolet/themes' import { renderWithTheme, expectNoViolations } from '@utils/test' -import { describe, it } from 'vitest' +import { describe, expect, it } from 'vitest' import { Button } from '..' describe('button - A11y', { tags: ['a11y'] }, () => { - it.for([...consoleThemesMap.entries()])('should not have violations with (theme: %s)', async ([, currentTheme]) => { + it.for([...consoleThemesMap.entries()])( + 'should not have violations with default props (theme: %s)', + async ([, currentTheme]) => { + const { container } = renderWithTheme( + , + currentTheme, + ) + + await expectNoViolations(container) + }, + ) + + it('should not have violations with icon-only button labelled via accessibleLabel', async () => { const { container } = renderWithTheme( - , - currentTheme, ) await expectNoViolations(container) }) - it.todo.for([...consoleThemesMap.entries()])( - 'should not have violations with tooltips (theme: %s )', - async ([, currentTheme]) => { - const { container } = renderWithTheme(, currentTheme) + it('should not have violations with tooltipLabel', async () => { + const { container } = renderWithTheme( + , + ) - await expectNoViolations(container) - }, - ) + await expectNoViolations(container) + }) + + it('forwards ARIA attributes to the native button', () => { + const { getByRole } = renderWithTheme( + , + ) + const button = getByRole('button') + + expect(button).toHaveAttribute('aria-describedby', 'desc-id') + expect(button).toHaveAttribute('aria-disabled', 'true') + expect(button).toHaveAttribute('aria-pressed', 'true') + expect(button).toHaveAttribute('aria-roledescription', 'toggle button') + expect(button).toHaveAttribute('aria-keyshortcuts', 'Alt+Shift+E') + }) + + it('does not render undefined ARIA attributes on the button', () => { + const { getByRole } = renderWithTheme() + const button = getByRole('button') + + expect(button).not.toHaveAttribute('aria-describedby') + expect(button).not.toHaveAttribute('aria-pressed') + expect(button).not.toHaveAttribute('aria-roledescription') + expect(button).not.toHaveAttribute('aria-keyshortcuts') + }) }) diff --git a/packages/ui/src/components/Button/index.tsx b/packages/ui/src/components/Button/index.tsx index 2192d4a1bb..7942a0deac 100644 --- a/packages/ui/src/components/Button/index.tsx +++ b/packages/ui/src/components/Button/index.tsx @@ -201,40 +201,43 @@ export const Button = forwardRef( ) } + const commonProps = { + 'aria-controls': ariaControls, + 'aria-current': ariaCurrent, + 'aria-disabled': ariaDisabled, + 'aria-describedby': ariaDescribedby, + 'aria-expanded': ariaExpanded, + 'aria-haspopup': ariaHaspopup, + 'aria-keyshortcuts': ariaKeyshortcuts, + 'aria-pressed': ariaPressed, + 'aria-roledescription': ariaRoledescription, + autoFocus, + className: computedClassName, + 'data-testid': dataTestId, + 'data-flip-id': dataFlipId, + onBlur, + onClick, + onMouseDown, + onMouseEnter, + onMouseLeave, + onMouseOut, + onMouseUp, + role, + style, + tabIndex, + } + // @note: an anchor can't be disabled if (href && !computeIsDisabled) { return ( } - role={role} - style={style} - tabIndex={tabIndex} target={target} - type={type} > {content} @@ -245,31 +248,14 @@ export const Button = forwardRef( return (