@@ -6,9 +6,11 @@ import userEvent from '@testing-library/user-event';
66
77// A mock component for testing the Hook
88const TestComponent : React . FC = ( ) => {
9- const [ itemCount , setItemCount ] = useState ( 3 ) ;
9+ const [ itemCount , setItemCount ] = useState ( 4 ) ;
1010 const { buttonProps, itemProps, isOpen, setIsOpen } = useDropdownMenu ( itemCount ) ;
1111
12+ const clickHandlers : ( ( ) => void ) [ ] = [ ( ) : void => console . log ( 'Item one clicked' ) , ( ) : void => setIsOpen ( false ) ] ;
13+
1214 return (
1315 < React . Fragment >
1416 < button { ...buttonProps } id = 'menu-button' >
@@ -21,10 +23,10 @@ const TestComponent: React.FC = () => {
2123 { ...props }
2224 key = { i }
2325 id = { `menu-item-${ i + 1 } ` }
24- onClick = { i === 0 ? ( ) : void => setIsOpen ( false ) : undefined }
25- href = { i !== 0 ? 'https://example.com' : undefined }
26+ onClick = { clickHandlers [ i ] }
27+ href = { i > 1 ? 'https://example.com' : undefined }
2628 >
27- Item { i + 1 }
29+ { i + 1 } Item
2830 </ a >
2931 ) ) }
3032 </ div >
@@ -60,7 +62,7 @@ it('Moves the focus to the first menu item after pressing enter while focused on
6062 skipClick : true ,
6163 } ) ;
6264
63- expect ( screen . getByText ( 'Item 1 ' ) ) . toHaveFocus ( ) ;
65+ expect ( screen . getByText ( '1 Item ' ) ) . toHaveFocus ( ) ;
6466} ) ;
6567
6668it ( 'Moves the focus to the first menu item after pressing space while focused on the menu button' , ( ) => {
@@ -74,7 +76,7 @@ it('Moves the focus to the first menu item after pressing space while focused on
7476 skipClick : true ,
7577 } ) ;
7678
77- expect ( screen . getByText ( 'Item 1 ' ) ) . toHaveFocus ( ) ;
79+ expect ( screen . getByText ( '1 Item ' ) ) . toHaveFocus ( ) ;
7880} ) ;
7981
8082it ( 'Moves the focus to the first menu item after clicking the menu to open it, then pressing tab while focused on the menu button' , ( ) => {
@@ -86,7 +88,7 @@ it('Moves the focus to the first menu item after clicking the menu to open it, t
8688
8789 userEvent . tab ( ) ;
8890
89- expect ( screen . getByText ( 'Item 1 ' ) ) . toHaveFocus ( ) ;
91+ expect ( screen . getByText ( '1 Item ' ) ) . toHaveFocus ( ) ;
9092} ) ;
9193
9294it ( 'Moves the focus to the first menu item after clicking the menu to open it, then pressing arrow down while focused on the menu button' , ( ) => {
@@ -105,7 +107,7 @@ it('Moves the focus to the first menu item after clicking the menu to open it, t
105107 } )
106108 ) ;
107109
108- expect ( screen . getByText ( 'Item 1 ' ) ) . toHaveFocus ( ) ;
110+ expect ( screen . getByText ( '1 Item ' ) ) . toHaveFocus ( ) ;
109111} ) ;
110112
111113it ( 'Sets isOpen to true after pressing enter while focused on the menu button' , ( ) => {
@@ -144,7 +146,7 @@ it('Sets isOpen to false after clicking a menu item that calls the state change
144146 render ( < TestComponent /> ) ;
145147
146148 userEvent . click ( screen . getByText ( 'Primary' ) ) ;
147- userEvent . click ( screen . getByText ( 'Item 1 ' ) ) ;
149+ userEvent . click ( screen . getByText ( '2 Item ' ) ) ;
148150
149151 expect ( screen . getByTestId ( 'is-open-indicator' ) ) . toHaveTextContent ( 'false' ) ;
150152} ) ;
@@ -158,18 +160,18 @@ it('Moves the focus to the next element in the menu after pressing the down arro
158160 skipClick : true ,
159161 } ) ;
160162
161- expect ( screen . getByText ( 'Item 1 ' ) ) . toHaveFocus ( ) ;
163+ expect ( screen . getByText ( '1 Item ' ) ) . toHaveFocus ( ) ;
162164
163165 fireEvent (
164- screen . getByText ( 'Item 1 ' ) ,
166+ screen . getByText ( '1 Item ' ) ,
165167 new KeyboardEvent ( 'keydown' , {
166168 key : 'ArrowDown' ,
167169 bubbles : true ,
168170 cancelable : true ,
169171 } )
170172 ) ;
171173
172- expect ( screen . getByText ( 'Item 2 ' ) ) . toHaveFocus ( ) ;
174+ expect ( screen . getByText ( '2 Item ' ) ) . toHaveFocus ( ) ;
173175} ) ;
174176
175177it ( 'Moves the focus to the previous element in the menu after pressing the up arrow' , ( ) => {
@@ -181,29 +183,29 @@ it('Moves the focus to the previous element in the menu after pressing the up ar
181183 skipClick : true ,
182184 } ) ;
183185
184- expect ( screen . getByText ( 'Item 1 ' ) ) . toHaveFocus ( ) ;
186+ expect ( screen . getByText ( '1 Item ' ) ) . toHaveFocus ( ) ;
185187
186188 fireEvent (
187- screen . getByText ( 'Item 1 ' ) ,
189+ screen . getByText ( '1 Item ' ) ,
188190 new KeyboardEvent ( 'keydown' , {
189191 key : 'ArrowDown' ,
190192 bubbles : true ,
191193 cancelable : true ,
192194 } )
193195 ) ;
194196
195- expect ( screen . getByText ( 'Item 2 ' ) ) . toHaveFocus ( ) ;
197+ expect ( screen . getByText ( '2 Item ' ) ) . toHaveFocus ( ) ;
196198
197199 fireEvent (
198- screen . getByText ( 'Item 2 ' ) ,
200+ screen . getByText ( '2 Item ' ) ,
199201 new KeyboardEvent ( 'keydown' , {
200202 key : 'ArrowUp' ,
201203 bubbles : true ,
202204 cancelable : true ,
203205 } )
204206 ) ;
205207
206- expect ( screen . getByText ( 'Item 1 ' ) ) . toHaveFocus ( ) ;
208+ expect ( screen . getByText ( '1 Item ' ) ) . toHaveFocus ( ) ;
207209} ) ;
208210
209211it ( 'Wraps the focus to the last element when pressing the up arrow at the beginning of the menu' , ( ) => {
@@ -215,18 +217,18 @@ it('Wraps the focus to the last element when pressing the up arrow at the beginn
215217 skipClick : true ,
216218 } ) ;
217219
218- expect ( screen . getByText ( 'Item 1 ' ) ) . toHaveFocus ( ) ;
220+ expect ( screen . getByText ( '1 Item ' ) ) . toHaveFocus ( ) ;
219221
220222 fireEvent (
221- screen . getByText ( 'Item 1 ' ) ,
223+ screen . getByText ( '1 Item ' ) ,
222224 new KeyboardEvent ( 'keydown' , {
223225 key : 'ArrowUp' ,
224226 bubbles : true ,
225227 cancelable : true ,
226228 } )
227229 ) ;
228230
229- expect ( screen . getByText ( 'Item 3 ' ) ) . toHaveFocus ( ) ;
231+ expect ( screen . getByText ( '4 Item ' ) ) . toHaveFocus ( ) ;
230232} ) ;
231233
232234it ( 'Wraps the focus to the first element when pressing the down arrow at the end of the menu' , ( ) => {
@@ -238,29 +240,29 @@ it('Wraps the focus to the first element when pressing the down arrow at the end
238240 skipClick : true ,
239241 } ) ;
240242
241- expect ( screen . getByText ( 'Item 1 ' ) ) . toHaveFocus ( ) ;
243+ expect ( screen . getByText ( '1 Item ' ) ) . toHaveFocus ( ) ;
242244
243245 fireEvent (
244- screen . getByText ( 'Item 1 ' ) ,
246+ screen . getByText ( '1 Item ' ) ,
245247 new KeyboardEvent ( 'keydown' , {
246248 key : 'ArrowUp' ,
247249 bubbles : true ,
248250 cancelable : true ,
249251 } )
250252 ) ;
251253
252- expect ( screen . getByText ( 'Item 3 ' ) ) . toHaveFocus ( ) ;
254+ expect ( screen . getByText ( '4 Item ' ) ) . toHaveFocus ( ) ;
253255
254256 fireEvent (
255- screen . getByText ( 'Item 3 ' ) ,
257+ screen . getByText ( '4 Item ' ) ,
256258 new KeyboardEvent ( 'keydown' , {
257259 key : 'ArrowDown' ,
258260 bubbles : true ,
259261 cancelable : true ,
260262 } )
261263 ) ;
262264
263- expect ( screen . getByText ( 'Item 1 ' ) ) . toHaveFocus ( ) ;
265+ expect ( screen . getByText ( '1 Item ' ) ) . toHaveFocus ( ) ;
264266} ) ;
265267
266268it ( 'Sets isOpen to false after pressing escape while focused on a menu item' , ( ) => {
@@ -272,7 +274,7 @@ it('Sets isOpen to false after pressing escape while focused on a menu item', ()
272274 skipClick : true ,
273275 } ) ;
274276
275- userEvent . type ( screen . getByText ( 'Item 1 ' ) , '{esc}' , {
277+ userEvent . type ( screen . getByText ( '1 Item ' ) , '{esc}' , {
276278 skipClick : true ,
277279 } ) ;
278280
@@ -302,7 +304,7 @@ it('Moves the focus to the menu button after pressing escape while focused on a
302304 skipClick : true ,
303305 } ) ;
304306
305- userEvent . type ( screen . getByText ( 'Item 1 ' ) , '{esc}' , {
307+ userEvent . type ( screen . getByText ( '1 Item ' ) , '{esc}' , {
306308 skipClick : true ,
307309 } ) ;
308310
@@ -333,7 +335,7 @@ it('Adds properties to items added after mount', () => {
333335
334336 userEvent . click ( screen . getByText ( 'Add Item' ) ) ;
335337
336- expect ( screen . getByText ( 'Item 4 ' ) ) . toHaveAttribute ( 'role' , 'menuitem' ) ;
338+ expect ( screen . getByText ( '4 Item ' ) ) . toHaveAttribute ( 'role' , 'menuitem' ) ;
337339} ) ;
338340
339341it ( 'Can navigate to a dynamically-added item' , ( ) => {
@@ -353,7 +355,16 @@ it('Can navigate to a dynamically-added item', () => {
353355 ) ;
354356
355357 fireEvent (
356- screen . getByText ( 'Item 1' ) ,
358+ screen . getByText ( '1 Item' ) ,
359+ new KeyboardEvent ( 'keydown' , {
360+ key : 'ArrowDown' ,
361+ bubbles : true ,
362+ cancelable : true ,
363+ } )
364+ ) ;
365+
366+ fireEvent (
367+ screen . getByText ( '2 Item' ) ,
357368 new KeyboardEvent ( 'keydown' , {
358369 key : 'ArrowDown' ,
359370 bubbles : true ,
@@ -362,7 +373,7 @@ it('Can navigate to a dynamically-added item', () => {
362373 ) ;
363374
364375 fireEvent (
365- screen . getByText ( 'Item 2 ' ) ,
376+ screen . getByText ( '3 Item ' ) ,
366377 new KeyboardEvent ( 'keydown' , {
367378 key : 'ArrowDown' ,
368379 bubbles : true ,
@@ -371,15 +382,15 @@ it('Can navigate to a dynamically-added item', () => {
371382 ) ;
372383
373384 fireEvent (
374- screen . getByText ( 'Item 3 ' ) ,
385+ screen . getByText ( '4 Item ' ) ,
375386 new KeyboardEvent ( 'keydown' , {
376387 key : 'ArrowDown' ,
377388 bubbles : true ,
378389 cancelable : true ,
379390 } )
380391 ) ;
381392
382- expect ( screen . getByText ( 'Item 4 ' ) ) . toHaveFocus ( ) ;
393+ expect ( screen . getByText ( '5 Item ' ) ) . toHaveFocus ( ) ;
383394} ) ;
384395
385396it ( 'Ignores keys that buttons don’t need to handle' , ( ) => {
@@ -401,9 +412,11 @@ it('Ignores keys that items don’t need to handle', () => {
401412 skipClick : true ,
402413 } ) ;
403414
404- userEvent . type ( screen . getByText ( 'Item 1 ' ) , 'Z' , {
415+ userEvent . type ( screen . getByText ( '1 Item ' ) , 'Z' , {
405416 skipClick : true ,
406417 } ) ;
418+
419+ expect ( screen . getByText ( '1 Item' ) ) . toHaveFocus ( ) ;
407420} ) ;
408421
409422it ( 'Doesn’t crash when enter press occurs on a menu item' , ( ) => {
@@ -415,7 +428,91 @@ it('Doesn’t crash when enter press occurs on a menu item', () => {
415428 skipClick : true ,
416429 } ) ;
417430
418- userEvent . type ( screen . getByText ( 'Item 1' ) , '{enter}' , {
431+ userEvent . type ( screen . getByText ( '1 Item' ) , '{enter}' , {
432+ skipClick : true ,
433+ } ) ;
434+ } ) ;
435+
436+ it ( 'Closes the menu after pressing enter on a menu item with a click handler' , ( ) => {
437+ render ( < TestComponent /> ) ;
438+
439+ userEvent . tab ( ) ;
440+
441+ userEvent . type ( screen . getByText ( 'Primary' ) , '{enter}' , {
442+ skipClick : true ,
443+ } ) ;
444+
445+ userEvent . type ( screen . getByText ( '1 Item' ) , '{enter}' , {
446+ skipClick : true ,
447+ } ) ;
448+
449+ expect ( screen . getByTestId ( 'is-open-indicator' ) ) . toHaveTextContent ( 'false' ) ;
450+ } ) ;
451+
452+ it ( 'Activates the click handler of a menu item after pressing enter while focused on it' , ( ) => {
453+ render ( < TestComponent /> ) ;
454+
455+ jest . spyOn ( console , 'log' ) ;
456+
457+ userEvent . tab ( ) ;
458+
459+ userEvent . type ( screen . getByText ( 'Primary' ) , '{enter}' , {
460+ skipClick : true ,
461+ } ) ;
462+
463+ userEvent . type ( screen . getByText ( '1 Item' ) , '{enter}' , {
419464 skipClick : true ,
420465 } ) ;
466+
467+ expect ( console . log ) . toHaveBeenCalledWith ( 'Item one clicked' ) ;
468+ } ) ;
469+
470+ it ( 'Closes the menu after pressing space on a menu item with a click handler' , ( ) => {
471+ render ( < TestComponent /> ) ;
472+
473+ userEvent . tab ( ) ;
474+
475+ userEvent . type ( screen . getByText ( 'Primary' ) , '{enter}' , {
476+ skipClick : true ,
477+ } ) ;
478+
479+ userEvent . type ( screen . getByText ( '1 Item' ) , '{space}' , {
480+ skipClick : true ,
481+ } ) ;
482+
483+ expect ( screen . getByTestId ( 'is-open-indicator' ) ) . toHaveTextContent ( 'false' ) ;
484+ } ) ;
485+
486+ it ( 'Activates the click handler of a menu item after pressing space while focused on it' , ( ) => {
487+ render ( < TestComponent /> ) ;
488+
489+ jest . spyOn ( console , 'log' ) ;
490+
491+ userEvent . tab ( ) ;
492+
493+ userEvent . type ( screen . getByText ( 'Primary' ) , '{enter}' , {
494+ skipClick : true ,
495+ } ) ;
496+
497+ userEvent . type ( screen . getByText ( '1 Item' ) , '{space}' , {
498+ skipClick : true ,
499+ } ) ;
500+
501+ expect ( console . log ) . toHaveBeenCalledWith ( 'Item one clicked' ) ;
502+ } ) ;
503+
504+ it ( 'Moves the focus to the menu item with a label that starts with the corresponding character that was pressed' , ( ) => {
505+ render ( < TestComponent /> ) ;
506+
507+ userEvent . tab ( ) ;
508+
509+ userEvent . type ( screen . getByText ( 'Primary' ) , '{enter}' , {
510+ skipClick : true ,
511+ } ) ;
512+
513+ userEvent . type ( screen . getByText ( '1 Item' ) , '3' , {
514+ skipClick : true ,
515+ } ) ;
516+
517+ expect ( screen . getByText ( '3 Item' ) ) . toHaveFocus ( ) ;
421518} ) ;
0 commit comments