@@ -9,8 +9,10 @@ import {first} from '../rxjs/index';
99
1010
1111class FakeFocusable {
12+ constructor ( private _label = '' ) { }
1213 disabled = false ;
1314 focus ( ) { }
15+ getLabel ( ) { return this . _label ; }
1416}
1517
1618class FakeHighlightable {
@@ -20,11 +22,11 @@ class FakeHighlightable {
2022}
2123
2224class FakeQueryList < T > extends QueryList < T > {
23- get length ( ) { return this . items . length ; }
2425 items : T [ ] ;
25- toArray ( ) {
26- return this . items ;
27- }
26+ get length ( ) { return this . items . length ; }
27+ get first ( ) { return this . items [ 0 ] ; }
28+ toArray ( ) { return this . items ; }
29+ some ( ) { return this . items . some . apply ( this . items , arguments ) ; }
2830}
2931
3032
@@ -43,7 +45,7 @@ describe('Key managers', () => {
4345 downArrow : createKeyboardEvent ( 'keydown' , DOWN_ARROW ) ,
4446 upArrow : createKeyboardEvent ( 'keydown' , UP_ARROW ) ,
4547 tab : createKeyboardEvent ( 'keydown' , TAB ) ,
46- unsupported : createKeyboardEvent ( 'keydown' , 65 ) // corresponds to the letter "a"
48+ unsupported : createKeyboardEvent ( 'keydown' , 192 ) // corresponds to the tilde character (~)
4749 } ;
4850 } ) ;
4951
@@ -52,7 +54,11 @@ describe('Key managers', () => {
5254 let keyManager : ListKeyManager < FakeFocusable > ;
5355
5456 beforeEach ( ( ) => {
55- itemList . items = [ new FakeFocusable ( ) , new FakeFocusable ( ) , new FakeFocusable ( ) ] ;
57+ itemList . items = [
58+ new FakeFocusable ( 'one' ) ,
59+ new FakeFocusable ( 'two' ) ,
60+ new FakeFocusable ( 'three' )
61+ ] ;
5662 keyManager = new ListKeyManager < FakeFocusable > ( itemList ) ;
5763
5864 // first item is already focused
@@ -383,6 +389,65 @@ describe('Key managers', () => {
383389
384390 } ) ;
385391
392+ describe ( 'typeahead mode' , ( ) => {
393+ const debounceInterval = 300 ;
394+
395+ beforeEach ( ( ) => {
396+ keyManager . withTypeAhead ( debounceInterval ) ;
397+ keyManager . setActiveItem ( - 1 ) ;
398+ } ) ;
399+
400+ it ( 'should throw if the items do not implement the getLabel method' , ( ) => {
401+ const invalidQueryList = new FakeQueryList ( ) ;
402+
403+ invalidQueryList . items = [ { disabled : false } ] ;
404+
405+ const invalidManager = new ListKeyManager ( invalidQueryList ) ;
406+
407+ expect ( ( ) => invalidManager . withTypeAhead ( ) ) . toThrowError ( / m u s t i m p l e m e n t / ) ;
408+ } ) ;
409+
410+ it ( 'should debounce the input key presses' , fakeAsync ( ( ) => {
411+ keyManager . onKeydown ( createKeyboardEvent ( 'keydown' , 79 ) ) ; // types "o"
412+ keyManager . onKeydown ( createKeyboardEvent ( 'keydown' , 78 ) ) ; // types "n"
413+ keyManager . onKeydown ( createKeyboardEvent ( 'keydown' , 69 ) ) ; // types "e"
414+
415+ expect ( keyManager . activeItem ) . not . toBe ( itemList . items [ 0 ] ) ;
416+
417+ tick ( debounceInterval ) ;
418+
419+ expect ( keyManager . activeItem ) . toBe ( itemList . items [ 0 ] ) ;
420+ } ) ) ;
421+
422+ it ( 'should focus the first item that starts with a letter' , fakeAsync ( ( ) => {
423+ keyManager . onKeydown ( createKeyboardEvent ( 'keydown' , 84 ) ) ; // types "t"
424+
425+ tick ( debounceInterval ) ;
426+
427+ expect ( keyManager . activeItem ) . toBe ( itemList . items [ 1 ] ) ;
428+ } ) ) ;
429+
430+ it ( 'should focus the first item that starts with sequence of letters' , fakeAsync ( ( ) => {
431+ keyManager . onKeydown ( createKeyboardEvent ( 'keydown' , 84 ) ) ; // types "t"
432+ keyManager . onKeydown ( createKeyboardEvent ( 'keydown' , 72 ) ) ; // types "h"
433+
434+ tick ( debounceInterval ) ;
435+
436+ expect ( keyManager . activeItem ) . toBe ( itemList . items [ 2 ] ) ;
437+ } ) ) ;
438+
439+ it ( 'should cancel any pending timers if a navigation key is pressed' , fakeAsync ( ( ) => {
440+ keyManager . onKeydown ( createKeyboardEvent ( 'keydown' , 84 ) ) ; // types "t"
441+ keyManager . onKeydown ( createKeyboardEvent ( 'keydown' , 72 ) ) ; // types "h"
442+ keyManager . onKeydown ( fakeKeyEvents . downArrow ) ;
443+
444+ tick ( debounceInterval ) ;
445+
446+ expect ( keyManager . activeItem ) . toBe ( itemList . items [ 0 ] ) ;
447+ } ) ) ;
448+
449+ } ) ;
450+
386451 } ) ;
387452
388453 describe ( 'FocusKeyManager' , ( ) => {
@@ -400,40 +465,40 @@ describe('Key managers', () => {
400465 spyOn ( itemList . items [ 2 ] , 'focus' ) ;
401466 } ) ;
402467
403- it ( 'should focus subsequent items when down arrow is pressed' , ( ) => {
404- keyManager . onKeydown ( fakeKeyEvents . downArrow ) ;
468+ it ( 'should focus subsequent items when down arrow is pressed' , ( ) => {
469+ keyManager . onKeydown ( fakeKeyEvents . downArrow ) ;
405470
406- expect ( itemList . items [ 0 ] . focus ) . not . toHaveBeenCalled ( ) ;
407- expect ( itemList . items [ 1 ] . focus ) . toHaveBeenCalledTimes ( 1 ) ;
408- expect ( itemList . items [ 2 ] . focus ) . not . toHaveBeenCalled ( ) ;
471+ expect ( itemList . items [ 0 ] . focus ) . not . toHaveBeenCalled ( ) ;
472+ expect ( itemList . items [ 1 ] . focus ) . toHaveBeenCalledTimes ( 1 ) ;
473+ expect ( itemList . items [ 2 ] . focus ) . not . toHaveBeenCalled ( ) ;
409474
410- keyManager . onKeydown ( fakeKeyEvents . downArrow ) ;
411- expect ( itemList . items [ 0 ] . focus ) . not . toHaveBeenCalled ( ) ;
412- expect ( itemList . items [ 1 ] . focus ) . toHaveBeenCalledTimes ( 1 ) ;
413- expect ( itemList . items [ 2 ] . focus ) . toHaveBeenCalledTimes ( 1 ) ;
414- } ) ;
475+ keyManager . onKeydown ( fakeKeyEvents . downArrow ) ;
476+ expect ( itemList . items [ 0 ] . focus ) . not . toHaveBeenCalled ( ) ;
477+ expect ( itemList . items [ 1 ] . focus ) . toHaveBeenCalledTimes ( 1 ) ;
478+ expect ( itemList . items [ 2 ] . focus ) . toHaveBeenCalledTimes ( 1 ) ;
479+ } ) ;
415480
416- it ( 'should focus previous items when up arrow is pressed' , ( ) => {
417- keyManager . onKeydown ( fakeKeyEvents . downArrow ) ;
481+ it ( 'should focus previous items when up arrow is pressed' , ( ) => {
482+ keyManager . onKeydown ( fakeKeyEvents . downArrow ) ;
418483
419- expect ( itemList . items [ 0 ] . focus ) . not . toHaveBeenCalled ( ) ;
420- expect ( itemList . items [ 1 ] . focus ) . toHaveBeenCalledTimes ( 1 ) ;
484+ expect ( itemList . items [ 0 ] . focus ) . not . toHaveBeenCalled ( ) ;
485+ expect ( itemList . items [ 1 ] . focus ) . toHaveBeenCalledTimes ( 1 ) ;
421486
422- keyManager . onKeydown ( fakeKeyEvents . upArrow ) ;
487+ keyManager . onKeydown ( fakeKeyEvents . upArrow ) ;
423488
424- expect ( itemList . items [ 0 ] . focus ) . toHaveBeenCalledTimes ( 1 ) ;
425- expect ( itemList . items [ 1 ] . focus ) . toHaveBeenCalledTimes ( 1 ) ;
426- } ) ;
489+ expect ( itemList . items [ 0 ] . focus ) . toHaveBeenCalledTimes ( 1 ) ;
490+ expect ( itemList . items [ 1 ] . focus ) . toHaveBeenCalledTimes ( 1 ) ;
491+ } ) ;
427492
428- it ( 'should allow setting the focused item without calling focus' , ( ) => {
429- expect ( keyManager . activeItemIndex )
430- . toBe ( 0 , `Expected first item of the list to be active.` ) ;
493+ it ( 'should allow setting the focused item without calling focus' , ( ) => {
494+ expect ( keyManager . activeItemIndex )
495+ . toBe ( 0 , `Expected first item of the list to be active.` ) ;
431496
432- keyManager . updateActiveItemIndex ( 1 ) ;
433- expect ( keyManager . activeItemIndex )
434- . toBe ( 1 , `Expected activeItemIndex to update after calling updateActiveItemIndex().` ) ;
435- expect ( itemList . items [ 1 ] . focus ) . not . toHaveBeenCalledTimes ( 1 ) ;
436- } ) ;
497+ keyManager . updateActiveItemIndex ( 1 ) ;
498+ expect ( keyManager . activeItemIndex )
499+ . toBe ( 1 , `Expected activeItemIndex to update after calling updateActiveItemIndex().` ) ;
500+ expect ( itemList . items [ 1 ] . focus ) . not . toHaveBeenCalledTimes ( 1 ) ;
501+ } ) ;
437502
438503 } ) ;
439504
0 commit comments