@@ -27,6 +27,7 @@ import {
27
27
dispatchKeyboardEvent ,
28
28
createKeyboardEvent ,
29
29
dispatchEvent ,
30
+ dispatchMouseEvent ,
30
31
} from '@angular/cdk/testing/private' ;
31
32
import { CdkMenuBar } from './menu-bar' ;
32
33
import { CdkMenuModule } from './menu-module' ;
@@ -837,6 +838,195 @@ describe('MenuBar', () => {
837
838
. toBe ( 1 ) ;
838
839
} ) ;
839
840
} ) ;
841
+
842
+ describe ( 'Mouse handling' , ( ) => {
843
+ let fixture : ComponentFixture < MultiMenuWithSubmenu > ;
844
+ let nativeMenus : HTMLElement [ ] ;
845
+ let menuBarNativeItems : HTMLButtonElement [ ] ;
846
+ let fileMenuNativeItems : HTMLButtonElement [ ] ;
847
+ let shareMenuNativeItems : HTMLButtonElement [ ] ;
848
+
849
+ /** Get menus and items used for tests. */
850
+ function grabElementsForTesting ( ) {
851
+ nativeMenus = fixture . componentInstance . nativeMenus . map ( e => e . nativeElement ) ;
852
+
853
+ menuBarNativeItems = fixture . componentInstance . nativeItems
854
+ . map ( e => e . nativeElement )
855
+ . slice ( 0 , 2 ) ; // menu bar has the first 2 menu items
856
+
857
+ fileMenuNativeItems = fixture . componentInstance . nativeItems
858
+ . map ( e => e . nativeElement )
859
+ . slice ( 2 , 5 ) ; // file menu has the next 3 menu items
860
+
861
+ shareMenuNativeItems = fixture . componentInstance . nativeItems
862
+ . map ( e => e . nativeElement )
863
+ . slice ( 5 , 7 ) ; // share menu has the next 2 menu items
864
+ }
865
+
866
+ /** Run change detection and extract then set the rendered elements. */
867
+ function detectChanges ( ) {
868
+ fixture . detectChanges ( ) ;
869
+ grabElementsForTesting ( ) ;
870
+ }
871
+
872
+ /** Mock mouse events required to open the file menu. */
873
+ function openFileMenu ( ) {
874
+ dispatchMouseEvent ( menuBarNativeItems [ 0 ] , 'mouseenter' ) ;
875
+ dispatchMouseEvent ( menuBarNativeItems [ 0 ] , 'click' ) ;
876
+ detectChanges ( ) ;
877
+ }
878
+
879
+ /** Mock mouse events required to open the share menu. */
880
+ function openShareMenu ( ) {
881
+ dispatchMouseEvent ( fileMenuNativeItems [ 1 ] , 'mouseenter' ) ;
882
+ detectChanges ( ) ;
883
+ }
884
+
885
+ beforeEach ( async ( ( ) => {
886
+ TestBed . configureTestingModule ( {
887
+ imports : [ CdkMenuModule ] ,
888
+ declarations : [ MultiMenuWithSubmenu ] ,
889
+ } ) . compileComponents ( ) ;
890
+ } ) ) ;
891
+
892
+ beforeEach ( ( ) => {
893
+ fixture = TestBed . createComponent ( MultiMenuWithSubmenu ) ;
894
+ detectChanges ( ) ;
895
+ } ) ;
896
+
897
+ it ( 'should toggle menu from menu bar when clicked' , ( ) => {
898
+ openFileMenu ( ) ;
899
+
900
+ expect ( nativeMenus . length ) . toBe ( 1 ) ;
901
+ expect ( nativeMenus [ 0 ] . id ) . toBe ( 'file_menu' ) ;
902
+
903
+ dispatchMouseEvent ( menuBarNativeItems [ 0 ] , 'click' ) ;
904
+ detectChanges ( ) ;
905
+
906
+ expect ( nativeMenus . length ) . toBe ( 0 ) ;
907
+ } ) ;
908
+
909
+ it ( 'should not open menu when hovering over trigger in menu bar with no open siblings' , ( ) => {
910
+ dispatchMouseEvent ( menuBarNativeItems [ 0 ] , 'mouseenter' ) ;
911
+ detectChanges ( ) ;
912
+
913
+ expect ( nativeMenus . length ) . toBe ( 0 ) ;
914
+ } ) ;
915
+
916
+ it (
917
+ 'should not change focused items when hovering over trigger in menu bar with no open ' +
918
+ 'siblings' ,
919
+ ( ) => {
920
+ dispatchMouseEvent ( menuBarNativeItems [ 0 ] , 'mouseenter' ) ;
921
+ detectChanges ( ) ;
922
+
923
+ expect ( document . querySelector ( ':focus' ) ) . not . toEqual ( menuBarNativeItems [ 0 ] ) ;
924
+ expect ( document . querySelector ( ':focus' ) ) . not . toEqual ( menuBarNativeItems [ 1 ] ) ;
925
+ }
926
+ ) ;
927
+
928
+ it (
929
+ 'should toggle open menus in menu bar if sibling is open when mouse moves from one item ' +
930
+ 'to the other' ,
931
+ ( ) => {
932
+ openFileMenu ( ) ;
933
+
934
+ dispatchMouseEvent ( menuBarNativeItems [ 1 ] , 'mouseenter' ) ;
935
+ detectChanges ( ) ;
936
+
937
+ expect ( nativeMenus . length ) . toBe ( 1 ) ;
938
+ expect ( nativeMenus [ 0 ] . id ) . toBe ( 'edit_menu' ) ;
939
+
940
+ dispatchMouseEvent ( menuBarNativeItems [ 0 ] , 'mouseenter' ) ;
941
+ detectChanges ( ) ;
942
+
943
+ expect ( nativeMenus . length ) . toBe ( 1 ) ;
944
+ expect ( nativeMenus [ 0 ] . id ) . toBe ( 'file_menu' ) ;
945
+
946
+ dispatchMouseEvent ( menuBarNativeItems [ 1 ] , 'mouseenter' ) ;
947
+ detectChanges ( ) ;
948
+
949
+ expect ( nativeMenus . length ) . toBe ( 1 ) ;
950
+ expect ( nativeMenus [ 0 ] . id ) . toBe ( 'edit_menu' ) ;
951
+ }
952
+ ) ;
953
+
954
+ it ( 'should not close the menu when re-hovering the trigger' , ( ) => {
955
+ openFileMenu ( ) ;
956
+
957
+ dispatchMouseEvent ( menuBarNativeItems [ 0 ] , 'mouseenter' ) ;
958
+
959
+ expect ( nativeMenus . length ) . toBe ( 1 ) ;
960
+ expect ( nativeMenus [ 0 ] . id ) . toBe ( 'file_menu' ) ;
961
+ } ) ;
962
+
963
+ it ( 'should open a submenu when hovering over a trigger in a menu with no siblings open' , ( ) => {
964
+ openFileMenu ( ) ;
965
+
966
+ openShareMenu ( ) ;
967
+
968
+ expect ( nativeMenus . length ) . toBe ( 2 ) ;
969
+ expect ( nativeMenus [ 0 ] . id ) . toBe ( 'file_menu' ) ;
970
+ expect ( nativeMenus [ 1 ] . id ) . toBe ( 'share_menu' ) ;
971
+ } ) ;
972
+
973
+ it ( 'should close menu when hovering over non-triggering sibling menu item' , ( ) => {
974
+ openFileMenu ( ) ;
975
+ openShareMenu ( ) ;
976
+
977
+ dispatchMouseEvent ( fileMenuNativeItems [ 0 ] , 'mouseenter' ) ;
978
+ detectChanges ( ) ;
979
+
980
+ expect ( nativeMenus . length ) . toBe ( 1 ) ;
981
+ expect ( nativeMenus [ 0 ] . id ) . toBe ( 'file_menu' ) ;
982
+ } ) ;
983
+
984
+ it ( 'should retain open menus when hovering over root level trigger which opened them' , ( ) => {
985
+ openFileMenu ( ) ;
986
+ openShareMenu ( ) ;
987
+
988
+ dispatchMouseEvent ( menuBarNativeItems [ 0 ] , 'mouseenter' ) ;
989
+ detectChanges ( ) ;
990
+
991
+ expect ( nativeMenus . length ) . toBe ( 2 ) ;
992
+ } ) ;
993
+
994
+ it ( 'should close out the menu tree when hovering over sibling item in menu bar' , ( ) => {
995
+ openFileMenu ( ) ;
996
+ openShareMenu ( ) ;
997
+
998
+ dispatchMouseEvent ( menuBarNativeItems [ 1 ] , 'mouseenter' ) ;
999
+ detectChanges ( ) ;
1000
+
1001
+ expect ( nativeMenus . length ) . toBe ( 1 ) ;
1002
+ expect ( nativeMenus [ 0 ] . id ) . toBe ( 'edit_menu' ) ;
1003
+ } ) ;
1004
+
1005
+ it ( 'should close out the menu tree when clicking a non-triggering menu item' , ( ) => {
1006
+ openFileMenu ( ) ;
1007
+ openShareMenu ( ) ;
1008
+
1009
+ dispatchMouseEvent ( shareMenuNativeItems [ 0 ] , 'mouseenter' ) ;
1010
+ dispatchMouseEvent ( shareMenuNativeItems [ 0 ] , 'click' ) ;
1011
+ detectChanges ( ) ;
1012
+
1013
+ expect ( nativeMenus . length ) . toBe ( 0 ) ;
1014
+ } ) ;
1015
+
1016
+ it (
1017
+ 'should allow keyboard down arrow to focus next item after mouse sets focus to' +
1018
+ ' initial item' ,
1019
+ ( ) => {
1020
+ openFileMenu ( ) ;
1021
+ dispatchMouseEvent ( fileMenuNativeItems [ 0 ] , 'mouseenter' ) ;
1022
+ detectChanges ( ) ;
1023
+
1024
+ dispatchKeyboardEvent ( nativeMenus [ 0 ] , 'keydown' , DOWN_ARROW ) ;
1025
+
1026
+ expect ( document . querySelector ( ':focus' ) ) . toEqual ( fileMenuNativeItems [ 1 ] ) ;
1027
+ }
1028
+ ) ;
1029
+ } ) ;
840
1030
} ) ;
841
1031
842
1032
@Component ( {
0 commit comments