Skip to content

Commit 076e76f

Browse files
committed
✨(frontend) make delete buttons nvda-accessible
add aria-labels and include close button in title prop so NVDA announces actions Signed-off-by: Cyril <[email protected]>
1 parent 0cf8b9d commit 076e76f

File tree

9 files changed

+123
-94
lines changed

9 files changed

+123
-94
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ and this project adheres to
1717
- #1262
1818
- #1244
1919
- #1270
20+
- #1281
2021

2122
### Fixed
2223

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

Lines changed: 20 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,7 @@ test.describe('Doc Export', () => {
2929
})
3030
.click();
3131

32-
await expect(
33-
page
34-
.locator('div')
35-
.filter({ hasText: /^Download$/ })
36-
.first(),
37-
).toBeVisible();
32+
await expect(page.getByTestId('modal-export-title')).toBeVisible();
3833
await expect(
3934
page.getByText('Download your document in a .docx or .pdf format.'),
4035
).toBeVisible();
@@ -45,7 +40,7 @@ test.describe('Doc Export', () => {
4540
await expect(
4641
page.getByRole('button', { name: 'Close the modal' }),
4742
).toBeVisible();
48-
await expect(page.getByRole('button', { name: 'Download' })).toBeVisible();
43+
await expect(page.getByTestId('modal-download-button')).toBeVisible();
4944
});
5045

5146
test('it exports the doc with pdf line break', async ({
@@ -122,7 +117,9 @@ test.describe('Doc Export', () => {
122117
const fileChooser = await fileChooserPromise;
123118
await fileChooser.setFiles(path.join(__dirname, 'assets/test.svg'));
124119

125-
const image = page.getByRole('img', { name: 'test.svg' });
120+
const image = page
121+
.locator('.--docs--editor-container img.bn-visual-media')
122+
.first();
126123

127124
await expect(image).toBeVisible();
128125

@@ -136,23 +133,13 @@ test.describe('Doc Export', () => {
136133
await page.getByRole('combobox', { name: 'Format' }).click();
137134
await page.getByRole('option', { name: 'Docx' }).click();
138135

139-
await expect(
140-
page.getByRole('button', {
141-
name: 'Download',
142-
exact: true,
143-
}),
144-
).toBeVisible();
136+
await expect(page.getByTestId('modal-download-button')).toBeVisible();
145137

146138
const downloadPromise = page.waitForEvent('download', (download) => {
147139
return download.suggestedFilename().includes(`${randomDoc}.docx`);
148140
});
149141

150-
void page
151-
.getByRole('button', {
152-
name: 'Download',
153-
exact: true,
154-
})
155-
.click();
142+
void page.getByTestId('modal-download-button').click();
156143

157144
const download = await downloadPromise;
158145
expect(download.suggestedFilename()).toBe(`${randomDoc}.docx`);
@@ -182,7 +169,9 @@ test.describe('Doc Export', () => {
182169
const fileChooser = await fileChooserPromise;
183170
await fileChooser.setFiles(path.join(__dirname, 'assets/test.svg'));
184171

185-
const image = page.getByRole('img', { name: 'test.svg' });
172+
const image = page
173+
.locator('.--docs--editor-container img.bn-visual-media')
174+
.first();
186175

187176
await expect(image).toBeVisible();
188177

@@ -216,11 +205,7 @@ test.describe('Doc Export', () => {
216205

217206
await new Promise((resolve) => setTimeout(resolve, 1000));
218207

219-
await expect(
220-
page.getByRole('button', {
221-
name: 'Download',
222-
}),
223-
).toBeVisible();
208+
await expect(page.getByTestId('modal-download-button')).toBeVisible();
224209

225210
const responseCorsPromise = page.waitForResponse(
226211
(response) =>
@@ -231,11 +216,7 @@ test.describe('Doc Export', () => {
231216
return download.suggestedFilename().includes(`${randomDoc}.pdf`);
232217
});
233218

234-
void page
235-
.getByRole('button', {
236-
name: 'Download',
237-
})
238-
.click();
219+
void page.getByTestId('modal-download-button').click();
239220

240221
const responseCors = await responseCorsPromise;
241222
expect(responseCors.ok()).toBe(true);
@@ -277,21 +258,13 @@ test.describe('Doc Export', () => {
277258
})
278259
.click();
279260

280-
await expect(
281-
page.getByRole('button', {
282-
name: 'Download',
283-
}),
284-
).toBeVisible();
261+
await expect(page.getByTestId('modal-download-button')).toBeVisible();
285262

286263
const downloadPromise = page.waitForEvent('download', (download) => {
287264
return download.suggestedFilename().includes(`${randomDoc}.pdf`);
288265
});
289266

290-
void page
291-
.getByRole('button', {
292-
name: 'Download',
293-
})
294-
.click();
267+
void page.getByTestId('modal-download-button').click();
295268

296269
const download = await downloadPromise;
297270
expect(download.suggestedFilename()).toBe(`${randomDoc}.pdf`);
@@ -328,23 +301,13 @@ test.describe('Doc Export', () => {
328301
})
329302
.click();
330303

331-
await expect(
332-
page.getByRole('button', {
333-
name: 'Download',
334-
exact: true,
335-
}),
336-
).toBeVisible();
304+
await expect(page.getByTestId('modal-download-button')).toBeVisible();
337305

338306
const downloadPromise = page.waitForEvent('download', (download) => {
339307
return download.suggestedFilename().includes(`${randomDoc}.pdf`);
340308
});
341309

342-
void page
343-
.getByRole('button', {
344-
name: 'Download',
345-
exact: true,
346-
})
347-
.click();
310+
void page.getByTestId('modal-download-button').click();
348311

349312
const download = await downloadPromise;
350313
expect(download.suggestedFilename()).toBe(`${randomDoc}.pdf`);
@@ -391,23 +354,13 @@ test.describe('Doc Export', () => {
391354
})
392355
.click();
393356

394-
await expect(
395-
page.getByRole('button', {
396-
name: 'Download',
397-
exact: true,
398-
}),
399-
).toBeVisible();
357+
await expect(page.getByTestId('modal-download-button')).toBeVisible();
400358

401359
const downloadPromise = page.waitForEvent('download', (download) => {
402360
return download.suggestedFilename().includes(`${randomDoc}.pdf`);
403361
});
404362

405-
void page
406-
.getByRole('button', {
407-
name: 'Download',
408-
exact: true,
409-
})
410-
.click();
363+
void page.getByTestId('modal-download-button').click();
411364

412365
const download = await downloadPromise;
413366
expect(download.suggestedFilename()).toBe(`${randomDoc}.pdf`);
@@ -470,12 +423,7 @@ test.describe('Doc Export', () => {
470423
return download.suggestedFilename().includes(`${randomDocFrench}.pdf`);
471424
});
472425

473-
void page
474-
.getByRole('button', {
475-
name: 'Télécharger',
476-
exact: true,
477-
})
478-
.click();
426+
void page.getByTestId('modal-download-button').click();
479427

480428
const download = await downloadPromise;
481429
expect(download.suggestedFilename()).toBe(`${randomDocFrench}.pdf`);
@@ -538,12 +486,7 @@ test.describe('Doc Export', () => {
538486
})
539487
.click();
540488

541-
void page
542-
.getByRole('button', {
543-
name: 'Download',
544-
exact: true,
545-
})
546-
.click();
489+
void page.getByTestId('modal-download-button').click();
547490

548491
const download = await downloadPromise;
549492
expect(download.suggestedFilename()).toBe(`${docChild}.pdf`);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ test.describe('Home page', () => {
7272
await page.waitForLoadState('domcontentloaded');
7373

7474
// Wait a bit more for the responsive store to be initialized
75+
// eslint-disable-next-line playwright/no-wait-for-timeout
7576
await page.waitForTimeout(500);
7677

7778
// Check header content

src/frontend/apps/impress/src/features/docs/doc-export/components/ModalExport.tsx

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
131131
isOpen
132132
closeOnClickOutside
133133
onClose={() => onClose()}
134+
hideCloseButton
134135
rightActions={
135136
<>
136137
<Button
@@ -148,16 +149,45 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
148149
fullWidth
149150
onClick={() => void onSubmit()}
150151
disabled={isExporting}
152+
data-testid="modal-download-button"
151153
>
152154
{t('Download')}
153155
</Button>
154156
</>
155157
}
156158
size={ModalSize.MEDIUM}
157159
title={
158-
<Text $size="h6" $variation="1000" $align="flex-start">
159-
{t('Download')}
160-
</Text>
160+
<Box
161+
$direction="row"
162+
$justify="space-between"
163+
$align="center"
164+
$width="100%"
165+
>
166+
<Text
167+
$size="h6"
168+
$variation="1000"
169+
$align="flex-start"
170+
data-testid="modal-export-title"
171+
>
172+
{t('Download')}
173+
</Text>
174+
<Button
175+
aria-label={t('Close the download modal')}
176+
size="small"
177+
color="primary-text"
178+
onClick={() => onClose()}
179+
disabled={isExporting}
180+
icon={
181+
<Box
182+
as="span"
183+
aria-hidden="true"
184+
className="material-icons-filled"
185+
>
186+
close
187+
</Box>
188+
}
189+
/>
190+
</Box>
161191
}
162192
>
163193
<Box

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

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { usePathname } from 'next/navigation';
99
import { useRouter } from 'next/router';
1010
import { Trans, useTranslation } from 'react-i18next';
1111

12-
import { Box, Text, TextErrors } from '@/components';
12+
import { Box, Icon, Text, TextErrors } from '@/components';
1313

1414
import { useRemoveDoc } from '../api/useRemoveDoc';
1515
import { Doc } from '../types';
@@ -53,11 +53,12 @@ export const ModalRemoveDoc = ({
5353
<Modal
5454
isOpen
5555
closeOnClickOutside
56+
hideCloseButton
5657
onClose={() => onClose()}
5758
rightActions={
5859
<>
5960
<Button
60-
aria-label={t('Close the modal')}
61+
aria-label={t('Close the delete modal')}
6162
color="secondary"
6263
fullWidth
6364
onClick={() => onClose()}
@@ -80,15 +81,29 @@ export const ModalRemoveDoc = ({
8081
}
8182
size={ModalSize.MEDIUM}
8283
title={
83-
<Text
84-
$size="h6"
85-
as="h6"
86-
$margin={{ all: '0' }}
87-
$align="flex-start"
88-
$variation="1000"
84+
<Box
85+
$direction="row"
86+
$justify="space-between"
87+
$align="center"
88+
$width="100%"
8989
>
90-
{t('Delete a doc')}
91-
</Text>
90+
<Text
91+
$size="h6"
92+
as="h6"
93+
$margin={{ all: '0' }}
94+
$align="flex-start"
95+
$variation="1000"
96+
>
97+
{t('Delete a doc')}
98+
</Text>
99+
<Button
100+
aria-label={t('Close the delete modal')}
101+
size="small"
102+
color="primary-text"
103+
onClick={() => onClose()}
104+
icon={<Icon iconName="close" />}
105+
/>
106+
</Box>
92107
}
93108
>
94109
<Box

src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareAccessRequest.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ const DocShareAccessRequestItem = ({ doc, accessRequest }: Props) => {
100100
docId: doc.id,
101101
})
102102
}
103+
aria-label={t('Close the access request modal')}
103104
>
104105
<Icon iconName="close" $variation="600" $size="16px" />
105106
</BoxButton>

src/frontend/apps/impress/src/features/docs/doc-share/components/DocShareModal.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Modal, ModalSize } from '@openfun/cunningham-react';
1+
import { Button, Modal, ModalSize } from '@openfun/cunningham-react';
22
import { useMemo, useRef, useState } from 'react';
33
import { useTranslation } from 'react-i18next';
44
import { createGlobalStyle, css } from 'styled-components';
@@ -137,12 +137,29 @@ export const DocShareModal = ({ doc, onClose, isRootDoc = true }: Props) => {
137137
aria-label={t('Share modal')}
138138
size={isDesktop ? ModalSize.LARGE : ModalSize.FULL}
139139
onClose={onClose}
140-
title={<Box $align="flex-start">{t('Share the document')}</Box>}
140+
title={
141+
<Box $direction="row" $justify="space-between" $align="center">
142+
<Box $align="flex-start">{t('Share the document')}</Box>
143+
<Button
144+
aria-label={t('Close the modal')}
145+
size="small"
146+
color="primary-text"
147+
onClick={onClose}
148+
icon={
149+
<Box aria-hidden="true" className="material-icons-filled">
150+
close
151+
</Box>
152+
}
153+
/>
154+
</Box>
155+
}
156+
hideCloseButton
141157
>
142158
<ShareModalStyle />
143159
<Box
144160
aria-label={t('Share modal')}
145-
$height={canViewAccesses ? modalContentHeight : 'auto'}
161+
$height="auto"
162+
$maxHeight={canViewAccesses ? modalContentHeight : 'none'}
146163
$overflow="hidden"
147164
className="--docs--doc-share-modal noPadding "
148165
$justify="space-between"

src/frontend/apps/impress/src/features/docs/doc-versioning/components/ModalSelectVersion.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ export const ModalSelectVersion = ({
118118
onClick={onClose}
119119
size="nano"
120120
color="primary-text"
121+
aria-label={t('Close the version history modal')}
121122
icon={<Icon iconName="close" />}
122123
/>
123124
</Box>

0 commit comments

Comments
 (0)