Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

fix(menuBar): Fix hovering consecutive nested menus. #7361

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/components/menu/js/menuController.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ function MenuController($mdMenu, $attrs, $element, $scope, $mdUtil, $timeout, $r
if (self.currentlyOpenMenu && self.currentlyOpenMenu != nestedMenu) {
var closeTo = self.nestLevel + 1;
self.currentlyOpenMenu.close(true, { closeTo: closeTo });
self.isAlreadyOpening = true;
nestedMenu.open();
} else if (nestedMenu && !nestedMenu.isOpen && nestedMenu.open) {
self.isAlreadyOpening = true;
nestedMenu.open();
Expand Down
2 changes: 1 addition & 1 deletion src/components/menuBar/js/menuBarController.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ MenuBarController.prototype.focusMenu = function(direction) {

var changed = false;

if (focusedIndex == -1) { focusedIndex = 0; }
if (focusedIndex == -1) { focusedIndex = 0; changed = true; }
else if (
direction < 0 && focusedIndex > 0 ||
direction > 0 && focusedIndex < menus.length - direction
Expand Down
113 changes: 111 additions & 2 deletions src/components/menuBar/menu-bar.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ describe('material.components.menuBar', function() {
it('sets md-position-mode to "bottom left" on nested menus', function() {
var menuBar = setup();
var nestedMenu = menuBar[0].querySelector('md-menu');

expect(nestedMenu.getAttribute('md-position-mode')).toBe('left bottom');
});

Expand All @@ -25,6 +26,92 @@ describe('material.components.menuBar', function() {
expect(ariaRole).toBe('menubar');
});
});

describe('nested menus', function() {
var menuBar, menus, subMenuOpen, ctrl;

it('opens consecutive nested menus', function() {
menuBar = setup();
ctrl = menuBar.controller('mdMenuBar');
menus = menuBar[0].querySelectorAll('md-menu md-menu');

angular.element(document.body).append(menuBar);

// Open the menu-bar menu
ctrl.focusMenu(1);
ctrl.openFocusedMenu();
waitForMenuOpen();

// Open the first nested menu
openSubMenu(0);
waitForMenuOpen();
expect(getOpenSubMenu().text().trim()).toBe('Sub 1 - Content');

// Open the second nested menu, the first menu should close
openSubMenu(1);
waitForMenuClose();

// Then the second menu should become visible
waitForMenuOpen();
expect(getOpenSubMenu().text().trim()).toBe('Sub 2 - Content');

menuBar.remove();
});

function openSubMenu(index) {
// If a menu is already open, trigger the mouse leave to close it
if (subMenuOpen) {
subMenuOpen.triggerHandler({
type: 'mouseleave',
target: subMenuOpen[0],
currentTarget: subMenuOpen[0]
});
}

// Set the currently open sub-menu and trigger the mouse enter
subMenuOpen = angular.element(menus[index]);
subMenuOpen.triggerHandler({
type: 'mouseenter',
target: subMenuOpen[0],
currentTarget: subMenuOpen[0]
});
}

function getOpenSubMenu() {
debugger;
var containers = document.body.querySelectorAll('._md-open-menu-container._md-active');
var lastContainer = containers.item(containers.length - 1);

return angular.element(lastContainer.querySelector('md-menu-content'));
}

function setup(){
var el;
inject(function($compile, $rootScope) {
el = $compile([
'<md-menu-bar>',
' <md-menu>',
' <md-menu-item>',
' <button ng-click="clicked=true">Button {{i}}</button>',
' </md-menu-item>',
' <md-menu-content class="test-submenu">',
' <md-menu ng-repeat="i in [1, 2]">',
' <md-menu-item>',
' <button ng-click="subclicked=true">Sub Button{{i}}</button>',
' </md-menu-item>',
' <md-menu-content>Sub {{i}} - Content</md-menu-content>',
' </md-menu>',
' </md-menu-content>',
' </md-menu>',
'</md-menu-bar>'
].join(''))($rootScope);
$rootScope.$digest();
});
attachedMenuElements.push(el);

return el;
}
});
});

describe('MenuBarCtrl', function() {
Expand Down Expand Up @@ -64,6 +151,13 @@ describe('material.components.menuBar', function() {
describe('#focusMenu', function() {
var focused;
beforeEach(function() { focused = false; });
it('focuses the first menu if none is focused', function() {
var menus = mockButtonAtIndex(0);
spyOn(ctrl, 'getFocusedMenuIndex').and.returnValue(-1);
spyOn(ctrl, 'getMenus').and.returnValue(menus);
ctrl.focusMenu(1);
expect(focused).toBe(true);
});
it('focuses the next menu', function() {
var menus = mockButtonAtIndex(1);
spyOn(ctrl, 'getFocusedMenuIndex').and.returnValue(0);
Expand Down Expand Up @@ -100,13 +194,16 @@ describe('material.components.menuBar', function() {
var mockButton = {
querySelector: function() { return {
focus: function() { focused = true; }
}; }
}; },

// TODO: This may need to become more complex if more of the tests use it
classList: { contains: function() { return false; } }
};
for (var i = 0; i < 3; ++i) {
if (i == index) {
result.push(mockButton);
} else {
result.push({});
result.push({ classList: mockButton.classList });
}
}
return result;
Expand Down Expand Up @@ -307,5 +404,17 @@ describe('material.components.menuBar', function() {
}
});
});

function waitForMenuOpen() {
inject(function($material) {
$material.flushInterimElement();
});
}

function waitForMenuClose() {
inject(function($material) {
$material.flushInterimElement();
});
}
});