diff --git a/hooks/__tests__/use-focus-trap.test.tsx b/hooks/__tests__/use-focus-trap.test.tsx index 6f3e212d7..f820f8820 100644 --- a/hooks/__tests__/use-focus-trap.test.tsx +++ b/hooks/__tests__/use-focus-trap.test.tsx @@ -141,3 +141,19 @@ describe('useFocusTrap', () => { expect(screen.getByTestId('panel')).not.toHaveAttribute('tabindex'); }); }); + +describe('useFocusTrap and the page behind', () => { + it('keeps an Escape that closed the dialog from the page', () => { + const page = vi.fn(); + document.addEventListener('keydown', page); + try { + const onEscape = vi.fn(); + render(); + fireEvent.keyDown(screen.getByLabelText('name'), { key: 'Escape' }); + expect(onEscape).toHaveBeenCalledTimes(1); + expect(page).not.toHaveBeenCalled(); + } finally { + document.removeEventListener('keydown', page); + } + }); +}); diff --git a/hooks/use-focus-trap.ts b/hooks/use-focus-trap.ts index b6ef9b684..e80339591 100644 --- a/hooks/use-focus-trap.ts +++ b/hooks/use-focus-trap.ts @@ -70,7 +70,11 @@ export function useFocusTrap({ const handleKeyDown = (e: KeyboardEvent) => { // Handle Escape if (e.key === 'Escape' && onEscapeRef.current) { - if (isTopmost()) onEscapeRef.current(); + if (!isTopmost()) return; + // Escape closes the innermost layer only (R8): the page's own + // handlers (Files clears its selection) must not see it too. + e.stopPropagation(); + onEscapeRef.current(); return; }