Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -180,7 +179,7 @@ describe( 'BlockSwitcherDropdownMenu', () => {
const onToggleStub = jest.fn();
const mockKeyDown = {
preventDefault: () => {},
keyCode: DOWN,
code: 'ArrowDown',
};

afterEach( () => {
Expand Down
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 1 addition & 2 deletions packages/components/src/dropdown-menu/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import classnames from 'classnames';
/**
* WordPress dependencies
*/
import { DOWN } from '@wordpress/keycodes';
import { menu } from '@wordpress/icons';

/**
Expand Down Expand Up @@ -87,7 +86,7 @@ function DropdownMenu( dropdownMenuProps ) {
return;
}

if ( ! isOpen && event.keyCode === DOWN ) {
if ( ! isOpen && event.code === 'ArrowDown' ) {
event.preventDefault();
onToggle();
}
Expand Down
13 changes: 0 additions & 13 deletions packages/components/src/dropdown-menu/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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(
Expand All @@ -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 }
Expand Down
112 changes: 54 additions & 58 deletions packages/components/src/dropdown-menu/test/index.js
Original file line number Diff line number Diff line change
@@ -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';

/**
Expand All @@ -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 } ) => <MenuItem onClick={ onClose } />;
it( 'should not render when neither controls nor children are assigned', () => {
render( <DropdownMenu /> );

// 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( <DropdownMenu controls={ [] } /> );

// 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,
Expand All @@ -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( <DropdownMenu /> );
render( <DropdownMenu controls={ controls } /> );

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( <DropdownMenu controls={ [] } /> );
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( <DropdownMenu controls={ controls } /> );

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( <DropdownMenu children={ children } /> );
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(
<DropdownMenu
children={ ( { onClose } ) => <MenuItem onClick={ onClose } /> }
/>
);

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();
} );
} );