11/* eslint-disable no-undef */
22import { act , fireEvent , render } from '@testing-library/react' ;
33import { spyElementPrototypes } from '@rc-component/util/lib/test/domHook' ;
4+ import KeyCode from '@rc-component/util/lib/KeyCode' ;
45import React from 'react' ;
56import Menu , { MenuItem , MenuItemGroup , MenuRef , SubMenu } from '../src' ;
7+ import { isActive } from './util' ;
68
79describe ( 'Focus' , ( ) => {
810 beforeAll ( ( ) => {
@@ -26,6 +28,24 @@ describe('Focus', () => {
2628 jest . useRealTimers ( ) ;
2729 } ) ;
2830
31+ function keyDown ( container : HTMLElement , keyCode : number ) {
32+ fireEvent . keyDown ( container . querySelector ( 'ul.rc-menu-root' ) , {
33+ which : keyCode ,
34+ keyCode,
35+ charCode : keyCode ,
36+ } ) ;
37+
38+ // SubMenu raf need slow than accessibility
39+ for ( let i = 0 ; i < 20 ; i += 1 ) {
40+ act ( ( ) => {
41+ jest . advanceTimersByTime ( 10 ) ;
42+ } ) ;
43+ }
44+ act ( ( ) => {
45+ jest . runAllTimers ( ) ;
46+ } ) ;
47+ }
48+
2949 it ( 'Get focus' , async ( ) => {
3050 const { container } = await act ( async ( ) =>
3151 render (
@@ -186,5 +206,98 @@ describe('Focus', () => {
186206 expect ( document . activeElement ) . toBe ( getByTitle ( 'Submenu' ) ) ;
187207 expect ( getByTestId ( 'sub-menu' ) ) . toHaveClass ( 'rc-menu-submenu-active' ) ;
188208 } ) ;
209+
210+ it ( 'When selectable is not configured, the focus should move to the first available item instead of keeping the previously focused item' , async ( ) => {
211+ const menuRef = React . createRef < MenuRef > ( ) ;
212+ const items = [
213+ { key : '0' , label : 'First Item' } ,
214+ { key : '1' , label : 'Second Item' } ,
215+ { key : '2' , label : 'Third Item' } ,
216+ ] ;
217+ const TestApp = ( ) => {
218+ return (
219+ < div >
220+ < Menu data-testid = "menu" selectable = { false } ref = { menuRef } >
221+ { items . map ( item => (
222+ < MenuItem key = { item . key } data-testid = { item . key } >
223+ { item . label }
224+ </ MenuItem >
225+ ) ) }
226+ </ Menu >
227+ </ div >
228+ ) ;
229+ } ;
230+ const { getByTestId, container } = render ( < TestApp /> ) ;
231+ // ================ check keydown ==============
232+ // first item
233+ keyDown ( container , KeyCode . DOWN ) ;
234+ isActive ( container , 0 ) ;
235+ // second item
236+ keyDown ( container , KeyCode . DOWN ) ;
237+ isActive ( container , 1 ) ;
238+ // select second item
239+ keyDown ( container , KeyCode . ENTER ) ;
240+
241+ // mock focus on item 0 to make sure it gets focused
242+ const item0 = getByTestId ( '0' ) ;
243+ const focusSpy = jest . spyOn ( item0 , 'focus' ) . mockImplementation ( ( ) => { } ) ;
244+ menuRef . current . focus ( ) ;
245+ expect ( focusSpy ) . toHaveBeenCalled ( ) ;
246+
247+ // ================ check click ==============
248+ // click third item
249+ const item2 = getByTestId ( '2' ) ;
250+ fireEvent . click ( item2 ) ;
251+ menuRef . current . focus ( ) ;
252+ expect ( focusSpy ) . toHaveBeenCalled ( ) ;
253+ // cleanup
254+ focusSpy . mockRestore ( ) ;
255+ } ) ;
256+ it ( 'When selectable is configured, the focus should move to the selected item if there is a selection, else to the first item, not retain on last focused item' , async ( ) => {
257+ const menuRef = React . createRef < MenuRef > ( ) ;
258+ const items = [
259+ { key : '0' , label : 'First Item' } ,
260+ { key : '1' , label : 'Second Item' } ,
261+ { key : '2' , label : 'Third Item' } ,
262+ ] ;
263+ const TestApp = ( ) => {
264+ return (
265+ < div >
266+ < Menu data-testid = "menu" selectable ref = { menuRef } >
267+ { items . map ( item => (
268+ < MenuItem key = { item . key } data-testid = { item . key } >
269+ { item . label }
270+ </ MenuItem >
271+ ) ) }
272+ </ Menu >
273+ </ div >
274+ ) ;
275+ } ;
276+ const { getByTestId, container } = render ( < TestApp /> ) ;
277+ // ================ check keydown ==============
278+ // first item
279+ keyDown ( container , KeyCode . DOWN ) ;
280+ isActive ( container , 0 ) ;
281+ // second item
282+ keyDown ( container , KeyCode . DOWN ) ;
283+ isActive ( container , 1 ) ;
284+ // select second item
285+ keyDown ( container , KeyCode . ENTER ) ;
286+ // mock focus on item 1 to make sure it gets focused
287+ const item1 = getByTestId ( '1' ) ;
288+ const focusSpy = jest . spyOn ( item1 , 'focus' ) . mockImplementation ( ( ) => { } ) ;
289+ menuRef . current . focus ( ) ;
290+ expect ( focusSpy ) . toHaveBeenCalled ( ) ;
291+
292+ // ================ check click ==============
293+ // click third item
294+ const item2 = getByTestId ( '2' ) ;
295+ fireEvent . click ( item2 ) ;
296+ menuRef . current . focus ( ) ;
297+ // mock focus on item 2 to make sure it gets focused
298+ expect ( focusSpy ) . toHaveBeenCalled ( ) ;
299+ // cleanup
300+ focusSpy . mockRestore ( ) ;
301+ } ) ;
189302} ) ;
190303/* eslint-enable */
0 commit comments