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

Commit e3e392b

Browse files
committed
fix(menu): fix menus inside toolbars and dialogs
fixes #6131, fixes #6109, fixes #6049, fixes #6073, references #6080, fixes #6089, fixes #6116
1 parent 90ec97c commit e3e392b

File tree

4 files changed

+41
-23
lines changed

4 files changed

+41
-23
lines changed

src/components/menu/js/menuController.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ function MenuController($mdMenu, $attrs, $element, $scope, $mdUtil, $timeout, $r
2525
// Default element for ARIA attributes has the ngClick or ngMouseenter expression
2626
triggerElement = $element[0].querySelector('[ng-click],[ng-mouseenter]');
2727

28+
this.isInMenuBar = opts.isInMenuBar;
2829
this.nestedMenus = $mdUtil.nodesToArray(menuContainer[0].querySelectorAll('.md-nested-menu'));
2930

3031
menuContainer.on('$mdInterimElementRemove', function() {
@@ -117,7 +118,7 @@ function MenuController($mdMenu, $attrs, $element, $scope, $mdUtil, $timeout, $r
117118
element: menuContainer,
118119
target: triggerElement,
119120
preserveElement: true,
120-
parent: $element
121+
parent: 'body'
121122
}).finally(function() {
122123
self.disableHoverListener();
123124
});
@@ -128,14 +129,12 @@ function MenuController($mdMenu, $attrs, $element, $scope, $mdUtil, $timeout, $r
128129

129130
$scope.$watch(function() { return self.isOpen; }, function(isOpen) {
130131
if (isOpen) {
131-
triggerElement.setAttribute('aria-expanded', 'true');
132132
menuContainer.attr('aria-hidden', 'false');
133133
$element[0].classList.add('md-open');
134134
angular.forEach(self.nestedMenus, function(el) {
135135
el.classList.remove('md-open');
136136
});
137137
} else {
138-
triggerElement && triggerElement.setAttribute('aria-expanded', 'false');
139138
menuContainer.attr('aria-hidden', 'true');
140139
$element[0].classList.remove('md-open');
141140
}

src/components/menu/js/menuDirective.js

+4
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,10 @@ function MenuDirective($mdUtil) {
204204
}
205205
menuContainer.append(menuContents);
206206

207+
element.on('$destroy', function() {
208+
menuContainer.remove();
209+
});
210+
207211
element.append(menuContainer);
208212
menuContainer[0].style.display = 'none';
209213
mdMenuCtrl.init(menuContainer, { isInMenuBar: isInMenuBar });

src/components/menu/js/menuServiceProvider.js

+3-6
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,8 @@ function MenuProvider($$interimElementProvider) {
127127
* Place the menu into the DOM and call positioning related functions
128128
*/
129129
function showMenu() {
130-
if (!opts.preserveElement) {
131-
opts.parent.append(element);
132-
} else {
133-
element[0].style.display = '';
134-
}
130+
opts.parent.append(element);
131+
element[0].style.display = '';
135132

136133
return $q(function(resolve) {
137134
var position = calculateMenuPosition(element, opts);
@@ -294,7 +291,7 @@ function MenuProvider($$interimElementProvider) {
294291
if ((hasAnyAttribute(target, ['ng-click', 'ng-href', 'ui-sref']) ||
295292
target.nodeName == 'BUTTON' || target.nodeName == 'MD-BUTTON') && !hasAnyAttribute(target, ['md-prevent-menu-close'])) {
296293
var closestMenu = $mdUtil.getClosest(target, 'MD-MENU');
297-
if (!target.hasAttribute('disabled') && (closestMenu == opts.parent[0])) {
294+
if (!target.hasAttribute('disabled') && (!closestMenu || closestMenu == opts.parent[0])) {
298295
close();
299296
}
300297
break;

src/components/menu/menu.spec.js

+32-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
describe('material.components.menu', function() {
1+
ddescribe('material.components.menu', function() {
22
var attachedElements = [];
33
var $mdMenu, $timeout, menuActionPerformed, $mdUtil;
44

@@ -7,7 +7,7 @@ describe('material.components.menu', function() {
77
$mdUtil = _$mdUtil_;
88
$mdMenu = _$mdMenu_;
99
$timeout = _$timeout_;
10-
var abandonedMenus = $document[0].querySelectorAll('.md-menu-container');
10+
var abandonedMenus = $document[0].querySelectorAll('.md-open-menu-container');
1111
angular.element(abandonedMenus).remove();
1212
}));
1313
afterEach(function() {
@@ -41,6 +41,23 @@ describe('material.components.menu', function() {
4141
expect(getOpenMenuContainer(menu).length).toBe(0);
4242
});
4343

44+
it('cleans up an open menu when the element leaves the DOM', function() {
45+
var menu = setup();
46+
openMenu(menu);
47+
menu.remove();
48+
expect(getOpenMenuContainer(menu).length).toBe(0);
49+
});
50+
51+
it('sets up proper aria-owns and aria-haspopup relations between the button and the container', function() {
52+
var menu = setup();
53+
var button = menu[0].querySelector('button');
54+
expect(button.hasAttribute('aria-haspopup')).toBe(true);
55+
expect(button.hasAttribute('aria-owns')).toBe(true);
56+
var ownsId = button.getAttribute('aria-owns');
57+
openMenu(menu);
58+
expect(getOpenMenuContainer(menu).attr('id')).toBe(ownsId);
59+
});
60+
4461
it('opens on click without $event', function() {
4562
var noEvent = true;
4663
var menu = setup('ng-click', noEvent);
@@ -98,7 +115,7 @@ describe('material.components.menu', function() {
98115
openMenu(menu);
99116
expect(getOpenMenuContainer(menu).length).toBe(1);
100117

101-
var openMenuEl = menu[0].querySelector('md-menu-content');
118+
var openMenuEl = $document[0].querySelector('md-menu-content');
102119

103120
pressKey(openMenuEl, $mdConstant.KEY_CODE.ESCAPE);
104121
waitForMenuClose();
@@ -197,21 +214,22 @@ describe('material.components.menu', function() {
197214
// ********************************************
198215

199216
function getOpenMenuContainer(el) {
217+
var res;
200218
el = (el instanceof angular.element) ? el[0] : el;
201-
var container = el.querySelector('.md-open-menu-container');
202-
if (container.style.display == 'none') {
203-
return angular.element([]);
204-
} else {
205-
return angular.element(container);
206-
}
219+
inject(function($document) {
220+
var container = $document[0].querySelector('.md-open-menu-container');
221+
if (container && container.style.display == 'none') {
222+
res = [];
223+
} else {
224+
res = angular.element(container);
225+
}
226+
});
227+
return res;
207228
}
208229

209230
function openMenu(el, triggerType) {
210-
inject(function($document) {
211-
el.children().eq(0).triggerHandler(triggerType || 'click');
212-
$document[0].body.appendChild(el[0]);
213-
waitForMenuOpen();
214-
});
231+
el.children().eq(0).triggerHandler(triggerType || 'click');
232+
waitForMenuOpen();
215233
}
216234

217235
function closeMenu() {

0 commit comments

Comments
 (0)