@@ -34,6 +34,8 @@ import {CdkMenuItemRadio} from './menu-item-radio';
34
34
import { CdkMenu } from './menu' ;
35
35
import { CdkMenuItem } from './menu-item' ;
36
36
import { CdkMenuItemCheckbox } from './menu-item-checkbox' ;
37
+ import { CdkMenuItemTrigger } from './menu-item-trigger' ;
38
+ import { CdkMenuGroup } from './menu-group' ;
37
39
38
40
describe ( 'MenuBar' , ( ) => {
39
41
describe ( 'as radio group' , ( ) => {
@@ -735,6 +737,106 @@ describe('MenuBar', () => {
735
737
) ;
736
738
} ) ;
737
739
} ) ;
740
+
741
+ describe ( 'background click closeout' , ( ) => {
742
+ let fixture : ComponentFixture < MenuBarWithMenus > ;
743
+
744
+ let menus : CdkMenu [ ] ;
745
+ let triggers : CdkMenuItemTrigger [ ] ;
746
+
747
+ /** open the attached menu. */
748
+ function openMenu ( ) {
749
+ triggers [ 0 ] . toggle ( ) ;
750
+ detectChanges ( ) ;
751
+ }
752
+
753
+ /** set the menus and triggers arrays. */
754
+ function grabElementsForTesting ( ) {
755
+ menus = fixture . componentInstance . menus . toArray ( ) ;
756
+ triggers = fixture . componentInstance . triggers . toArray ( ) ;
757
+ }
758
+
759
+ /** run change detection and, extract and set the rendered elements. */
760
+ function detectChanges ( ) {
761
+ fixture . detectChanges ( ) ;
762
+ grabElementsForTesting ( ) ;
763
+ }
764
+
765
+ beforeEach ( async ( ( ) => {
766
+ TestBed . configureTestingModule ( {
767
+ imports : [ CdkMenuModule ] ,
768
+ declarations : [ MenuBarWithMenus ] ,
769
+ } ) . compileComponents ( ) ;
770
+ } ) ) ;
771
+
772
+ beforeEach ( ( ) => {
773
+ fixture = TestBed . createComponent ( MenuBarWithMenus ) ;
774
+ detectChanges ( ) ;
775
+ } ) ;
776
+
777
+ it ( 'should close out all open menus when clicked outside the menu tree' , ( ) => {
778
+ openMenu ( ) ;
779
+ expect ( menus . length ) . toBe ( 1 ) ;
780
+
781
+ fixture . debugElement . query ( By . css ( '#container' ) ) . nativeElement . click ( ) ;
782
+ detectChanges ( ) ;
783
+
784
+ expect ( menus . length ) . toBe ( 0 ) ;
785
+ } ) ;
786
+
787
+ it ( 'should not close open menus when clicking on a menu group' , ( ) => {
788
+ openMenu ( ) ;
789
+ expect ( menus . length ) . toBe ( 1 ) ;
790
+
791
+ const menuGroups = fixture . debugElement . queryAll ( By . directive ( CdkMenuGroup ) ) ;
792
+ menuGroups [ 2 ] . nativeElement . click ( ) ;
793
+ detectChanges ( ) ;
794
+
795
+ expect ( menus . length ) . toBe ( 1 ) ;
796
+ } ) ;
797
+
798
+ it ( 'should not close open menus when clicking on a menu' , ( ) => {
799
+ openMenu ( ) ;
800
+ expect ( menus . length ) . toBe ( 1 ) ;
801
+
802
+ fixture . debugElement . query ( By . directive ( CdkMenu ) ) . nativeElement . click ( ) ;
803
+ detectChanges ( ) ;
804
+
805
+ expect ( menus . length ) . toBe ( 1 ) ;
806
+ } ) ;
807
+
808
+ it ( 'should not close open menus when clicking on a menu bar' , ( ) => {
809
+ openMenu ( ) ;
810
+ expect ( menus . length ) . toBe ( 1 ) ;
811
+
812
+ fixture . debugElement . query ( By . directive ( CdkMenuBar ) ) . nativeElement . click ( ) ;
813
+ detectChanges ( ) ;
814
+
815
+ expect ( menus . length ) . toBe ( 1 ) ;
816
+ } ) ;
817
+
818
+ it ( 'should not close when clicking on a CdkMenuItemCheckbox element' , ( ) => {
819
+ openMenu ( ) ;
820
+ expect ( menus . length ) . toBe ( 1 ) ;
821
+
822
+ fixture . debugElement . query ( By . directive ( CdkMenuItemCheckbox ) ) . nativeElement . click ( ) ;
823
+ fixture . detectChanges ( ) ;
824
+
825
+ expect ( menus . length ) . toBe ( 1 ) ;
826
+ } ) ;
827
+
828
+ it ( 'should not close when clicking on a non-menu element inside menu' , ( ) => {
829
+ openMenu ( ) ;
830
+ expect ( menus . length ) . toBe ( 1 ) ;
831
+
832
+ fixture . debugElement . query ( By . css ( '#inner-element' ) ) . nativeElement . click ( ) ;
833
+ detectChanges ( ) ;
834
+
835
+ expect ( menus . length )
836
+ . withContext ( 'menu should stay open if clicking on an inner span element' )
837
+ . toBe ( 1 ) ;
838
+ } ) ;
839
+ } ) ;
738
840
} ) ;
739
841
740
842
@Component ( {
@@ -847,3 +949,27 @@ class MenuWithRadioButtons {
847
949
848
950
@ViewChildren ( CdkMenuItemRadio ) radioItems : QueryList < CdkMenuItemRadio > ;
849
951
}
952
+
953
+ @Component ( {
954
+ template : `
955
+ <div id="container">
956
+ <div cdkMenuBar>
957
+ <button cdkMenuItem [cdkMenuTriggerFor]="sub1">Trigger</button>
958
+ </div>
959
+
960
+ <ng-template cdkMenuPanel #sub1="cdkMenuPanel">
961
+ <div cdkMenu [cdkMenuPanel]="sub1">
962
+ <div cdkMenuGroup>
963
+ <button cdkMenuItemCheckbox>Trigger</button>
964
+ <span id="inner-element">A nested non-menuitem element</span>
965
+ </div>
966
+ </div>
967
+ </ng-template>
968
+ </div>
969
+ ` ,
970
+ } )
971
+ class MenuBarWithMenus {
972
+ @ViewChildren ( CdkMenu ) menus : QueryList < CdkMenu > ;
973
+
974
+ @ViewChildren ( CdkMenuItemTrigger ) triggers : QueryList < CdkMenuItemTrigger > ;
975
+ }
0 commit comments