From 82b667a8662bad3065b96a995c0e2be987e1e8b3 Mon Sep 17 00:00:00 2001 From: Andrea Fercia Date: Fri, 3 Mar 2023 16:40:08 +0100 Subject: [PATCH 1/3] Add test for useConstrainedTabbing first pass. --- .../hooks/use-constrained-tabbing/index.js | 5 +- .../use-constrained-tabbing/test/index.js | 88 +++++++++++++++++++ 2 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 packages/compose/src/hooks/use-constrained-tabbing/test/index.js diff --git a/packages/compose/src/hooks/use-constrained-tabbing/index.js b/packages/compose/src/hooks/use-constrained-tabbing/index.js index 97b8a2a0a5eb5..998c8a4d21671 100644 --- a/packages/compose/src/hooks/use-constrained-tabbing/index.js +++ b/packages/compose/src/hooks/use-constrained-tabbing/index.js @@ -1,7 +1,6 @@ /** * WordPress dependencies */ -import { TAB } from '@wordpress/keycodes'; import { focus } from '@wordpress/dom'; /** @@ -33,9 +32,9 @@ import useRefEffect from '../use-ref-effect'; function useConstrainedTabbing() { return useRefEffect( ( /** @type {HTMLElement} */ node ) => { function onKeyDown( /** @type {KeyboardEvent} */ event ) { - const { keyCode, shiftKey, target } = event; + const { code, shiftKey, target } = event; - if ( keyCode !== TAB ) { + if ( code !== 'Tab' ) { return; } diff --git a/packages/compose/src/hooks/use-constrained-tabbing/test/index.js b/packages/compose/src/hooks/use-constrained-tabbing/test/index.js new file mode 100644 index 0000000000000..7761c811e9d4c --- /dev/null +++ b/packages/compose/src/hooks/use-constrained-tabbing/test/index.js @@ -0,0 +1,88 @@ +/** + * External dependencies + */ +import { render, screen, waitFor, fireEvent } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +/** + * Internal dependencies + */ +import useConstrainedTabbing from '../'; + +describe( 'useConstrainedTabbing', () => { + function ConstrainedTabbingComponent() { + const constrainedTabbingRef = useConstrainedTabbing(); + return ( +
+ +
+ + +
+ ); + } + + it( 'should constrain tabbing when tabbing forwards', async () => { + const user = userEvent.setup( { delay: 100 } ); + + render( +
+ + + +
+ ); + + const focusableBefore = screen.getByRole( 'button', { + name: 'Focusable element before', + } ); + const button1 = screen.getByRole( 'button', { + name: 'Button 1', + } ); + const button2 = screen.getByRole( 'button', { + name: 'Button 2', + } ); + const button3 = screen.getByRole( 'button', { + name: 'Button 3', + } ); + + await user.keyboard( '{Tab}' ); + expect( focusableBefore ).toHaveFocus(); + + await user.keyboard( '{Tab}' ); + expect( button1 ).toHaveFocus(); + + await user.keyboard( '{Tab}' ); + expect( button2 ).toHaveFocus(); + + await user.keyboard( '{Tab}' ); + expect( button3 ).toHaveFocus(); + + // Looks like the React Testing Library didn't implement event.keycode. + // Also, we can't use user.Tab() and the like, as the trap element is + // injected in the DOM while the Tab key is being pressed. + fireEvent.keyDown( button3, { code: 'Tab' } ); + + const component = screen.getByTestId( 'test-component' ); + // eslint-disable-next-line testing-library/no-node-access + const trap = component.firstChild; + + await waitFor( () => + expect( trap.outerHTML ).toEqual( '
' ) + ); + + expect( trap ).toHaveFocus(); + + // At this point, the trap element should be blurred. + // Then, the focused element should be Button 1. + } ); +} ); From 3265482f95e006ff1747dd92e31c69911ef55c0a Mon Sep 17 00:00:00 2001 From: Andrea Fercia Date: Mon, 20 Mar 2023 11:10:34 +0100 Subject: [PATCH 2/3] Fix ids. --- .../src/hooks/use-constrained-tabbing/test/index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/compose/src/hooks/use-constrained-tabbing/test/index.js b/packages/compose/src/hooks/use-constrained-tabbing/test/index.js index 7761c811e9d4c..5344236afabe2 100644 --- a/packages/compose/src/hooks/use-constrained-tabbing/test/index.js +++ b/packages/compose/src/hooks/use-constrained-tabbing/test/index.js @@ -15,13 +15,14 @@ describe( 'useConstrainedTabbing', () => { return (
-
+
+ + +
+ ); + + const focusableBefore = screen.getByRole( 'button', { + name: 'Focusable element before', + } ); + const button1 = screen.getByRole( 'button', { + name: 'Button 1', + } ); + const button2 = screen.getByRole( 'button', { + name: 'Button 2', + } ); + const button3 = screen.getByRole( 'button', { + name: 'Button 3', + } ); + + await user.tab(); + expect( focusableBefore ).toHaveFocus(); + + await user.tab(); + expect( button1 ).toHaveFocus(); + + await user.tab(); + expect( button2 ).toHaveFocus(); + + await user.tab(); + expect( button3 ).toHaveFocus(); const component = screen.getByTestId( 'test-component' ); + + // Looks like the React Testing Library didn't implement event.keycode. + // Note: Using await user.tab() would make the test fail. + fireEvent.keyDown( component, { code: 'Tab' } ); + // eslint-disable-next-line testing-library/no-node-access const trap = component.firstChild; - await waitFor( () => - expect( trap.outerHTML ).toEqual( '
' ) + expect( trap.outerHTML ).toEqual( '
' ); + expect( trap ).toHaveFocus(); + } ); + + it( 'should constrain tabbing when tabbing forwards using await user.tab', async () => { + const user = userEvent.setup(); + + render( +
+ + + +
); - expect( trap ).toHaveFocus(); + const focusableBefore = screen.getByRole( 'button', { + name: 'Focusable element before', + } ); + const button1 = screen.getByRole( 'button', { + name: 'Button 1', + } ); + const button2 = screen.getByRole( 'button', { + name: 'Button 2', + } ); + const button3 = screen.getByRole( 'button', { + name: 'Button 3', + } ); + + await user.tab(); + expect( focusableBefore ).toHaveFocus(); + + await user.tab(); + expect( button1 ).toHaveFocus(); - // At this point, the trap element should be blurred. - // Then, the focused element should be Button 1. + await user.tab(); + expect( button2 ).toHaveFocus(); + + await user.tab(); + expect( button3 ).toHaveFocus(); + + // Fails. Focus goes to `` + await user.tab(); + expect( button1 ).toHaveFocus(); + } ); + + it( 'should constrain tabbing when tabbing forwards using user.tab with no await', async () => { + const user = userEvent.setup(); + + render( +
+ + + +
+ ); + + const focusableBefore = screen.getByRole( 'button', { + name: 'Focusable element before', + } ); + const button1 = screen.getByRole( 'button', { + name: 'Button 1', + } ); + const button2 = screen.getByRole( 'button', { + name: 'Button 2', + } ); + const button3 = screen.getByRole( 'button', { + name: 'Button 3', + } ); + + await user.tab(); + expect( focusableBefore ).toHaveFocus(); + + await user.tab(); + expect( button1 ).toHaveFocus(); + + await user.tab(); + expect( button2 ).toHaveFocus(); + + await user.tab(); + expect( button3 ).toHaveFocus(); + + // Fails. Focus is still on `` + user.tab(); + expect( button1 ).toHaveFocus(); } ); } );