Skip to content

Commit 94ce8e0

Browse files
committed
♿️(frontend) improve keyboard tab order in document list
Announce title, update date, and participants on document link focus.
1 parent 3264e29 commit 94ce8e0

6 files changed

Lines changed: 52 additions & 40 deletions

File tree

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ test.describe('Doc grid move', () => {
240240

241241
await expect(docsGrid.getByText(titleDoc1)).toBeHidden();
242242
await docsGrid
243-
.getByRole('link', { name: `Open document ${titleDoc2}` })
243+
.getByRole('link', { name: new RegExp(titleDoc2) })
244244
.click();
245245

246246
await verifyDocName(page, titleDoc2);
@@ -384,7 +384,7 @@ test.describe('Doc grid move', () => {
384384

385385
await expect(docsGrid.getByText(titleDoc1)).toBeHidden();
386386
await docsGrid
387-
.getByRole('link', { name: `Open document ${titleDoc2}` })
387+
.getByRole('link', { name: new RegExp(titleDoc2) })
388388
.click();
389389

390390
await verifyDocName(page, titleDoc2);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ test.describe('Doc Trashbin', () => {
8484
await page.getByRole('link', { name: 'All docs' }).click();
8585
const row2Restored = await getGridRow(page, title2);
8686
await expect(row2Restored.getByText(title2)).toBeVisible();
87-
await row2Restored.getByRole('link', { name: /Open document/ }).click();
87+
await row2Restored.getByRole('link', { name: new RegExp(title2) }).click();
8888

8989
await verifyDocName(page, title2);
9090
await page.getByRole('button', { name: 'Back to homepage' }).click();

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

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,42 @@ import { DocsGridActions } from './DocsGridActions';
1717
import { DocsGridItemSharedButton } from './DocsGridItemSharedButton';
1818
import { DocsGridTrashbinActions } from './DocsGridTrashbinActions';
1919

20+
const useDateToDisplay = (doc: Doc, isInTrashbin: boolean) => {
21+
const { data: config } = useConfig();
22+
const { t } = useTranslation();
23+
const { relativeDate, calculateDaysLeft } = useDate();
24+
25+
let dateToDisplay = relativeDate(doc.updated_at);
26+
27+
if (isInTrashbin && config?.TRASHBIN_CUTOFF_DAYS && doc.deleted_at) {
28+
const daysLeft = calculateDaysLeft(
29+
doc.deleted_at,
30+
config.TRASHBIN_CUTOFF_DAYS,
31+
);
32+
33+
dateToDisplay = `${daysLeft} ${t('days', { count: daysLeft })}`;
34+
}
35+
36+
return dateToDisplay;
37+
};
38+
39+
const useDocItemAriaLabel = (doc: Doc, isInTrashbin: boolean) => {
40+
const { t } = useTranslation();
41+
const { untitledDocument } = useTrans();
42+
const dateToDisplay = useDateToDisplay(doc, isInTrashbin);
43+
const title = doc.title || untitledDocument;
44+
const participantCount = doc.nb_accesses_direct;
45+
46+
return t(
47+
'{{title}}, updated {{date}}, shared with {{count}} participant(s)',
48+
{
49+
title,
50+
date: dateToDisplay,
51+
count: participantCount,
52+
},
53+
);
54+
};
55+
2056
type DocsGridItemProps = {
2157
doc: Doc;
2258
dragMode?: boolean;
@@ -26,13 +62,11 @@ export const DocsGridItem = ({ doc, dragMode = false }: DocsGridItemProps) => {
2662
const searchParams = useSearchParams();
2763
const target = searchParams.get('target');
2864
const isInTrashbin = target === 'trashbin';
29-
const { untitledDocument } = useTrans();
3065

31-
const { t } = useTranslation();
3266
const { isDesktop } = useResponsiveStore();
3367
const { flexLeft, flexRight } = useResponsiveDocGrid();
3468
const { spacingsTokens } = useCunninghamTheme();
35-
const dateToDisplay = useDateToDisplay(doc, isInTrashbin);
69+
const docItemAriaLabel = useDocItemAriaLabel(doc, isInTrashbin);
3670

3771
const handleKeyDown = (e: KeyboardEvent) => {
3872
if (e.key === 'Enter' || e.key === ' ') {
@@ -60,9 +94,6 @@ export const DocsGridItem = ({ doc, dragMode = false }: DocsGridItemProps) => {
6094
}
6195
`}
6296
className="--docs--doc-grid-item"
63-
aria-label={t('Open document: {{title}}', {
64-
title: doc.title || untitledDocument,
65-
})}
6697
>
6798
<Box
6899
$flex={flexLeft}
@@ -79,6 +110,7 @@ export const DocsGridItem = ({ doc, dragMode = false }: DocsGridItemProps) => {
79110
`}
80111
href={`/docs/${doc.id}`}
81112
onKeyDown={handleKeyDown}
113+
aria-label={docItemAriaLabel}
82114
>
83115
<DocsGridItemTitle doc={doc} withTooltip={!dragMode} />
84116
</StyledLink>
@@ -91,14 +123,7 @@ export const DocsGridItem = ({ doc, dragMode = false }: DocsGridItemProps) => {
91123
$justify={isDesktop ? 'space-between' : 'flex-end'}
92124
$gap="32px"
93125
>
94-
<StyledLink
95-
href={`/docs/${doc.id}`}
96-
tabIndex={-1}
97-
aria-label={t('{{title}}, updated {{date}}', {
98-
title: doc.title || untitledDocument,
99-
date: dateToDisplay,
100-
})}
101-
>
126+
<StyledLink href={`/docs/${doc.id}`} tabIndex={-1} aria-hidden="true">
102127
<DocsGridItemDate
103128
doc={doc}
104129
isDesktop={isDesktop}
@@ -202,25 +227,6 @@ const IconPublic = ({ isPublic }: { isPublic: boolean }) => {
202227
);
203228
};
204229

205-
const useDateToDisplay = (doc: Doc, isInTrashbin: boolean) => {
206-
const { data: config } = useConfig();
207-
const { t } = useTranslation();
208-
const { relativeDate, calculateDaysLeft } = useDate();
209-
210-
let dateToDisplay = relativeDate(doc.updated_at);
211-
212-
if (isInTrashbin && config?.TRASHBIN_CUTOFF_DAYS && doc.deleted_at) {
213-
const daysLeft = calculateDaysLeft(
214-
doc.deleted_at,
215-
config.TRASHBIN_CUTOFF_DAYS,
216-
);
217-
218-
dateToDisplay = `${daysLeft} ${t('days', { count: daysLeft })}`;
219-
}
220-
221-
return dateToDisplay;
222-
};
223-
224230
export const DocsGridItemDate = ({
225231
doc,
226232
isDesktop,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export const DocsGridItemSharedButton = ({ doc, disabled }: Props) => {
4444
className="--docs--doc-grid-item-shared-button"
4545
aria-label={t('Open the sharing settings for the document')}
4646
data-testid={`docs-grid-item-shared-button-${doc.id}`}
47+
tabIndex={-1}
4748
style={{
4849
padding: `0 var(--c--globals--spacings--xxxs) 0 var(--c--globals--spacings--xxxs)`,
4950
}}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export const Draggable = <T,>(props: PropsWithChildren<DraggableProps<T>>) => {
1919
ref={setNodeRef}
2020
{...listeners}
2121
{...attributes}
22+
tabIndex={-1}
2223
data-testid={`draggable-doc-${props.id}`}
2324
className="--docs--grid-draggable"
2425
role="none"

src/frontend/apps/impress/src/i18n/translations.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,8 @@
566566
"{{count}} result(s) available_one": "{{count}} Ergebnis verfügbar",
567567
"{{count}} result(s) available_other": "{{count}} Ergebnisse verfügbar",
568568
"{{name}} added to invite list. Add more members or press Tab to select role and invite.": "{{name}} wurde zur Einladungsliste hinzugefügt. Füge mehr Mitglieder hinzu oder drücke Tab, um Rollen auszuwählen und einzuladen.",
569-
"{{name}} removed from invite list": "{{name}} aus der Einladungsliste entfernt"
569+
"{{name}} removed from invite list": "{{name}} aus der Einladungsliste entfernt",
570+
"{{title}}, updated {{date}}, shared with {{count}} participant(s)": "{{title}}, aktualisiert {{date}}, geteilt mit {{count}} Teilnehmer(n)"
570571
}
571572
},
572573
"el": {
@@ -1561,7 +1562,8 @@
15611562
"{{count}} result(s) available_other": "{{count}} résultat(s) disponible(s)",
15621563
"{{name}} added to invite list. Add more members or press Tab to select role and invite.": "{{name}} a été ajouté à la liste d'invitation. Ajoutez plus de membres ou appuyez sur Tab pour sélectionner le rôle et inviter.",
15631564
"{{name}} removed from invite list": "{{name}} a été retiré de la liste d'invitation",
1564-
"{{title}}, updated {{date}}": "{{title}}, mise à jour {{date}}"
1565+
"{{title}}, updated {{date}}": "{{title}}, mise à jour {{date}}",
1566+
"{{title}}, updated {{date}}, shared with {{count}} participant(s)": "{{title}}, mis à jour {{date}}, partagé avec {{count}} participant(s)"
15651567
}
15661568
},
15671569
"it": {
@@ -2434,7 +2436,8 @@
24342436
"{{count}} result(s) available_other": "Результатов: {{count}}",
24352437
"{{name}} added to invite list. Add more members or press Tab to select role and invite.": "{{name}} добавлен(а) в список приглашённых. Добавьте других участников или нажмите Tab, чтобы выбрать роль и пригласить.",
24362438
"{{name}} removed from invite list": "{{name}} удален(а) из списка приглашений",
2437-
"{{title}}, updated {{date}}": "{{title}}, обновлено {{date}}"
2439+
"{{title}}, updated {{date}}": "{{title}}, обновлено {{date}}",
2440+
"{{title}}, updated {{date}}, shared with {{count}} participant(s)": "{{title}}, обновлено {{date}}, доступ у {{count}} участник(ов)"
24382441
}
24392442
},
24402443
"sl": {
@@ -2933,7 +2936,8 @@
29332936
"{{count}} result(s) available_other": "Результатів: {{count}}",
29342937
"{{name}} added to invite list. Add more members or press Tab to select role and invite.": "{{name}} додано до списку запрошень. Додайте більше учасників або натисніть Tab, щоб вибрати роль та надіслати запрошення.",
29352938
"{{name}} removed from invite list": "{{name}} видалено зі списку запрошень",
2936-
"{{title}}, updated {{date}}": "{{title}}, оновлено {{date}}"
2939+
"{{title}}, updated {{date}}": "{{title}}, оновлено {{date}}",
2940+
"{{title}}, updated {{date}}, shared with {{count}} participant(s)": "{{title}}, оновлено {{date}}, доступ у {{count}} учасник(ів)"
29372941
}
29382942
},
29392943
"zh": {

0 commit comments

Comments
 (0)