️(frontend) announce search results through a live region

Announce result count updates while focus stays in input (#2043)
This commit is contained in:
Cyril
2026-03-17 16:27:32 +01:00
parent cef83067e6
commit b47c730e19
4 changed files with 19 additions and 7 deletions

View File

@@ -12,6 +12,7 @@ and this project adheres to
- ♿️(frontend) improve version history list accessibility #2033
- ♿️(frontend) fix more options menu feedback for screen readers #2071
- ♿(frontend) focus skip link on headings and skip grid dropzone #1983
- ♿️(frontend) fix search modal accessibility issues #2054
## [v4.8.2] - 2026-03-19

View File

@@ -29,7 +29,7 @@ test.describe('Document search', () => {
await page.getByTestId('search-docs-button').click();
await expect(
page.getByRole('img', { name: 'No active search' }),
page.getByLabel('Search modal').locator('img[alt=""]'),
).toBeVisible();
await expect(
@@ -107,7 +107,7 @@ test.describe('Document search', () => {
await searchButton.click();
await expect(
page.getByRole('combobox', { name: 'Quick search input' }),
page.getByRole('combobox', { name: 'Search documents' }),
).toBeVisible();
await expect(filters).toBeHidden();
@@ -120,7 +120,7 @@ test.describe('Document search', () => {
await searchButton.click();
await expect(
page.getByRole('combobox', { name: 'Quick search input' }),
page.getByRole('combobox', { name: 'Search documents' }),
).toBeVisible();
await expect(filters).toBeHidden();
@@ -164,9 +164,9 @@ test.describe('Document search', () => {
const searchButton = page.getByTestId('search-docs-button');
await searchButton.click();
await page.getByRole('combobox', { name: 'Quick search input' }).click();
await page.getByRole('combobox', { name: 'Search documents' }).click();
await page
.getByRole('combobox', { name: 'Quick search input' })
.getByRole('combobox', { name: 'Search documents' })
.fill('sub page search');
// Expect to find the first and second docs in the results list
@@ -188,7 +188,7 @@ test.describe('Document search', () => {
);
await searchButton.click();
await page
.getByRole('combobox', { name: 'Quick search input' })
.getByRole('combobox', { name: 'Search documents' })
.fill('second');
// Now there is a sub page - expect to have the focus on the current doc

View File

@@ -1,3 +1,4 @@
import { announce } from '@react-aria/live-announcer';
import { t } from 'i18next';
import { useEffect, useState } from 'react';
import { InView } from 'react-intersection-observer';
@@ -73,10 +74,12 @@ export const DocSearchContent = ({
docs = docs.filter(filterResults);
}
const elements = search || isSearchNotMandatory ? docs : [];
setDocsData({
groupName: docs.length > 0 ? groupName : '',
groupKey: 'docs',
elements: search || isSearchNotMandatory ? docs : [],
elements,
emptyString: t('No document found'),
endActions: hasNextPage
? [
@@ -90,6 +93,13 @@ export const DocSearchContent = ({
]
: [],
});
if (search) {
announce(
t('{{count}} result(s) available', { count: elements.length }),
'polite',
);
}
}, [
search,
data?.pages,

View File

@@ -64,6 +64,7 @@ const DocSearchModalGlobal = ({
aria-describedby="doc-search-modal-title"
>
<Box
aria-label={t('Search modal')}
$direction="column"
$justify="space-between"
className="--docs--doc-search-modal"