@@ -34,6 +34,8 @@ import {CdkMenuItemRadio} from './menu-item-radio';
3434import { CdkMenu } from './menu' ;
3535import { CdkMenuItem } from './menu-item' ;
3636import { CdkMenuItemCheckbox } from './menu-item-checkbox' ;
37+ import { CdkMenuItemTrigger } from './menu-item-trigger' ;
38+ import { CdkMenuGroup } from './menu-group' ;
3739
3840describe ( 'MenuBar' , ( ) => {
3941 describe ( 'as radio group' , ( ) => {
@@ -735,6 +737,106 @@ describe('MenuBar', () => {
735737 ) ;
736738 } ) ;
737739 } ) ;
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+ } ) ;
738840} ) ;
739841
740842@Component ( {
@@ -847,3 +949,27 @@ class MenuWithRadioButtons {
847949
848950 @ViewChildren ( CdkMenuItemRadio ) radioItems : QueryList < CdkMenuItemRadio > ;
849951}
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