Skip to content

Commit de84160

Browse files
committed
fixup! ✨(frontend) make components accessible to screen readers
1 parent 23004ee commit de84160

File tree

4 files changed

+156
-125
lines changed

4 files changed

+156
-125
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { useCallback } from 'react';
2+
import { useTranslation } from 'react-i18next';
3+
4+
import { BoxButton, Icon } from '@/components';
5+
6+
type ButtonAddChildDocProps = {
7+
onCreateChild: (params: { parentId: string }) => void;
8+
parentId: string;
9+
title?: string | null;
10+
};
11+
12+
export const ButtonAddChildDoc = ({
13+
onCreateChild,
14+
parentId,
15+
title,
16+
}: ButtonAddChildDocProps) => {
17+
const { t } = useTranslation();
18+
19+
const preventDefaultAndStopPropagation = useCallback(
20+
(e: React.MouseEvent | React.KeyboardEvent) => {
21+
e.stopPropagation();
22+
e.preventDefault();
23+
},
24+
[],
25+
);
26+
27+
const isValidKeyEvent = useCallback((e: React.KeyboardEvent) => {
28+
return e.key === 'Enter' || e.key === ' ';
29+
}, []);
30+
31+
const handleClick = useCallback(
32+
(e: React.MouseEvent) => {
33+
preventDefaultAndStopPropagation(e);
34+
void onCreateChild({ parentId });
35+
},
36+
[onCreateChild, parentId, preventDefaultAndStopPropagation],
37+
);
38+
39+
const handleKeyDown = useCallback(
40+
(e: React.KeyboardEvent) => {
41+
if (isValidKeyEvent(e)) {
42+
preventDefaultAndStopPropagation(e);
43+
void onCreateChild({ parentId });
44+
}
45+
},
46+
[
47+
onCreateChild,
48+
parentId,
49+
preventDefaultAndStopPropagation,
50+
isValidKeyEvent,
51+
],
52+
);
53+
54+
return (
55+
<BoxButton
56+
as="button"
57+
tabIndex={0}
58+
data-testid="add-child-doc"
59+
onClick={handleClick}
60+
onKeyDown={handleKeyDown}
61+
color="primary"
62+
aria-label={
63+
t('Add child document to') + ` ${title || t('Untitled document')}`
64+
}
65+
$hasTransition={false}
66+
>
67+
<Icon
68+
variant="filled"
69+
$variation="800"
70+
$theme="primary"
71+
iconName="add_box"
72+
aria-hidden="true"
73+
/>
74+
</BoxButton>
75+
);
76+
};
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { useCallback } from 'react';
2+
import { useTranslation } from 'react-i18next';
3+
4+
import { Icon } from '@/components';
5+
6+
type ButtonMoreOptionsProps = {
7+
isOpen?: boolean;
8+
onOpenChange?: (isOpen: boolean) => void;
9+
title?: string | null;
10+
className?: string;
11+
};
12+
13+
export const ButtonMoreOptions = ({
14+
isOpen,
15+
onOpenChange,
16+
title,
17+
className = 'icon-button',
18+
}: ButtonMoreOptionsProps) => {
19+
const { t } = useTranslation();
20+
21+
const preventDefaultAndStopPropagation = useCallback(
22+
(e: React.MouseEvent | React.KeyboardEvent) => {
23+
e.stopPropagation();
24+
e.preventDefault();
25+
},
26+
[],
27+
);
28+
29+
const isValidKeyEvent = useCallback((e: React.KeyboardEvent) => {
30+
return e.key === 'Enter' || e.key === ' ';
31+
}, []);
32+
33+
const handleClick = useCallback(
34+
(e: React.MouseEvent) => {
35+
preventDefaultAndStopPropagation(e);
36+
onOpenChange?.(!isOpen);
37+
},
38+
[isOpen, onOpenChange, preventDefaultAndStopPropagation],
39+
);
40+
41+
const handleKeyDown = useCallback(
42+
(e: React.KeyboardEvent) => {
43+
if (isValidKeyEvent(e)) {
44+
preventDefaultAndStopPropagation(e);
45+
onOpenChange?.(!isOpen);
46+
}
47+
},
48+
[isOpen, onOpenChange, preventDefaultAndStopPropagation, isValidKeyEvent],
49+
);
50+
51+
return (
52+
<Icon
53+
onClick={handleClick}
54+
iconName="more_horiz"
55+
variant="filled"
56+
$theme="primary"
57+
$variation="600"
58+
className={className}
59+
tabIndex={0}
60+
role="button"
61+
aria-label={t('More options for') + ` ${title || t('Untitled document')}`}
62+
aria-haspopup="true"
63+
aria-expanded={isOpen}
64+
onKeyDown={handleKeyDown}
65+
/>
66+
);
67+
};

src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTreeItemActions.tsx

Lines changed: 13 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ import { useRouter } from 'next/router';
88
import { useTranslation } from 'react-i18next';
99
import { css } from 'styled-components';
1010

11-
import { Box, BoxButton, Icon } from '@/components';
12-
import { useDocTreeItemHandlers } from '@/features/docs/doc-tree/hooks/useDocTreeItemHandlers';
13-
import { useDropdownFocusManagement } from '@/features/docs/doc-tree/hooks/useDropdownFocusManagement';
11+
import { Box, Icon } from '@/components';
1412
import {
1513
Doc,
1614
ModalRemoveDoc,
@@ -19,6 +17,9 @@ import {
1917
useCreateChildDoc,
2018
useDuplicateDoc,
2119
} from '@/docs/doc-management';
20+
import { ButtonAddChildDoc } from '@/features/docs/doc-tree/components/ButtonAddChildDoc';
21+
import { ButtonMoreOptions } from '@/features/docs/doc-tree/components/ButtonMoreOptions';
22+
import { useDropdownFocusManagement } from '@/features/docs/doc-tree/hooks/useDropdownFocusManagement';
2223

2324
import { useDetachDoc } from '../api/useDetach';
2425
import MoveDocIcon from '../assets/doc-extract-bold.svg';
@@ -149,18 +150,6 @@ export const DocTreeItemActions = ({
149150
}
150151
};
151152

152-
const {
153-
handleMoreOptionsClick,
154-
handleMoreOptionsKeyDown,
155-
handleAddChildClick,
156-
handleAddChildKeyDown,
157-
} = useDocTreeItemHandlers({
158-
isOpen,
159-
onOpenChange,
160-
createChildDoc,
161-
docId: doc.id,
162-
});
163-
164153
useDropdownFocusManagement({
165154
isOpen: isOpen || false,
166155
docId: doc.id,
@@ -202,45 +191,18 @@ export const DocTreeItemActions = ({
202191
isOpen={isOpen}
203192
onOpenChange={onOpenChange}
204193
>
205-
<Icon
206-
onClick={handleMoreOptionsClick}
207-
iconName="more_horiz"
208-
variant="filled"
209-
$theme="primary"
210-
$variation="600"
211-
className="icon-button"
212-
tabIndex={0}
213-
role="button"
214-
aria-label={
215-
t('More options for') + ` ${doc.title || t('Untitled document')}`
216-
}
217-
aria-haspopup="true"
218-
aria-expanded={isOpen}
219-
onKeyDown={handleMoreOptionsKeyDown}
194+
<ButtonMoreOptions
195+
isOpen={isOpen}
196+
onOpenChange={onOpenChange}
197+
title={doc.title}
220198
/>
221199
</DropdownMenu>
222200
{doc.abilities.children_create && (
223-
<BoxButton
224-
as="button"
225-
tabIndex={0}
226-
data-testid="add-child-doc"
227-
onClick={handleAddChildClick}
228-
onKeyDown={handleAddChildKeyDown}
229-
color="primary"
230-
aria-label={
231-
t('Add child document to') +
232-
` ${doc.title || t('Untitled document')}`
233-
}
234-
$hasTransition={false}
235-
>
236-
<Icon
237-
variant="filled"
238-
$variation="800"
239-
$theme="primary"
240-
iconName="add_box"
241-
aria-hidden="true"
242-
/>
243-
</BoxButton>
201+
<ButtonAddChildDoc
202+
onCreateChild={createChildDoc}
203+
parentId={doc.id}
204+
title={doc.title}
205+
/>
244206
)}
245207
</Box>
246208
{deleteModal.isOpen && (

src/frontend/apps/impress/src/features/docs/doc-tree/hooks/useDocTreeItemHandlers.ts

Lines changed: 0 additions & 74 deletions
This file was deleted.

0 commit comments

Comments
 (0)