From 42f21c21a067de0d758afe32c7a40e78737ccbd2 Mon Sep 17 00:00:00 2001 From: yashgill-10 Date: Sun, 8 Nov 2026 09:33:29 +0530 Subject: [PATCH] fix(a11y): expose ranking option lists correctly to assistive tech The submit-mode ranking question wrapped two inner lists in a container with role="list" even though it is not a list itself. Change the outer container to role="group", keep the named inner lists, and add an explicit role="list" so WebKit exposes them as lists despite missing list markers. The ranked list only gets the role while populated to avoid an empty list role. Add a Playwright regression test asserting the group, the list names and the explicit roles. Signed-off-by: yashgill-10 --- playwright/e2e/a11y-question-inputs.spec.ts | 35 ++++++++++++++++++++ src/components/Questions/QuestionRanking.vue | 15 +++++---- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/playwright/e2e/a11y-question-inputs.spec.ts b/playwright/e2e/a11y-question-inputs.spec.ts index 5264d23b4..f95b25ee3 100644 --- a/playwright/e2e/a11y-question-inputs.spec.ts +++ b/playwright/e2e/a11y-question-inputs.spec.ts @@ -305,4 +305,39 @@ test.describe('Accessibility: aria attributes on question inputs', () => { question.getByRole('radio', { name: 'Service, Good' }), ).toBeAttached() }) + + test('Ranking question groups two named lists instead of a list of non-items', async ({ + appNavigation, + form, + topBar, + page, + }) => { + await appNavigation.clickNewForm() + await form.fillTitle('Test form') + + await form.addQuestion(QuestionType.Ranking) + const questions = await form.getQuestions() + await questions[0].fillTitle('Rank snacks') + await questions[0].addAnswer('Pretzels') + await questions[0].addAnswer('Popcorn') + + await topBar.toggleView(FormsView.View) + + const question = page.getByRole('listitem', { name: /Question number 1/ }) + const group = question.getByRole('group', { name: /Rank snacks/ }) + const unrankedList = group.getByRole('list', { + name: 'Unranked options', + }) + const rankedList = group.getByRole('list', { name: 'Your ranking' }) + + await expect(unrankedList).toBeVisible() + await expect(unrankedList).toHaveAttribute('role', 'list') + // The empty ranked list has no role to avoid an empty list role + await expect(rankedList).toBeAttached() + await expect(rankedList).not.toHaveAttribute('role', 'list') + + await group.getByRole('button', { name: 'Pretzels' }).click() + await expect(rankedList).toHaveAttribute('role', 'list') + await expect(rankedList).toBeVisible() + }) }) diff --git a/src/components/Questions/QuestionRanking.vue b/src/components/Questions/QuestionRanking.vue index d2bc758bf..60ab84f6d 100644 --- a/src/components/Questions/QuestionRanking.vue +++ b/src/components/Questions/QuestionRanking.vue @@ -30,7 +30,7 @@
+ class="sort-target" + role="list" + :aria-label="t('forms', 'Unranked options')">