Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add test for useConstrainedTabbing #48736

Draft
wants to merge 3 commits into
base: trunk
Choose a base branch
from
Draft
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
5 changes: 2 additions & 3 deletions packages/compose/src/hooks/use-constrained-tabbing/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/**
* WordPress dependencies
*/
import { TAB } from '@wordpress/keycodes';
import { focus } from '@wordpress/dom';

/**
Expand Down Expand Up @@ -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;
}

Expand Down
210 changes: 210 additions & 0 deletions packages/compose/src/hooks/use-constrained-tabbing/test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
/**
* External dependencies
*/
import { render, screen, 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 (
<div ref={ constrainedTabbingRef } data-testid="test-component">
<button type="button">Button 1</button>
<div data-testid="test-focusable-element" tabIndex={ -1 } />
<button type="button">Button 2</button>
<button
type="button"
onClick={ () => {
const placeholder = screen.getByTestId(
'test-focusable-element'
);
placeholder.focus();
} }
>
Button 3
</button>
</div>
);
}

it( 'should allow native tab behavior after focus is programmatically set on elements with negative tabindex', async () => {
const user = userEvent.setup();

render(
<div>
<button type="button">Focusable element before</button>
<ConstrainedTabbingComponent />
<button type="button">Focusable element after</button>
</div>
);

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',
} );
const focusableDiv = screen.getByTestId( 'test-focusable-element' );

await user.tab();
expect( focusableBefore ).toHaveFocus();

await user.tab();
expect( button1 ).toHaveFocus();

await user.tab();
expect( button2 ).toHaveFocus();

await user.tab();
expect( button3 ).toHaveFocus();

await button3.click();

expect( focusableDiv ).toHaveFocus();

await user.tab();
expect( button2 ).toHaveFocus();
} );

it( 'should prepend and focus the trap element when tabbing forwards', async () => {
const user = userEvent.setup();

render(
<div>
<button type="button">Focusable element before</button>
<ConstrainedTabbingComponent />
<button type="button">Focusable element after</button>
</div>
);

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;

expect( trap.outerHTML ).toEqual( '<div tabindex="-1"></div>' );
expect( trap ).toHaveFocus();
} );

it( 'should constrain tabbing when tabbing forwards using await user.tab', async () => {
const user = userEvent.setup();

render(
<div>
<button type="button">Focusable element before</button>
<ConstrainedTabbingComponent />
<button type="button">Focusable element after</button>
</div>
);

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 goes to `<button type="button">Focusable element after</button>`
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(
<div>
<button type="button">Focusable element before</button>
<ConstrainedTabbingComponent />
<button type="button">Focusable element after</button>
</div>
);

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 `<button type="button">Button 3</button>`
user.tab();
expect( button1 ).toHaveFocus();
} );
} );
Loading