11import { cleanup , render as HTMLRender , waitFor , fireEvent } from '@testing-library/react'
2+ import userEvent from '@testing-library/user-event'
23import 'babel-polyfill'
34import { axe , toHaveNoViolations } from 'jest-axe'
45import React from 'react'
@@ -58,9 +59,7 @@ describe('ActionMenu', () => {
5859 const component = HTMLRender ( < Example /> )
5960 const button = component . getByText ( 'Toggle Menu' )
6061
61- // We pass keycode here to navigate a implementation detail in react-testing-library
62- // https://github.com/testing-library/react-testing-library/issues/269#issuecomment-455854112
63- fireEvent . keyDown ( button , { key : 'Enter' , charCode : 13 } )
62+ fireEvent . keyDown ( button , { key : 'Enter' } )
6463 expect ( component . getByRole ( 'menu' ) ) . toBeInTheDocument ( )
6564 cleanup ( )
6665 } )
@@ -83,6 +82,9 @@ describe('ActionMenu', () => {
8382
8483 fireEvent . click ( button )
8584 const menuItems = await waitFor ( ( ) => component . getAllByRole ( 'menuitem' ) )
85+
86+ // We pass keycode here to navigate a implementation detail in react-testing-library
87+ // https://github.com/testing-library/react-testing-library/issues/269#issuecomment-455854112
8688 fireEvent . keyPress ( menuItems [ 0 ] , { key : 'Enter' , charCode : 13 } )
8789 expect ( component . queryByRole ( 'menu' ) ) . toBeNull ( )
8890
@@ -136,6 +138,86 @@ describe('ActionMenu', () => {
136138 cleanup ( )
137139 } )
138140
141+ it ( 'should keep focus on Button when menu is opened with click' , async ( ) => {
142+ const component = HTMLRender ( < Example /> )
143+ const button = component . getByRole ( 'button' )
144+
145+ userEvent . tab ( ) // tab into the story, this should focus on the first button
146+ expect ( button ) . toEqual ( document . activeElement ) // trust, but verify
147+
148+ fireEvent . click ( button )
149+ expect ( component . queryByRole ( 'menu' ) ) . toBeInTheDocument ( )
150+
151+ /** We use waitFor because the hook uses an effect with setTimeout
152+ * and we need to wait for that to happen in the next tick
153+ */
154+ await waitFor ( ( ) => expect ( document . activeElement ) . toEqual ( button ) )
155+
156+ cleanup ( )
157+ } )
158+
159+ it ( 'should select first element when ArrowDown is pressed after opening Menu with click' , async ( ) => {
160+ const component = HTMLRender ( < Example /> )
161+
162+ const button = component . getByText ( 'Toggle Menu' )
163+ button . focus ( ) // browsers do this automatically on click, but tests don't
164+ fireEvent . click ( button )
165+ expect ( component . queryByRole ( 'menu' ) ) . toBeInTheDocument ( )
166+
167+ // button should be the active element
168+ fireEvent . keyDown ( document . activeElement ! , { key : 'ArrowDown' , code : 'ArrowDown' } )
169+
170+ await waitFor ( ( ) => {
171+ expect ( component . getAllByRole ( 'menuitem' ) [ 0 ] ) . toEqual ( document . activeElement )
172+ } )
173+
174+ cleanup ( )
175+ } )
176+
177+ it ( 'should select last element when ArrowUp is pressed after opening Menu with click' , async ( ) => {
178+ const component = HTMLRender ( < Example /> )
179+
180+ const button = component . getByText ( 'Toggle Menu' )
181+ button . focus ( ) // browsers do this automatically on click, but tests don't
182+ fireEvent . click ( button )
183+ expect ( component . queryByRole ( 'menu' ) ) . toBeInTheDocument ( )
184+
185+ // button should be the active element
186+ fireEvent . keyDown ( document . activeElement ! , { key : 'ArrowUp' , code : 'ArrowUp' } )
187+
188+ await waitFor ( ( ) => {
189+ expect ( component . getAllByRole ( 'menuitem' ) . pop ( ) ) . toEqual ( document . activeElement )
190+ } )
191+
192+ cleanup ( )
193+ } )
194+
195+ it ( 'should close the menu if Tab is pressed and move to next element' , async ( ) => {
196+ const component = HTMLRender (
197+ < >
198+ < Example />
199+ < input type = "text" placeholder = "next focusable element" />
200+ </ >
201+ )
202+ const anchor = component . getByRole ( 'button' )
203+
204+ userEvent . tab ( ) // tab into the story, this should focus on the first button
205+ expect ( anchor ) . toEqual ( document . activeElement ) // trust, but verify
206+
207+ fireEvent . keyDown ( anchor , { key : 'Enter' } )
208+ expect ( component . queryByRole ( 'menu' ) ) . toBeInTheDocument ( )
209+
210+ expect ( component . getAllByRole ( 'menuitem' ) [ 0 ] ) . toEqual ( document . activeElement )
211+
212+ await waitFor ( ( ) => {
213+ userEvent . tab ( )
214+ expect ( document . activeElement ) . toEqual ( component . getByPlaceholderText ( 'next focusable element' ) )
215+ expect ( component . queryByRole ( 'menu' ) ) . not . toBeInTheDocument ( )
216+ } )
217+
218+ cleanup ( )
219+ } )
220+
139221 it ( 'should have no axe violations' , async ( ) => {
140222 const { container} = HTMLRender ( < Example /> )
141223 const results = await axe ( container )
0 commit comments