diff --git a/playwright/e2e/a11y-question-inputs.spec.ts b/playwright/e2e/a11y-question-inputs.spec.ts
index 66df3a141..55488836e 100644
--- a/playwright/e2e/a11y-question-inputs.spec.ts
+++ b/playwright/e2e/a11y-question-inputs.spec.ts
@@ -8,7 +8,7 @@ import { test as formTest } from '../support/fixtures/form.ts'
import { test as appNavigationTest } from '../support/fixtures/navigation.ts'
import { test as randomUserTest } from '../support/fixtures/random-user.ts'
import { test as topBarTest } from '../support/fixtures/topBar.ts'
-import { QuestionType } from '../support/sections/QuestionType.ts'
+import { GridSubtype, QuestionType } from '../support/sections/QuestionType.ts'
import { FormsView } from '../support/sections/TopBarSection.ts'
const test = mergeTests(randomUserTest, appNavigationTest, formTest, topBarTest)
@@ -32,6 +32,9 @@ test.describe('Accessibility: aria attributes on question inputs', () => {
const questions = await form.getQuestions()
await questions[0].fillTitle('My question')
await questions[0].fillDescription('Some context')
+ await expect(questions[0].descriptionInput).toHaveAccessibleName(
+ 'Description of question number 1',
+ )
await topBar.toggleView(FormsView.View)
@@ -148,6 +151,9 @@ test.describe('Accessibility: aria attributes on question inputs', () => {
page.getByRole('heading', { name: 'My dropdown question' }),
).toHaveId('q1_title')
await expect(page.locator('#q1_desc')).toContainText('Choose an option')
+ await expect(question.getByRole('combobox')).toHaveAccessibleName(
+ 'My dropdown question',
+ )
})
test('Date question with description has aria-labelledby and aria-describedby', async ({
@@ -176,6 +182,9 @@ test.describe('Accessibility: aria attributes on question inputs', () => {
page.getByRole('heading', { name: 'My date question' }),
).toHaveId('q1_title')
await expect(page.locator('#q1_desc')).toContainText('Pick a date')
+ await expect(question.getByRole('textbox')).toHaveAccessibleName(
+ 'My date question',
+ )
})
test('Linear scale question with description has aria-labelledby and aria-describedby', async ({
@@ -261,4 +270,27 @@ test.describe('Accessibility: aria attributes on question inputs', () => {
).toHaveId('q1_title')
await expect(page.locator('#q1_desc')).toContainText('Pick a color')
})
+
+ test('Grid cells are named by row and column', async ({
+ appNavigation,
+ form,
+ topBar,
+ page,
+ }) => {
+ await appNavigation.clickNewForm()
+ await form.fillTitle('Test form')
+
+ await form.addQuestion(QuestionType.Grid, GridSubtype.RadioButtons)
+ const questions = await form.getQuestions()
+ await questions[0].fillTitle('Satisfaction')
+ await questions[0].addColumn('Good')
+ await questions[0].addRow('Service')
+
+ await topBar.toggleView(FormsView.View)
+
+ const question = page.getByRole('listitem', { name: /Question number 1/ })
+ await expect(
+ question.getByRole('radio', { name: 'Service, Good' }),
+ ).toBeAttached()
+ })
})
diff --git a/playwright/support/sections/FormSection.ts b/playwright/support/sections/FormSection.ts
index f934ca7ed..ab7d3e8d0 100644
--- a/playwright/support/sections/FormSection.ts
+++ b/playwright/support/sections/FormSection.ts
@@ -25,6 +25,7 @@ export class FormSection {
})
this.descriptionField = this.mainContent.getByRole('textbox', {
name: 'Description',
+ exact: true,
})
}
diff --git a/src/FormsSettings.vue b/src/FormsSettings.vue
index d5b935242..fccb5007a 100644
--- a/src/FormsSettings.vue
+++ b/src/FormsSettings.vue
@@ -20,6 +20,7 @@
multiple
:options="availableGroups"
:placeholder="t('forms', 'Select groups')"
+ :aria-label-combobox="t('forms', 'Select groups')"
class="forms-settings__creation__multiselect"
label="displayName"
@update:modelValue="onCreationAllowedGroupsChange" />
diff --git a/src/components/OptionInputDialog.vue b/src/components/OptionInputDialog.vue
index 955827e7e..201c0110f 100644
--- a/src/components/OptionInputDialog.vue
+++ b/src/components/OptionInputDialog.vue
@@ -15,7 +15,6 @@
diff --git a/src/components/Questions/Question.vue b/src/components/Questions/Question.vue
index ebd9f8a7c..353fad731 100644
--- a/src/components/Questions/Question.vue
+++ b/src/components/Questions/Question.vue
@@ -130,6 +130,11 @@
ref="descriptionField"
dir="auto"
:value="description"
+ :aria-label="
+ t('forms', 'Description of question number {index}', {
+ index,
+ })
+ "
:placeholder="
t(
'forms',
diff --git a/src/components/Questions/QuestionDate.vue b/src/components/Questions/QuestionDate.vue
index 9636ffd64..0d8707011 100644
--- a/src/components/Questions/QuestionDate.vue
+++ b/src/components/Questions/QuestionDate.vue
@@ -92,6 +92,7 @@
:disabled="!readOnly"
:format="stringify"
:placeholder="datetimePickerPlaceholder"
+ :aria-label="text"
:showSecond="false"
:type="dateTimePickerType"
:disabledDate="disabledDates"
diff --git a/src/components/Questions/QuestionDropdown.vue b/src/components/Questions/QuestionDropdown.vue
index 77db95a1c..a22aea366 100644
--- a/src/components/Questions/QuestionDropdown.vue
+++ b/src/components/Questions/QuestionDropdown.vue
@@ -40,7 +40,7 @@
:options="choices"
:searchable="false"
label="text"
- :aria-label-combobox="selectOptionPlaceholder"
+ :aria-label-combobox="text"
:aria-errormessage="hasError ? errorId : undefined"
:aria-invalid="hasError ? 'true' : undefined"
@invalid.prevent="validate"
diff --git a/src/components/Questions/QuestionFile.vue b/src/components/Questions/QuestionFile.vue
index 5d7f9a006..8d2245da0 100644
--- a/src/components/Questions/QuestionFile.vue
+++ b/src/components/Questions/QuestionFile.vue
@@ -39,6 +39,7 @@
type="multiselect"
:modelValue="maxFileSizeUnit"
:options="availableUnits"
+ :aria-label-combobox="t('forms', 'File size unit')"
required
:clearable="false"
:searchable="false"
@@ -68,6 +69,7 @@
diff --git a/src/components/SidebarTabs/TransferOwnership.vue b/src/components/SidebarTabs/TransferOwnership.vue
index 47eb3c0a7..bb2d221b4 100644
--- a/src/components/SidebarTabs/TransferOwnership.vue
+++ b/src/components/SidebarTabs/TransferOwnership.vue
@@ -43,7 +43,7 @@
class="modal-content__select"
:loading="loading"
:options="options"
- :placeholder="t('forms', 'Search for a user')"
+ :inputLabel="t('forms', 'Search for a user')"
@search="
(query) => asyncSearch(query, [SHARE_TYPES.SHARE_TYPE_USER])
">