From 19126ea48ed82326a14552c562345e034ccb4e28 Mon Sep 17 00:00:00 2001 From: Dmitriy Abragamov Date: Wed, 26 Aug 2026 13:18:36 -0400 Subject: [PATCH 1/2] fix(SDK-1173): give contractor row action-menu buttons a row-specific accessible name The onboarding-completion and management contractor lists' per-row hamburger menu trigger used a generic accessible name ("Open menu" / "Contractor actions menu") that didn't identify which row it acted on, failing WCAG 4.1.2 per an external accessibility audit. It now reads "Actions for ", matching the pattern already used by the state taxes list. --- .../management/ManagementContractorList.test.tsx | 14 +++++++------- .../management/ManagementContractorListView.tsx | 12 ++++++++++-- .../onboarding/ContractorList.test.tsx | 16 ++++++++-------- .../onboarding/ContractorListView.tsx | 7 ++++++- src/i18n/en/Contractor.ContractorList.json | 1 + .../en/Contractor.ManagementContractorList.json | 2 +- src/i18n/types.d.ts | 4 +++- 7 files changed, 36 insertions(+), 20 deletions(-) diff --git a/src/components/Contractor/ContractorList/management/ManagementContractorList.test.tsx b/src/components/Contractor/ContractorList/management/ManagementContractorList.test.tsx index 6dae4fd1f2..4a9d00ee5c 100644 --- a/src/components/Contractor/ContractorList/management/ManagementContractorList.test.tsx +++ b/src/components/Contractor/ContractorList/management/ManagementContractorList.test.tsx @@ -92,7 +92,7 @@ describe('ManagementContractorList — Active tab', () => { renderWithProviders() await screen.findByText('Ada Lovelace') - await user.click(screen.getByRole('button', { name: 'Contractor actions menu' })) + await user.click(screen.getByRole('button', { name: 'Actions for Ada Lovelace' })) await user.click(await screen.findByRole('menuitem', { name: 'Dismiss contractor' })) expect(onEvent).toHaveBeenCalledWith(contractorEvents.CONTRACTOR_DISMISS, { @@ -120,7 +120,7 @@ describe('ManagementContractorList — Active tab', () => { renderWithProviders() await screen.findByText('Ada Lovelace') - await user.click(screen.getByRole('button', { name: 'Contractor actions menu' })) + await user.click(screen.getByRole('button', { name: 'Actions for Ada Lovelace' })) await user.click(await screen.findByRole('menuitem', { name: 'Cancel dismissal' })) expect(await screen.findByRole('dialog')).toBeTruthy() @@ -142,7 +142,7 @@ describe('ManagementContractorList — Active tab', () => { renderWithProviders() await screen.findByText('Ada Lovelace') - await user.click(screen.getByRole('button', { name: 'Contractor actions menu' })) + await user.click(screen.getByRole('button', { name: 'Actions for Ada Lovelace' })) await user.click(await screen.findByRole('menuitem', { name: 'View details' })) expect(onEvent).toHaveBeenCalledWith(contractorEvents.CONTRACTOR_VIEW, { @@ -170,7 +170,7 @@ describe('ManagementContractorList — Onboarding tab', () => { await screen.findByText('Ada Lovelace') expect(screen.getByRole('button', { name: 'Continue' })).toBeInTheDocument() - await user.click(screen.getByRole('button', { name: 'Contractor actions menu' })) + await user.click(screen.getByRole('button', { name: 'Actions for Ada Lovelace' })) expect(await screen.findByRole('menuitem', { name: 'Remove' })).toBeInTheDocument() }) @@ -204,7 +204,7 @@ describe('ManagementContractorList — Onboarding tab', () => { await user.click(await screen.findByRole('tab', { name: 'Onboarding' })) await screen.findByText('Ada Lovelace') - await user.click(screen.getByRole('button', { name: 'Contractor actions menu' })) + await user.click(screen.getByRole('button', { name: 'Actions for Ada Lovelace' })) await user.click(await screen.findByRole('menuitem', { name: 'Remove' })) expect(deleteResolver).not.toHaveBeenCalled() @@ -257,7 +257,7 @@ describe('ManagementContractorList — Dismissed tab', () => { await user.click(await screen.findByRole('tab', { name: 'Dismissed' })) await screen.findByText('Ada Lovelace') - await user.click(screen.getByRole('button', { name: 'Contractor actions menu' })) + await user.click(screen.getByRole('button', { name: 'Actions for Ada Lovelace' })) await user.click(await screen.findByRole('menuitem', { name: 'Rehire contractor' })) expect(onEvent).toHaveBeenCalledWith(contractorEvents.CONTRACTOR_REHIRE, { @@ -287,7 +287,7 @@ describe('ManagementContractorList — Dismissed tab', () => { await user.click(await screen.findByRole('tab', { name: 'Dismissed' })) await screen.findByText('Ada Lovelace') - await user.click(screen.getByRole('button', { name: 'Contractor actions menu' })) + await user.click(screen.getByRole('button', { name: 'Actions for Ada Lovelace' })) await user.click(await screen.findByRole('menuitem', { name: 'Cancel rehire' })) expect(await screen.findByRole('dialog')).toBeTruthy() diff --git a/src/components/Contractor/ContractorList/management/ManagementContractorListView.tsx b/src/components/Contractor/ContractorList/management/ManagementContractorListView.tsx index d72205e725..8018619932 100644 --- a/src/components/Contractor/ContractorList/management/ManagementContractorListView.tsx +++ b/src/components/Contractor/ContractorList/management/ManagementContractorListView.tsx @@ -223,7 +223,10 @@ export function ManagementContractorListView({ : t('continueCta')} )} - + ) } @@ -280,7 +283,12 @@ export function ManagementContractorListView({ }) } - return + return ( + + ) }, isFetching, pagination, diff --git a/src/components/Contractor/ContractorList/onboarding/ContractorList.test.tsx b/src/components/Contractor/ContractorList/onboarding/ContractorList.test.tsx index f7d7ef0e4f..4113f8d6aa 100644 --- a/src/components/Contractor/ContractorList/onboarding/ContractorList.test.tsx +++ b/src/components/Contractor/ContractorList/onboarding/ContractorList.test.tsx @@ -46,7 +46,7 @@ describe('ContractorList hamburger menu edit/review CTA', () => { renderWithProviders( {}} />) await screen.findByText('Ada Lovelace') - await user.click(screen.getByRole('button', { name: 'Open menu' })) + await user.click(screen.getByRole('button', { name: 'Actions for Ada Lovelace' })) expect(await screen.findByRole('menuitem', { name: 'Edit' })).toBeTruthy() }) @@ -58,7 +58,7 @@ describe('ContractorList hamburger menu edit/review CTA', () => { renderWithProviders( {}} />) await screen.findByText('Ada Lovelace') - await user.click(screen.getByRole('button', { name: 'Open menu' })) + await user.click(screen.getByRole('button', { name: 'Actions for Ada Lovelace' })) expect(await screen.findByRole('menuitem', { name: 'Review' })).toBeTruthy() }) @@ -70,7 +70,7 @@ describe('ContractorList hamburger menu edit/review CTA', () => { renderWithProviders( {}} />) await screen.findByText('Ada Lovelace') - await user.click(screen.getByRole('button', { name: 'Open menu' })) + await user.click(screen.getByRole('button', { name: 'Actions for Ada Lovelace' })) expect(await screen.findByRole('menuitem', { name: 'Edit' })).toBeTruthy() }) @@ -82,7 +82,7 @@ describe('ContractorList hamburger menu edit/review CTA', () => { renderWithProviders( {}} />) await screen.findByText('Ada Lovelace') - await user.click(screen.getByRole('button', { name: 'Open menu' })) + await user.click(screen.getByRole('button', { name: 'Actions for Ada Lovelace' })) expect(await screen.findByRole('menuitem', { name: 'Edit' })).toBeTruthy() }) @@ -104,7 +104,7 @@ describe('ContractorList delete action', () => { renderWithProviders() await screen.findByText('Ada Lovelace') - await user.click(screen.getByRole('button', { name: 'Open menu' })) + await user.click(screen.getByRole('button', { name: 'Actions for Ada Lovelace' })) await user.click(await screen.findByRole('menuitem', { name: 'Delete' })) expect(deleteResolver).not.toHaveBeenCalled() @@ -131,7 +131,7 @@ describe('ContractorList cancel self-onboarding action', () => { renderWithProviders( {}} />) await screen.findByText('Ada Lovelace') - await user.click(screen.getByRole('button', { name: 'Open menu' })) + await user.click(screen.getByRole('button', { name: 'Actions for Ada Lovelace' })) expect(await screen.findByRole('menuitem', { name: 'Cancel self-onboarding' })).toBeTruthy() // Editing is blocked mid self-onboarding — the admin must cancel it first. @@ -149,7 +149,7 @@ describe('ContractorList cancel self-onboarding action', () => { renderWithProviders( {}} />) await screen.findByText('Ada Lovelace') - await user.click(screen.getByRole('button', { name: 'Open menu' })) + await user.click(screen.getByRole('button', { name: 'Actions for Ada Lovelace' })) await screen.findByRole('menuitem', { name: /Edit|Review/ }) expect( @@ -177,7 +177,7 @@ describe('ContractorList cancel self-onboarding action', () => { renderWithProviders() await screen.findByText('Ada Lovelace') - await user.click(screen.getByRole('button', { name: 'Open menu' })) + await user.click(screen.getByRole('button', { name: 'Actions for Ada Lovelace' })) await user.click(await screen.findByRole('menuitem', { name: 'Cancel self-onboarding' })) await waitFor(() => { diff --git a/src/components/Contractor/ContractorList/onboarding/ContractorListView.tsx b/src/components/Contractor/ContractorList/onboarding/ContractorListView.tsx index 7a7aff1117..27614c4ebd 100644 --- a/src/components/Contractor/ContractorList/onboarding/ContractorListView.tsx +++ b/src/components/Contractor/ContractorList/onboarding/ContractorListView.tsx @@ -101,7 +101,12 @@ export function ContractorListView({ }) } - return + return ( + + ) }, isFetching, pagination, diff --git a/src/i18n/en/Contractor.ContractorList.json b/src/i18n/en/Contractor.ContractorList.json index e532f3a03d..df25d858af 100644 --- a/src/i18n/en/Contractor.ContractorList.json +++ b/src/i18n/en/Contractor.ContractorList.json @@ -6,6 +6,7 @@ "reviewCta": "Review", "deleteCta": "Delete", "cancelSelfOnboardingCta": "Cancel self-onboarding", + "hamburgerTitle": "Actions for {{name}}", "emptyTableDescription": "Add contractors to get them setup for payroll.", "emptyTableTitle": "You haven't added any contractors yet", "listHeaders": { diff --git a/src/i18n/en/Contractor.ManagementContractorList.json b/src/i18n/en/Contractor.ManagementContractorList.json index 6311442f51..ebef8d287b 100644 --- a/src/i18n/en/Contractor.ManagementContractorList.json +++ b/src/i18n/en/Contractor.ManagementContractorList.json @@ -25,7 +25,7 @@ "cancelSelfOnboardingCta": "Cancel self-onboarding", "cancelDismissalCta": "Cancel dismissal", "cancelRehireCta": "Cancel rehire", - "hamburgerTitle": "Contractor actions menu", + "hamburgerTitle": "Actions for {{name}}", "contractorListLabel": "List of contractors", "emptyState": { "active": { diff --git a/src/i18n/types.d.ts b/src/i18n/types.d.ts index 86da0385c7..45ab879ec3 100644 --- a/src/i18n/types.d.ts +++ b/src/i18n/types.d.ts @@ -2147,6 +2147,8 @@ export namespace Translations { deleteCta: string /** @defaultValue `"Cancel self-onboarding"` */ cancelSelfOnboardingCta: string + /** @defaultValue `"Actions for {{name}}"` */ + hamburgerTitle: string /** @defaultValue `"Add contractors to get them setup for payroll."` */ emptyTableDescription: string /** @defaultValue `"You haven't added any contractors yet"` */ @@ -2576,7 +2578,7 @@ export namespace Translations { cancelDismissalCta: string /** @defaultValue `"Cancel rehire"` */ cancelRehireCta: string - /** @defaultValue `"Contractor actions menu"` */ + /** @defaultValue `"Actions for {{name}}"` */ hamburgerTitle: string /** @defaultValue `"List of contractors"` */ contractorListLabel: string From 6945df28acd17293e30ed29d6d76abef94fff65b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 26 Aug 2026 17:28:25 +0000 Subject: [PATCH 2/2] chore: update derived files --- docs/reference/Translations/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/reference/Translations/index.md b/docs/reference/Translations/index.md index 143da02f06..8b4ad53302 100644 --- a/docs/reference/Translations/index.md +++ b/docs/reference/Translations/index.md @@ -1477,6 +1477,7 @@ Translation keys for the `Contractor.ContractorList` i18n namespace. | `editCta` | `"Edit"` | | `emptyTableDescription` | `"Add contractors to get them setup for payroll."` | | `emptyTableTitle` | `"You haven't added any contractors yet"` | +| `hamburgerTitle` | `"Actions for {{name}}"` | | `listHeaders` | | | `listHeaders.name` | `"Name"` | | `listHeaders.status` | `"Status"` | @@ -1671,7 +1672,7 @@ Translation keys for the `Contractor.ManagementContractorList` i18n namespace. | `emptyState.onboarding` | | | `emptyState.onboarding.description` | `"Contractors currently being onboarded will appear here"` | | `emptyState.onboarding.title` | `"There are no contractors onboarding"` | -| `hamburgerTitle` | `"Contractor actions menu"` | +| `hamburgerTitle` | `"Actions for {{name}}"` | | `lastDayBadge` | `"Last day {{date}}"` | | `nameLabel` | `"Contractor name"` | | `onboardingStatusLabel` | `"Onboarding status"` |