diff --git a/CHANGELOG.md b/CHANGELOG.md
index 886ff225..d5e11983 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,9 @@ and this project adheres to
- 🔒️(backend) configure throttle on every viewsets #1343
- ⬆️ Bump eslint to V9 #1071
+- ♿(frontend) improve accessibility:
+ - ♿(frontend) fix major accessibility issues reported by wave and axe #1344
+ - #1341
## [3.6.0] - 2025-09-04
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts
index ca4053c6..ec4cd2bc 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts
@@ -226,9 +226,13 @@ test.describe('Doc Editor', () => {
await editor.fill('Hello World Doc persisted 2');
await expect(editor.getByText('Hello World Doc persisted 2')).toBeVisible();
+ await page.waitForTimeout(1000);
+
const urlDoc = page.url();
await page.goto(urlDoc);
+ // Wait for editor to load
+ await expect(editor).toBeVisible();
await expect(editor.getByText('Hello World Doc persisted 2')).toBeVisible();
});
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-grid.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-grid.spec.ts
index 59a1b998..88429f00 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/doc-grid.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-grid.spec.ts
@@ -80,9 +80,7 @@ test.describe('Documents Grid mobile', () => {
hasText: 'My mocked document',
});
- await expect(
- row.locator('[aria-describedby="doc-title"]').nth(0),
- ).toHaveText('My mocked document');
+ await expect(row.getByTestId('doc-title')).toHaveText('My mocked document');
});
});
@@ -295,7 +293,7 @@ test.describe('Documents Grid', () => {
docs = result.results as SmallDoc[];
await expect(page.getByTestId('grid-loader')).toBeHidden();
- await expect(page.locator('h4').getByText('All docs')).toBeVisible();
+ await expect(page.locator('h2').getByText('All docs')).toBeVisible();
const thead = page.getByTestId('docs-grid-header');
await expect(thead.getByText(/Name/i)).toBeVisible();
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-search.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-search.spec.ts
index 60cff9a3..97d36cc8 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/doc-search.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-search.spec.ts
@@ -173,12 +173,13 @@ test.describe('Document search', () => {
.getByRole('combobox', { name: 'Quick search input' })
.fill('sub page search');
- // Expect to find the first doc
+ // Expect to find the first and second docs in the results list
+ const resultsList = page.getByRole('listbox');
await expect(
- page.getByRole('presentation').getByLabel(firstDocTitle),
+ resultsList.getByRole('option', { name: firstDocTitle }),
).toBeVisible();
await expect(
- page.getByRole('presentation').getByLabel(secondDocTitle),
+ resultsList.getByRole('option', { name: secondDocTitle }),
).toBeVisible();
await page.getByRole('button', { name: 'close' }).click();
@@ -195,14 +196,15 @@ test.describe('Document search', () => {
.fill('second');
// Now there is a sub page - expect to have the focus on the current doc
+ const updatedResultsList = page.getByRole('listbox');
await expect(
- page.getByRole('presentation').getByLabel(secondDocTitle),
+ updatedResultsList.getByRole('option', { name: secondDocTitle }),
).toBeVisible();
await expect(
- page.getByRole('presentation').getByLabel(secondChildDocTitle),
+ updatedResultsList.getByRole('option', { name: secondChildDocTitle }),
).toBeVisible();
await expect(
- page.getByRole('presentation').getByLabel(firstDocTitle),
+ updatedResultsList.getByRole('option', { name: firstDocTitle }),
).toBeHidden();
});
});
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/utils-common.ts b/src/frontend/apps/e2e/__tests__/app-impress/utils-common.ts
index 504f0b4e..4d5a214b 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/utils-common.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/utils-common.ts
@@ -172,7 +172,7 @@ export const goToGridDoc = async (
await expect(row).toBeVisible();
- const docTitleContent = row.locator('[aria-describedby="doc-title"]').first();
+ const docTitleContent = row.getByTestId('doc-title').first();
const docTitle = await docTitleContent.textContent();
expect(docTitle).toBeDefined();
diff --git a/src/frontend/apps/impress/src/components/dropdown-menu/DropdownMenu.tsx b/src/frontend/apps/impress/src/components/dropdown-menu/DropdownMenu.tsx
index e3617288..df3049f7 100644
--- a/src/frontend/apps/impress/src/components/dropdown-menu/DropdownMenu.tsx
+++ b/src/frontend/apps/impress/src/components/dropdown-menu/DropdownMenu.tsx
@@ -110,7 +110,6 @@ export const DropdownMenu = ({
$direction="row"
$align="center"
$position="relative"
- aria-controls="menu"
>
{children}
) : (
-
- {children}
-
+ {children}
)
}
>
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/components/SimpleDocItem.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/components/SimpleDocItem.tsx
index ab11f0a2..cdccc047 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/components/SimpleDocItem.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/components/SimpleDocItem.tsx
@@ -82,12 +82,11 @@ export const SimpleDocItem = ({
{displayTitle}
diff --git a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx
index 7867e904..9447d0a1 100644
--- a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx
+++ b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx
@@ -70,7 +70,6 @@ export const DocsGrid = ({
>
-
-
-
- {t('Name')}
-
-
- {isDesktop && (
-
-
- {t('Updated at')}
-
+
+
+
+
+
+ {t('Name')}
+
+
+ {isDesktop && (
+
+
+ {t('Updated at')}
+
+
+ )}
+
+
+ {isDesktop ? (
+
+ ) : (
+
+ )}
+
+ {hasNextPage && !loading && (
+
+ {!isFetching && hasNextPage && (
+
+ )}
+
)}
-
- {isDesktop ? (
-
- ) : (
-
- )}
-
- {hasNextPage && !loading && (
-
- {!isFetching && hasNextPage && (
-
- )}
-
- )}
)}
diff --git a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridItem.tsx b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridItem.tsx
index e84c4a2b..06edf5ed 100644
--- a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridItem.tsx
+++ b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridItem.tsx
@@ -53,67 +53,76 @@ export const DocsGridItem = ({ doc, dragMode = false }: DocsGridItemProps) => {
`}
className="--docs--doc-grid-item"
>
-
-
-
- {isShared && (
-
- {dragMode && (
-
- )}
- {!dragMode && (
-
- {isPublic
- ? t('Accessible to anyone')
- : t('Accessible to authenticated users')}
-
- }
- placement="top"
- >
-
-
-
-
- )}
-
- )}
-
-
+
+
+ {isShared && (
+
+ {dragMode && (
+
+ )}
+ {!dragMode && (
+
+ {isPublic
+ ? t('Accessible to anyone')
+ : t('Accessible to authenticated users')}
+
+ }
+ placement="top"
+ >
+
+
+
+
+ )}
+
+ )}
+
+
+
{
$align="center"
$justify={isDesktop ? 'space-between' : 'flex-end'}
$gap="32px"
+ role="gridcell"
>
{isDesktop && (
diff --git a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridLoader.tsx b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridLoader.tsx
index 71fa1828..cd3358ab 100644
--- a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridLoader.tsx
+++ b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridLoader.tsx
@@ -1,5 +1,5 @@
import { Loader } from '@openfun/cunningham-react';
-import { createGlobalStyle } from 'styled-components';
+import { createGlobalStyle, css } from 'styled-components';
import { Box } from '@/components';
@@ -32,6 +32,9 @@ export const DocsGridLoader = ({ isLoading }: DocsGridLoaderProps) => {
$zIndex={998}
$position="absolute"
className="--docs--doc-grid-loader"
+ $css={css`
+ pointer-events: none;
+ `}
>
diff --git a/src/frontend/apps/impress/src/features/docs/docs-grid/components/Draggable.tsx b/src/frontend/apps/impress/src/features/docs/docs-grid/components/Draggable.tsx
index bafacf0b..cc554d73 100644
--- a/src/frontend/apps/impress/src/features/docs/docs-grid/components/Draggable.tsx
+++ b/src/frontend/apps/impress/src/features/docs/docs-grid/components/Draggable.tsx
@@ -19,6 +19,7 @@ export const Draggable = (props: DraggableProps) => {
{...attributes}
data-testid={`draggable-doc-${props.id}`}
className="--docs--grid-draggable"
+ role="presentation"
>
{props.children}
diff --git a/src/frontend/apps/impress/src/features/docs/docs-grid/components/Droppable.tsx b/src/frontend/apps/impress/src/features/docs/docs-grid/components/Droppable.tsx
index 851bf6f6..b5301393 100644
--- a/src/frontend/apps/impress/src/features/docs/docs-grid/components/Droppable.tsx
+++ b/src/frontend/apps/impress/src/features/docs/docs-grid/components/Droppable.tsx
@@ -35,6 +35,7 @@ export const Droppable = ({
{
>
{t('Pinned documents')}
- void docs.fetchNextPage()}
- $padding="none"
- >
- {favoriteDocs.map((doc) => (
-
- ))}
-
+
+
+ {favoriteDocs.map((doc) => (
+
+ ))}
+
+ {docs.hasNextPage && (
+ void docs.fetchNextPage()}
+ $padding="none"
+ />
+ )}
+
);