Skip to content

Commit cd84751

Browse files
committed
✨(frontend) fix major accessibility issues found by wave and axe
improves a11y by fixing multiple critical validation errors Signed-off-by: Cyril <[email protected]>
1 parent 1d20a8b commit cd84751

File tree

13 files changed

+155
-125
lines changed

13 files changed

+155
-125
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ and this project adheres to
2020

2121
- 🔒️(backend) configure throttle on every viewsets #1343
2222
- ⬆️ Bump eslint to V9 #1071
23+
- ♿(frontend) improve accessibility:
24+
- ♿(frontend) fix major accessibility issues reported by wave and axe #1344
25+
- #1341
2326

2427
## [3.6.0] - 2025-09-04
2528

src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,9 +226,13 @@ test.describe('Doc Editor', () => {
226226
await editor.fill('Hello World Doc persisted 2');
227227
await expect(editor.getByText('Hello World Doc persisted 2')).toBeVisible();
228228

229+
await page.waitForTimeout(1000);
230+
229231
const urlDoc = page.url();
230232
await page.goto(urlDoc);
231233

234+
// Wait for editor to load
235+
await expect(editor).toBeVisible();
232236
await expect(editor.getByText('Hello World Doc persisted 2')).toBeVisible();
233237
});
234238

src/frontend/apps/e2e/__tests__/app-impress/doc-grid.spec.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,7 @@ test.describe('Documents Grid mobile', () => {
8080
hasText: 'My mocked document',
8181
});
8282

83-
await expect(
84-
row.locator('[aria-describedby="doc-title"]').nth(0),
85-
).toHaveText('My mocked document');
83+
await expect(row.getByTestId('doc-title')).toHaveText('My mocked document');
8684
});
8785
});
8886

@@ -295,7 +293,7 @@ test.describe('Documents Grid', () => {
295293
docs = result.results as SmallDoc[];
296294

297295
await expect(page.getByTestId('grid-loader')).toBeHidden();
298-
await expect(page.locator('h4').getByText('All docs')).toBeVisible();
296+
await expect(page.locator('h2').getByText('All docs')).toBeVisible();
299297

300298
const thead = page.getByTestId('docs-grid-header');
301299
await expect(thead.getByText(/Name/i)).toBeVisible();

src/frontend/apps/e2e/__tests__/app-impress/doc-search.spec.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -173,12 +173,13 @@ test.describe('Document search', () => {
173173
.getByRole('combobox', { name: 'Quick search input' })
174174
.fill('sub page search');
175175

176-
// Expect to find the first doc
176+
// Expect to find the first and second docs in the results list
177+
const resultsList = page.getByRole('listbox');
177178
await expect(
178-
page.getByRole('presentation').getByLabel(firstDocTitle),
179+
resultsList.getByRole('option', { name: firstDocTitle }),
179180
).toBeVisible();
180181
await expect(
181-
page.getByRole('presentation').getByLabel(secondDocTitle),
182+
resultsList.getByRole('option', { name: secondDocTitle }),
182183
).toBeVisible();
183184

184185
await page.getByRole('button', { name: 'close' }).click();
@@ -195,14 +196,15 @@ test.describe('Document search', () => {
195196
.fill('second');
196197

197198
// Now there is a sub page - expect to have the focus on the current doc
199+
const updatedResultsList = page.getByRole('listbox');
198200
await expect(
199-
page.getByRole('presentation').getByLabel(secondDocTitle),
201+
updatedResultsList.getByRole('option', { name: secondDocTitle }),
200202
).toBeVisible();
201203
await expect(
202-
page.getByRole('presentation').getByLabel(secondChildDocTitle),
204+
updatedResultsList.getByRole('option', { name: secondChildDocTitle }),
203205
).toBeVisible();
204206
await expect(
205-
page.getByRole('presentation').getByLabel(firstDocTitle),
207+
updatedResultsList.getByRole('option', { name: firstDocTitle }),
206208
).toBeHidden();
207209
});
208210
});

src/frontend/apps/e2e/__tests__/app-impress/utils-common.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ export const goToGridDoc = async (
172172

173173
await expect(row).toBeVisible();
174174

175-
const docTitleContent = row.locator('[aria-describedby="doc-title"]').first();
175+
const docTitleContent = row.getByTestId('doc-title').first();
176176
const docTitle = await docTitleContent.textContent();
177177
expect(docTitle).toBeDefined();
178178

src/frontend/apps/impress/src/components/dropdown-menu/DropdownMenu.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ export const DropdownMenu = ({
110110
$direction="row"
111111
$align="center"
112112
$position="relative"
113-
aria-controls="menu"
114113
>
115114
<Box>{children}</Box>
116115
<Icon
@@ -125,9 +124,7 @@ export const DropdownMenu = ({
125124
/>
126125
</Box>
127126
) : (
128-
<Box ref={blockButtonRef} aria-controls="menu">
129-
{children}
130-
</Box>
127+
<Box ref={blockButtonRef}>{children}</Box>
131128
)
132129
}
133130
>

src/frontend/apps/impress/src/features/docs/doc-management/components/SimpleDocItem.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,11 @@ export const SimpleDocItem = ({
8282
</Box>
8383
<Box $justify="center" $overflow="auto">
8484
<Text
85-
aria-describedby="doc-title"
86-
aria-label={doc.title || untitledDocument}
8785
$size="sm"
8886
$variation="1000"
8987
$weight="500"
9088
$css={ItemTextCss}
89+
data-testid="doc-title"
9190
>
9291
{displayTitle}
9392
</Text>

src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ export const DocsGrid = ({
7070
>
7171
<DocsGridLoader isLoading={isRefetching || loading} />
7272
<Card
73-
role="grid"
7473
data-testid="docs-grid"
7574
$height="100%"
7675
$width="100%"
@@ -84,7 +83,7 @@ export const DocsGrid = ({
8483
}}
8584
>
8685
<Text
87-
as="h4"
86+
as="h2"
8887
$size="h4"
8988
$variation="1000"
9089
$margin={{ top: '0px', bottom: '10px' }}
@@ -101,48 +100,57 @@ export const DocsGrid = ({
101100
)}
102101
{hasDocs && (
103102
<Box $gap="6px" $overflow="auto">
104-
<Box
105-
$direction="row"
106-
$padding={{ horizontal: 'xs' }}
107-
$gap="10px"
108-
data-testid="docs-grid-header"
109-
>
110-
<Box $flex={flexLeft} $padding="3xs">
111-
<Text $size="xs" $variation="600" $weight="500">
112-
{t('Name')}
113-
</Text>
114-
</Box>
115-
{isDesktop && (
116-
<Box $flex={flexRight} $padding={{ vertical: '3xs' }}>
117-
<Text $size="xs" $weight="500" $variation="600">
118-
{t('Updated at')}
119-
</Text>
103+
<Box role="grid">
104+
<Box role="rowgroup">
105+
<Box
106+
$direction="row"
107+
$padding={{ horizontal: 'xs' }}
108+
$gap="10px"
109+
data-testid="docs-grid-header"
110+
role="row"
111+
>
112+
<Box $flex={flexLeft} $padding="3xs" role="columnheader">
113+
<Text $size="xs" $variation="600" $weight="500">
114+
{t('Name')}
115+
</Text>
116+
</Box>
117+
{isDesktop && (
118+
<Box
119+
$flex={flexRight}
120+
$padding={{ vertical: '3xs' }}
121+
role="columnheader"
122+
>
123+
<Text $size="xs" $weight="500" $variation="600">
124+
{t('Updated at')}
125+
</Text>
126+
</Box>
127+
)}
120128
</Box>
129+
</Box>
130+
<Box role="rowgroup">
131+
{isDesktop ? (
132+
<DraggableDocGridContentList docs={docs} />
133+
) : (
134+
<DocGridContentList docs={docs} />
135+
)}
136+
</Box>
137+
{hasNextPage && !loading && (
138+
<InView
139+
data-testid="infinite-scroll-trigger"
140+
as="div"
141+
onChange={loadMore}
142+
>
143+
{!isFetching && hasNextPage && (
144+
<Button
145+
onClick={() => void fetchNextPage()}
146+
color="primary-text"
147+
>
148+
{t('More docs')}
149+
</Button>
150+
)}
151+
</InView>
121152
)}
122153
</Box>
123-
124-
{isDesktop ? (
125-
<DraggableDocGridContentList docs={docs} />
126-
) : (
127-
<DocGridContentList docs={docs} />
128-
)}
129-
130-
{hasNextPage && !loading && (
131-
<InView
132-
data-testid="infinite-scroll-trigger"
133-
as="div"
134-
onChange={loadMore}
135-
>
136-
{!isFetching && hasNextPage && (
137-
<Button
138-
onClick={() => void fetchNextPage()}
139-
color="primary-text"
140-
>
141-
{t('More docs')}
142-
</Button>
143-
)}
144-
</InView>
145-
)}
146154
</Box>
147155
)}
148156
</Card>

src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridItem.tsx

Lines changed: 65 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -53,74 +53,84 @@ export const DocsGridItem = ({ doc, dragMode = false }: DocsGridItemProps) => {
5353
`}
5454
className="--docs--doc-grid-item"
5555
>
56-
<StyledLink
56+
<Box
57+
$flex={flexLeft}
58+
role="gridcell"
5759
$css={css`
58-
flex: ${flexLeft};
5960
align-items: center;
6061
min-width: 0;
6162
`}
62-
href={`/docs/${doc.id}`}
6363
>
64-
<Box
65-
data-testid={`docs-grid-name-${doc.id}`}
66-
$direction="row"
67-
$align="center"
68-
$gap={spacingsTokens.xs}
69-
$padding={{ right: isDesktop ? 'md' : '3xs' }}
70-
$maxWidth="100%"
64+
<StyledLink
65+
$css={css`
66+
width: 100%;
67+
align-items: center;
68+
min-width: 0;
69+
`}
70+
href={`/docs/${doc.id}`}
7171
>
72-
<SimpleDocItem isPinned={doc.is_favorite} doc={doc} />
73-
{isShared && (
74-
<Box
75-
$padding={{ top: !isDesktop ? '4xs' : undefined }}
76-
$css={
77-
!isDesktop
78-
? css`
79-
align-self: flex-start;
80-
`
81-
: undefined
82-
}
83-
>
84-
{dragMode && (
85-
<Icon
86-
$theme="greyscale"
87-
$variation="600"
88-
$size="14px"
89-
iconName={isPublic ? 'public' : 'vpn_lock'}
90-
/>
91-
)}
92-
{!dragMode && (
93-
<Tooltip
94-
content={
95-
<Text $textAlign="center" $variation="000">
96-
{isPublic
97-
? t('Accessible to anyone')
98-
: t('Accessible to authenticated users')}
99-
</Text>
100-
}
101-
placement="top"
102-
>
103-
<div>
104-
<Icon
105-
$theme="greyscale"
106-
$variation="600"
107-
$size="14px"
108-
iconName={isPublic ? 'public' : 'vpn_lock'}
109-
/>
110-
</div>
111-
</Tooltip>
112-
)}
113-
</Box>
114-
)}
115-
</Box>
116-
</StyledLink>
72+
<Box
73+
data-testid={`docs-grid-name-${doc.id}`}
74+
$direction="row"
75+
$align="center"
76+
$gap={spacingsTokens.xs}
77+
$padding={{ right: isDesktop ? 'md' : '3xs' }}
78+
$maxWidth="100%"
79+
>
80+
<SimpleDocItem isPinned={doc.is_favorite} doc={doc} />
81+
{isShared && (
82+
<Box
83+
$padding={{ top: !isDesktop ? '4xs' : undefined }}
84+
$css={
85+
!isDesktop
86+
? css`
87+
align-self: flex-start;
88+
`
89+
: undefined
90+
}
91+
>
92+
{dragMode && (
93+
<Icon
94+
$theme="greyscale"
95+
$variation="600"
96+
$size="14px"
97+
iconName={isPublic ? 'public' : 'vpn_lock'}
98+
/>
99+
)}
100+
{!dragMode && (
101+
<Tooltip
102+
content={
103+
<Text $textAlign="center" $variation="000">
104+
{isPublic
105+
? t('Accessible to anyone')
106+
: t('Accessible to authenticated users')}
107+
</Text>
108+
}
109+
placement="top"
110+
>
111+
<div>
112+
<Icon
113+
$theme="greyscale"
114+
$variation="600"
115+
$size="14px"
116+
iconName={isPublic ? 'public' : 'vpn_lock'}
117+
/>
118+
</div>
119+
</Tooltip>
120+
)}
121+
</Box>
122+
)}
123+
</Box>
124+
</StyledLink>
125+
</Box>
117126

118127
<Box
119128
$flex={flexRight}
120129
$direction="row"
121130
$align="center"
122131
$justify={isDesktop ? 'space-between' : 'flex-end'}
123132
$gap="32px"
133+
role="gridcell"
124134
>
125135
{isDesktop && (
126136
<StyledLink href={`/docs/${doc.id}`}>

src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridLoader.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Loader } from '@openfun/cunningham-react';
2-
import { createGlobalStyle } from 'styled-components';
2+
import { createGlobalStyle, css } from 'styled-components';
33

44
import { Box } from '@/components';
55

@@ -32,6 +32,9 @@ export const DocsGridLoader = ({ isLoading }: DocsGridLoaderProps) => {
3232
$zIndex={998}
3333
$position="absolute"
3434
className="--docs--doc-grid-loader"
35+
$css={css`
36+
pointer-events: none;
37+
`}
3538
>
3639
<Loader />
3740
</Box>

0 commit comments

Comments
 (0)