@@ -14,7 +14,7 @@ import {createKeyboardEvent} from '../../testing/private';
1414import { QueryList } from '@angular/core' ;
1515import { take } from 'rxjs/operators' ;
1616import { TreeKeyManager , TreeKeyManagerItem } from './tree-key-manager' ;
17- import { Observable , of as observableOf } from 'rxjs' ;
17+ import { Observable , of as observableOf , Subscription } from 'rxjs' ;
1818
1919class FakeBaseTreeKeyManagerItem {
2020 _isExpanded = false ;
@@ -66,6 +66,12 @@ interface ItemConstructorTestContext {
6666 | FakeObservableTreeKeyManagerItem ;
6767}
6868
69+ interface ExpandCollapseKeyEventTestContext {
70+ direction : 'ltr' | 'rtl' ;
71+ expandKeyEvent : ( ) => KeyboardEvent ;
72+ collapseKeyEvent : ( ) => KeyboardEvent ;
73+ }
74+
6975describe ( 'TreeKeyManager' , ( ) => {
7076 let fakeKeyEvents : {
7177 downArrow : KeyboardEvent ;
@@ -430,6 +436,298 @@ describe('TreeKeyManager', () => {
430436 expect ( fakeKeyEvents . upArrow . defaultPrevented ) . toBe ( true ) ;
431437 } ) ;
432438 } ) ;
439+
440+ describe ( 'expand/collapse key events' , ( ) => {
441+ const parameters : ExpandCollapseKeyEventTestContext [ ] = [
442+ {
443+ direction : 'ltr' ,
444+ expandKeyEvent : ( ) => fakeKeyEvents . rightArrow ,
445+ collapseKeyEvent : ( ) => fakeKeyEvents . leftArrow ,
446+ } ,
447+ {
448+ direction : 'rtl' ,
449+ expandKeyEvent : ( ) => fakeKeyEvents . leftArrow ,
450+ collapseKeyEvent : ( ) => fakeKeyEvents . rightArrow ,
451+ } ,
452+ ] ;
453+
454+ for ( const param of parameters ) {
455+ describe ( `in ${ param . direction } mode` , ( ) => {
456+ beforeEach ( ( ) => {
457+ keyManager = new TreeKeyManager ( {
458+ items : itemList ,
459+ horizontalOrientation : param . direction ,
460+ } ) ;
461+ for ( const item of itemList ) {
462+ item . _isExpanded = false ;
463+ }
464+ } ) ;
465+
466+ it ( 'with nothing active, expand key does not expand any items' , ( ) => {
467+ expect ( itemList . toArray ( ) . map ( item => item . isExpanded ( ) ) )
468+ . withContext ( 'item expansion state, for all items' )
469+ . toEqual ( itemList . toArray ( ) . map ( _ => false ) ) ;
470+
471+ keyManager . onKeydown ( param . expandKeyEvent ( ) ) ;
472+
473+ expect ( itemList . toArray ( ) . map ( item => item . isExpanded ( ) ) )
474+ . withContext ( 'item expansion state, for all items, after expand event' )
475+ . toEqual ( itemList . toArray ( ) . map ( _ => false ) ) ;
476+ } ) ;
477+
478+ it ( 'with nothing active, collapse key does not collapse any items' , ( ) => {
479+ for ( const item of itemList ) {
480+ item . _isExpanded = true ;
481+ }
482+ expect ( itemList . toArray ( ) . map ( item => item . isExpanded ( ) ) )
483+ . withContext ( 'item expansion state, for all items' )
484+ . toEqual ( itemList . toArray ( ) . map ( _ => true ) ) ;
485+
486+ keyManager . onKeydown ( param . collapseKeyEvent ( ) ) ;
487+
488+ expect ( itemList . toArray ( ) . map ( item => item . isExpanded ( ) ) )
489+ . withContext ( 'item expansion state, for all items' )
490+ . toEqual ( itemList . toArray ( ) . map ( _ => true ) ) ;
491+ } ) ;
492+
493+ it ( 'with nothing active, expand key does not change the active item index' , ( ) => {
494+ expect ( keyManager . getActiveItemIndex ( ) )
495+ . withContext ( 'active item index, initial' )
496+ . toEqual ( - 1 ) ;
497+
498+ keyManager . onKeydown ( param . expandKeyEvent ( ) ) ;
499+
500+ expect ( keyManager . getActiveItemIndex ( ) )
501+ . withContext ( 'active item index, after expand event' )
502+ . toEqual ( - 1 ) ;
503+ } ) ;
504+
505+ it ( 'with nothing active, collapse key does not change the active item index' , ( ) => {
506+ for ( const item of itemList ) {
507+ item . _isExpanded = true ;
508+ }
509+
510+ expect ( keyManager . getActiveItemIndex ( ) )
511+ . withContext ( 'active item index, initial' )
512+ . toEqual ( - 1 ) ;
513+
514+ keyManager . onKeydown ( param . collapseKeyEvent ( ) ) ;
515+
516+ expect ( keyManager . getActiveItemIndex ( ) )
517+ . withContext ( 'active item index, after collapse event' )
518+ . toEqual ( - 1 ) ;
519+ } ) ;
520+
521+ describe ( 'if the current item is expanded' , ( ) => {
522+ let spy : jasmine . Spy ;
523+ let subscription : Subscription ;
524+
525+ beforeEach ( ( ) => {
526+ keyManager . onClick ( parentItem ) ;
527+ parentItem . _isExpanded = true ;
528+
529+ spy = jasmine . createSpy ( 'change spy' ) ;
530+ subscription = keyManager . change . subscribe ( spy ) ;
531+ } ) ;
532+
533+ afterEach ( ( ) => {
534+ subscription . unsubscribe ( ) ;
535+ } ) ;
536+
537+ it ( 'when the expand key is pressed, moves to the first child' , ( ) => {
538+ keyManager . onKeydown ( param . expandKeyEvent ( ) ) ;
539+
540+ expect ( keyManager . getActiveItemIndex ( ) )
541+ . withContext ( 'active item index, after one expand key event.' )
542+ . toBe ( 1 ) ;
543+ expect ( spy ) . not . toHaveBeenCalledWith ( parentItem ) ;
544+ expect ( spy ) . toHaveBeenCalledWith ( childItem ) ;
545+ } ) ;
546+
547+ it (
548+ 'when the expand key is pressed, and the first child is disabled, ' +
549+ 'moves to the first non-disabled child' ,
550+ ( ) => {
551+ childItem . isDisabled = true ;
552+
553+ keyManager . onKeydown ( param . expandKeyEvent ( ) ) ;
554+
555+ expect ( keyManager . getActiveItemIndex ( ) )
556+ . withContext ( 'active item index, after one expand key event.' )
557+ . toBe ( 3 ) ;
558+ expect ( spy ) . not . toHaveBeenCalledWith ( parentItem ) ;
559+ expect ( spy ) . not . toHaveBeenCalledWith ( childItem ) ;
560+ expect ( spy ) . toHaveBeenCalledWith ( childItemWithNoChildren ) ;
561+ } ,
562+ ) ;
563+
564+ it (
565+ 'when the expand key is pressed, and all children are disabled, ' +
566+ 'does not change the active item' ,
567+ ( ) => {
568+ childItem . isDisabled = true ;
569+ childItemWithNoChildren . isDisabled = true ;
570+
571+ keyManager . onKeydown ( param . expandKeyEvent ( ) ) ;
572+
573+ expect ( keyManager . getActiveItemIndex ( ) )
574+ . withContext ( 'active item index, after one expand key event.' )
575+ . toBe ( 0 ) ;
576+ expect ( spy ) . not . toHaveBeenCalled ( ) ;
577+ } ,
578+ ) ;
579+
580+ it ( 'when the collapse key is pressed, collapses the item' , ( ) => {
581+ expect ( parentItem . isExpanded ( ) )
582+ . withContext ( 'active item initial expansion state' )
583+ . toBe ( true ) ;
584+
585+ keyManager . onKeydown ( param . collapseKeyEvent ( ) ) ;
586+
587+ expect ( parentItem . isExpanded ( ) )
588+ . withContext ( 'active item expansion state, after collapse key' )
589+ . toBe ( false ) ;
590+ } ) ;
591+
592+ it ( 'when the collapse key is pressed, does not change the active item' , ( ) => {
593+ expect ( keyManager . getActiveItemIndex ( ) )
594+ . withContext ( 'active item index, initial' )
595+ . toBe ( 0 ) ;
596+
597+ keyManager . onKeydown ( param . collapseKeyEvent ( ) ) ;
598+
599+ expect ( keyManager . getActiveItemIndex ( ) )
600+ . withContext ( 'active item index, after one collapse key event.' )
601+ . toBe ( 0 ) ;
602+ expect ( spy ) . not . toHaveBeenCalled ( ) ;
603+ } ) ;
604+ } ) ;
605+
606+ describe ( 'if the current item is expanded, and there are no children' , ( ) => {
607+ let spy : jasmine . Spy ;
608+ let subscription : Subscription ;
609+
610+ beforeEach ( ( ) => {
611+ keyManager . onClick ( childItemWithNoChildren ) ;
612+ childItemWithNoChildren . _isExpanded = true ;
613+
614+ spy = jasmine . createSpy ( 'change spy' ) ;
615+ subscription = keyManager . change . subscribe ( spy ) ;
616+ } ) ;
617+
618+ afterEach ( ( ) => {
619+ subscription . unsubscribe ( ) ;
620+ } ) ;
621+
622+ it ( 'when the expand key is pressed, does not change the active item' , ( ) => {
623+ keyManager . onKeydown ( param . expandKeyEvent ( ) ) ;
624+
625+ expect ( keyManager . getActiveItemIndex ( ) )
626+ . withContext ( 'active item index, after one expand key event.' )
627+ . toBe ( 3 ) ;
628+ expect ( spy ) . not . toHaveBeenCalled ( ) ;
629+ } ) ;
630+ } ) ;
631+
632+ describe ( 'if the current item is collapsed, and has a parent item' , ( ) => {
633+ let spy : jasmine . Spy ;
634+ let subscription : Subscription ;
635+
636+ beforeEach ( ( ) => {
637+ keyManager . onClick ( childItem ) ;
638+ childItem . _isExpanded = false ;
639+
640+ spy = jasmine . createSpy ( 'change spy' ) ;
641+ subscription = keyManager . change . subscribe ( spy ) ;
642+ } ) ;
643+
644+ afterEach ( ( ) => {
645+ subscription . unsubscribe ( ) ;
646+ } ) ;
647+
648+ it ( 'when the expand key is pressed, expands the current item' , ( ) => {
649+ expect ( childItem . isExpanded ( ) )
650+ . withContext ( 'active item initial expansion state' )
651+ . toBe ( false ) ;
652+
653+ keyManager . onKeydown ( param . expandKeyEvent ( ) ) ;
654+
655+ expect ( childItem . isExpanded ( ) )
656+ . withContext ( 'active item expansion state, after expand key' )
657+ . toBe ( true ) ;
658+ } ) ;
659+
660+ it ( 'when the expand key is pressed, does not change active item' , ( ) => {
661+ expect ( keyManager . getActiveItemIndex ( ) )
662+ . withContext ( 'active item index, initial' )
663+ . toBe ( 1 ) ;
664+
665+ keyManager . onKeydown ( param . expandKeyEvent ( ) ) ;
666+
667+ expect ( keyManager . getActiveItemIndex ( ) )
668+ . withContext ( 'active item index, after one collapse key event.' )
669+ . toBe ( 1 ) ;
670+ expect ( spy ) . not . toHaveBeenCalled ( ) ;
671+ } ) ;
672+
673+ it ( 'when the collapse key is pressed, moves the active item to the parent' , ( ) => {
674+ expect ( keyManager . getActiveItemIndex ( ) )
675+ . withContext ( 'active item index, initial' )
676+ . toBe ( 1 ) ;
677+
678+ keyManager . onKeydown ( param . collapseKeyEvent ( ) ) ;
679+
680+ expect ( keyManager . getActiveItemIndex ( ) )
681+ . withContext ( 'active item index, after one collapse key event.' )
682+ . toBe ( 0 ) ;
683+ } ) ;
684+
685+ it ( 'when the collapse key is pressed, and the parent is disabled, does nothing' , ( ) => {
686+ expect ( keyManager . getActiveItemIndex ( ) )
687+ . withContext ( 'active item index, initial' )
688+ . toBe ( 1 ) ;
689+
690+ parentItem . isDisabled = true ;
691+ keyManager . onKeydown ( param . collapseKeyEvent ( ) ) ;
692+
693+ expect ( keyManager . getActiveItemIndex ( ) )
694+ . withContext ( 'active item index, after one collapse key event.' )
695+ . toBe ( 1 ) ;
696+ } ) ;
697+ } ) ;
698+
699+ describe ( 'if the current item is collapsed, and has no parent items' , ( ) => {
700+ let spy : jasmine . Spy ;
701+ let subscription : Subscription ;
702+
703+ beforeEach ( ( ) => {
704+ keyManager . onClick ( parentItem ) ;
705+ parentItem . _isExpanded = false ;
706+
707+ spy = jasmine . createSpy ( 'change spy' ) ;
708+ subscription = keyManager . change . subscribe ( spy ) ;
709+ } ) ;
710+
711+ afterEach ( ( ) => {
712+ subscription . unsubscribe ( ) ;
713+ } ) ;
714+
715+ it ( 'when the collapse key is pressed, does nothing' , ( ) => {
716+ expect ( keyManager . getActiveItemIndex ( ) )
717+ . withContext ( 'active item index, initial' )
718+ . toBe ( 0 ) ;
719+
720+ keyManager . onKeydown ( param . collapseKeyEvent ( ) ) ;
721+
722+ expect ( keyManager . getActiveItemIndex ( ) )
723+ . withContext ( 'active item index, after one collapse key event.' )
724+ . toBe ( 0 ) ;
725+ expect ( spy ) . not . toHaveBeenCalledWith ( parentItem ) ;
726+ } ) ;
727+ } ) ;
728+ } ) ;
729+ }
730+ } ) ;
433731 } ) ;
434732 }
435733} ) ;
0 commit comments