Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ and this project adheres to
- 🐛(backend) fix trashbin list
- ♿(frontend) improve accessibility:
- ♿(frontend) remove empty alt on logo due to Axe a11y error #1516
- ♿(frontend) add focus trap and enter key support to remove doc modal #1531

## [3.8.2] - 2025-10-17

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { HorizontalSeparator } from '@gouvfr-lasuite/ui-kit';
import {
Fragment,
KeyboardEvent,
PropsWithChildren,
ReactNode,
useCallback,
Expand Down Expand Up @@ -93,6 +94,18 @@ export const DropdownMenu = ({
}
}, [isOpen, options]);

const handleKeyDown = useCallback(
(event: KeyboardEvent<HTMLElement>, option: DropdownMenuOption) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
event.stopPropagation();
onOpenChange?.(false);
void option.callback?.();
}
},
[onOpenChange],
);

Comment on lines +97 to +108
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You just created the hook useKeyboardAction, you should use it.

if (disabled) {
return children;
}
Expand Down Expand Up @@ -173,6 +186,7 @@ export const DropdownMenu = ({
onOpenChange?.(false);
void option.callback?.();
}}
onKeyDown={(event) => handleKeyDown(event, option)}
key={option.label}
$align="center"
$justify="space-between"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,21 @@ import {
useToastProvider,
} from '@openfun/cunningham-react';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { Trans, useTranslation } from 'react-i18next';

import { Box, ButtonCloseModal, Text, TextErrors } from '@/components';
import { useConfig } from '@/core';
import { KEY_LIST_DOC_TRASHBIN } from '@/docs/docs-grid';
import { useKeyboardAction } from '@/hooks';

import { KEY_LIST_DOC } from '../api/useDocs';
import { useRemoveDoc } from '../api/useRemoveDoc';
import { useDocUtils } from '../hooks';
import { Doc } from '../types';

const CANCEL_BUTTON_ID = 'modal-remove-doc-cancel-button';

interface ModalRemoveDocProps {
doc: Doc;
onClose: () => void;
Expand Down Expand Up @@ -57,32 +61,51 @@ export const ModalRemoveDoc = ({
},
});

useEffect(() => {
const cancelButton = document.getElementById(CANCEL_BUTTON_ID);
if (cancelButton instanceof HTMLButtonElement) {
Comment on lines +65 to +66
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is better to use ref instead of an id or className in React.

cancelButton.focus();
}
}, []);
Comment on lines +64 to +69
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the purpose of this useEffect, I don't see any changed when I comment this snippet.


const handleClose = () => {
onClose();
};

const handleDelete = () => {
removeDoc({
docId: doc.id,
});
};

const handleCloseKeyDown = useKeyboardAction(handleClose);
const handleDeleteKeyDown = useKeyboardAction(handleDelete);

return (
<Modal
isOpen
closeOnClickOutside
hideCloseButton
onClose={() => onClose()}
onClose={handleClose}
aria-describedby="modal-remove-doc-title"
rightActions={
<>
<Button
id={CANCEL_BUTTON_ID}
aria-label={t('Cancel the deletion')}
color="secondary"
fullWidth
onClick={() => onClose()}
onClick={handleClose}
onKeyDown={handleCloseKeyDown}
>
{t('Cancel')}
</Button>
<Button
aria-label={t('Delete document')}
color="danger"
fullWidth
onClick={() =>
removeDoc({
docId: doc.id,
})
}
onClick={handleDelete}
onKeyDown={handleDeleteKeyDown}
>
{t('Delete')}
</Button>
Expand All @@ -108,7 +131,8 @@ export const ModalRemoveDoc = ({
</Text>
<ButtonCloseModal
aria-label={t('Close the delete modal')}
onClick={() => onClose()}
onClick={handleClose}
onKeyDown={handleCloseKeyDown}
/>
</Box>
}
Expand Down
1 change: 1 addition & 0 deletions src/frontend/apps/impress/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useKeyboardAction';
22 changes: 22 additions & 0 deletions src/frontend/apps/impress/src/hooks/useKeyboardAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { KeyboardEvent, useCallback } from 'react';

/**
* Hook to handle keyboard actions (Enter and Space) on interactive elements.
* Prevents default behavior and stops propagation to avoid conflicts.
*
* @param callback - The function to execute when Enter or Space is pressed
* @returns A keyboard event handler function

*/
export const useKeyboardAction = (callback: () => void) => {
return useCallback(
(event: KeyboardEvent<HTMLElement>) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
event.stopPropagation();
callback();
}
},
[callback],
);
};
Loading