diff --git a/packages/block-editor/src/components/block-switcher/test/index.js b/packages/block-editor/src/components/block-switcher/test/index.js index 3cdb022dc361ff..ae073d74d07a66 100644 --- a/packages/block-editor/src/components/block-switcher/test/index.js +++ b/packages/block-editor/src/components/block-switcher/test/index.js @@ -8,7 +8,6 @@ import { shallow, mount } from 'enzyme'; */ import { useSelect } from '@wordpress/data'; import { registerBlockType, unregisterBlockType } from '@wordpress/blocks'; -import { DOWN } from '@wordpress/keycodes'; import { Button } from '@wordpress/components'; import { copy } from '@wordpress/icons'; @@ -180,7 +179,7 @@ describe( 'BlockSwitcherDropdownMenu', () => { const onToggleStub = jest.fn(); const mockKeyDown = { preventDefault: () => {}, - keyCode: DOWN, + code: 'ArrowDown', }; afterEach( () => { diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 3aceea9955d780..160d26257a1748 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -62,6 +62,7 @@ - `Tooltip`: Refactor away from `_.includes()` ([#43518](https://github.com/WordPress/gutenberg/pull/43518/)). - `TreeGrid`: Refactor away from `_.includes()` ([#43518](https://github.com/WordPress/gutenberg/pull/43518/)). - `FormTokenField`: use `KeyboardEvent.code`, refactor tests to modern RTL and `user-event` ([#43442](https://github.com/WordPress/gutenberg/pull/43442/)). +- `DropdownMenu`: use `KeyboardEvent.code`, refactor tests to model RTL and `user-event` ([#43439](https://github.com/WordPress/gutenberg/pull/43439/)). ### Experimental diff --git a/packages/components/src/dropdown-menu/index.js b/packages/components/src/dropdown-menu/index.js index 9687ecce8ebc7b..481d84a6589f48 100644 --- a/packages/components/src/dropdown-menu/index.js +++ b/packages/components/src/dropdown-menu/index.js @@ -7,7 +7,6 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { DOWN } from '@wordpress/keycodes'; import { menu } from '@wordpress/icons'; /** @@ -87,7 +86,7 @@ function DropdownMenu( dropdownMenuProps ) { return; } - if ( ! isOpen && event.keyCode === DOWN ) { + if ( ! isOpen && event.code === 'ArrowDown' ) { event.preventDefault(); onToggle(); } diff --git a/packages/components/src/dropdown-menu/index.native.js b/packages/components/src/dropdown-menu/index.native.js index f75ac157616ade..1f60e0f67cf088 100644 --- a/packages/components/src/dropdown-menu/index.native.js +++ b/packages/components/src/dropdown-menu/index.native.js @@ -6,7 +6,6 @@ import { Platform } from 'react-native'; /** * WordPress dependencies */ -import { DOWN } from '@wordpress/keycodes'; import { BottomSheet, PanelBody } from '@wordpress/components'; import { withPreferredColorScheme } from '@wordpress/compose'; import { menu } from '@wordpress/icons'; @@ -76,12 +75,6 @@ function DropdownMenu( { className={ classnames( 'components-dropdown-menu', className ) } popoverProps={ mergedPopoverProps } renderToggle={ ( { isOpen, onToggle } ) => { - const openOnArrowDown = ( event ) => { - if ( ! isOpen && event.keyCode === DOWN ) { - event.preventDefault(); - onToggle(); - } - }; const mergedToggleProps = mergeProps( { className: classnames( @@ -104,12 +97,6 @@ function DropdownMenu( { mergedToggleProps.onClick( event ); } } } - onKeyDown={ ( event ) => { - openOnArrowDown( event ); - if ( mergedToggleProps.onKeyDown ) { - mergedToggleProps.onKeyDown( event ); - } - } } aria-haspopup="true" aria-expanded={ isOpen } label={ label } diff --git a/packages/components/src/dropdown-menu/test/index.js b/packages/components/src/dropdown-menu/test/index.js index 3a26af1e60313a..2eab09f6c052df 100644 --- a/packages/components/src/dropdown-menu/test/index.js +++ b/packages/components/src/dropdown-menu/test/index.js @@ -1,12 +1,12 @@ /** * External dependencies */ -import { fireEvent, render } from '@testing-library/react'; +import { render, screen, waitFor, within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; /** * WordPress dependencies */ -import { DOWN } from '@wordpress/keycodes'; import { arrowLeft, arrowRight, arrowUp, arrowDown } from '@wordpress/icons'; /** @@ -15,19 +15,27 @@ import { arrowLeft, arrowRight, arrowUp, arrowDown } from '@wordpress/icons'; import DropdownMenu from '../'; import { MenuItem } from '../../'; -function getMenuToggleButton( container ) { - return container.querySelector( '.components-dropdown-menu__toggle' ); -} -function getNavigableMenu( container ) { - return container.querySelector( '.components-dropdown-menu__menu' ); -} - describe( 'DropdownMenu', () => { - const children = ( { onClose } ) => ; + it( 'should not render when neither controls nor children are assigned', () => { + render( ); + + // The button toggle should not even be rendered + expect( screen.queryByRole( 'button' ) ).not.toBeInTheDocument(); + } ); + + it( 'should not render when controls are empty and children is not specified', () => { + render( ); + + // The button toggle should not even be rendered + expect( screen.queryByRole( 'button' ) ).not.toBeInTheDocument(); + } ); + + it( 'should open menu when pressing arrow down on the toggle and the controls prop is used to define menu items', async () => { + const user = userEvent.setup( { + advanceTimers: jest.advanceTimersByTime, + } ); - let controls; - beforeEach( () => { - controls = [ + const controls = [ { title: 'Up', icon: arrowUp, @@ -49,62 +57,50 @@ describe( 'DropdownMenu', () => { onClick: jest.fn(), }, ]; - } ); - describe( 'basic rendering', () => { - it( 'should render a null element when neither controls nor children are assigned', () => { - const { container } = render( ); + render( ); - expect( container.firstChild ).toBeNull(); - } ); + // Move focus on the toggle button + await user.tab(); - it( 'should render a null element when controls are empty and children is not specified', () => { - const { container } = render( ); + await user.keyboard( '[ArrowDown]' ); - expect( container.firstChild ).toBeNull(); + let menu; + await waitFor( () => { + menu = screen.getByRole( 'menu' ); + return expect( menu ).toBeVisible(); } ); - it( 'should open menu on arrow down (controls)', () => { - const { - container: { firstChild: dropdownMenuContainer }, - } = render( ); - - const button = getMenuToggleButton( dropdownMenuContainer ); - button.focus(); - fireEvent.keyDown( button, { - keyCode: DOWN, - preventDefault: () => {}, - } ); - const menu = getNavigableMenu( dropdownMenuContainer ); - expect( menu ).toBeTruthy(); - - expect( - dropdownMenuContainer.querySelectorAll( - '.components-dropdown-menu__menu-item' - ) - ).toHaveLength( controls.length ); - } ); + expect( within( menu ).getAllByRole( 'menuitem' ) ).toHaveLength( + controls.length + ); + } ); - it( 'should open menu on arrow down (children)', () => { - const { - container: { firstChild: dropdownMenuContainer }, - } = render( ); + it( 'should open menu when pressing arrow down on the toggle and the children prop is used to define menu items', async () => { + const user = userEvent.setup( { + advanceTimers: jest.advanceTimersByTime, + } ); - const button = getMenuToggleButton( dropdownMenuContainer ); - button.focus(); - fireEvent.keyDown( button, { - keyCode: DOWN, - preventDefault: () => {}, - } ); + render( + } + /> + ); - expect( getNavigableMenu( dropdownMenuContainer ) ).toBeTruthy(); + const button = screen.getByRole( 'button' ); + button.focus(); - const menuItem = dropdownMenuContainer.querySelector( - '.components-menu-item__button' - ); - fireEvent.click( menuItem ); + await user.keyboard( '[ArrowDown]' ); - expect( getNavigableMenu( dropdownMenuContainer ) ).toBeNull(); + let menu; + await waitFor( () => { + menu = screen.getByRole( 'menu' ); + return expect( menu ).toBeVisible(); } ); + + // Clicking the menu item will close the dropdown menu + await user.click( within( menu ).getByRole( 'menuitem' ) ); + + expect( screen.queryByRole( 'menu' ) ).not.toBeInTheDocument(); } ); } );