diff --git a/src/hooks/useAccessibility.ts b/src/hooks/useAccessibility.ts index a946e39..0f6d26f 100644 --- a/src/hooks/useAccessibility.ts +++ b/src/hooks/useAccessibility.ts @@ -28,12 +28,22 @@ export default function useAccessibility({ }; const focusMenu = (options?: FocusOptions) => { - if (overlayRef.current?.focus) { - overlayRef.current.focus(options); - focusMenuRef.current = true; - return true; + const overlay = overlayRef?.current; + if (!overlay?.focus) { + return false; } - return false; + + const activeElement = document.activeElement; + overlay.focus(options); + if (document.activeElement === activeElement) { + const focusTarget = (overlay.querySelector?.('[role="menu"]') ?? + overlay.querySelector?.('[tabindex]')) as HTMLElement | null; + focusTarget?.focus(options); + } + + const focused = document.activeElement !== activeElement; + focusMenuRef.current = focused; + return focused; }; const handleKeyDown = (event) => { diff --git a/tests/basic.test.tsx b/tests/basic.test.tsx index ed7e5cf..a5987d0 100644 --- a/tests/basic.test.tsx +++ b/tests/basic.test.tsx @@ -492,9 +492,16 @@ describe('dropdown', () => { // Focus menu with Tab window.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 9 })); // Tab + expect(document.activeElement).toHaveClass('rc-menu'); + fireEvent.keyDown(document.activeElement, { + key: 'ArrowDown', + keyCode: 40, + }); + await sleep(50); + expect(document.activeElement).toHaveTextContent('one'); // Close menu with Tab - window.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 9 })); // Tab + fireEvent.keyDown(document.activeElement, { key: 'Tab', keyCode: 9 }); await sleep(200); expect(document.activeElement.className).toContain('my-button'); }); @@ -584,50 +591,57 @@ describe('dropdown', () => { jest.useRealTimers(); }); - it('should support autoFocus', async () => { - jest.useFakeTimers(); - const focusSpy = jest.spyOn(HTMLElement.prototype, 'focus'); + it.each(['direct', 'wrapped'])( + 'should support autoFocus for a %s menu', + async (mode) => { + jest.useFakeTimers(); + const focusSpy = jest.spyOn(HTMLElement.prototype, 'focus'); - try { - const overlay = ( - - - one - - two - - ); - const { container } = render( - - - , - ); - const trigger = container.querySelector('.my-button'); - - // Open menu - fireEvent.click(trigger); - - await waitForTime(); - - expect( - container - .querySelector('.rc-dropdown') - .classList.contains('rc-dropdown-hidden'), - ).toBeFalsy(); - expect(document.activeElement.className).toContain('menu'); - expect(focusSpy).toHaveBeenCalledWith({ preventScroll: true }); - - // Close menu with Tab - window.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 9 })); // Tab - - await waitForTime(); - - expect(document.activeElement.className).toContain('my-button'); - } finally { - focusSpy.mockRestore(); - jest.useRealTimers(); - } - }); + try { + const overlay = ( + + + one + + two + + ); + const { container } = render( + {overlay} : overlay} + > + + , + ); + const trigger = container.querySelector('.my-button'); + + // Open menu + fireEvent.click(trigger); + + await waitForTime(); + + expect( + container + .querySelector('.rc-dropdown') + .classList.contains('rc-dropdown-hidden'), + ).toBeFalsy(); + expect(document.activeElement.className).toContain('menu'); + expect(focusSpy).toHaveBeenLastCalledWith({ preventScroll: true }); + + // Close menu with Tab + window.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 9 })); // Tab + + await waitForTime(); + + expect(document.activeElement.className).toContain('my-button'); + } finally { + focusSpy.mockRestore(); + jest.useRealTimers(); + } + }, + ); it('children cannot be given ref should not throw', () => { const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});