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

feat(list): add support for md-menu as proxied element #6459

Closed
wants to merge 1 commit into from
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
32 changes: 32 additions & 0 deletions src/components/list/demoListControls/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,38 @@

<md-divider></md-divider>

<md-subheader class="md-no-sticky">Secondary Menus</md-subheader>
<md-list-item>
<p>Click anywhere to fire the secondary action</p>

<md-menu class="md-secondary">
<md-button class="md-icon-button">
<md-icon md-svg-icon="communication:message"></md-icon>
</md-button>
<md-menu-content width="4">
<md-menu-item>
<md-button>
Redial
</md-button>
</md-menu-item>
<md-menu-item>
<md-button>
Check voicemail
</md-button>
</md-menu-item>
<md-menu-divider></md-menu-divider>
<md-menu-item>
<md-button>
Notifications
</md-button>
</md-menu-item>
</md-menu-content>
</md-menu>

</md-list-item>

<md-divider></md-divider>

<md-subheader class="md-no-sticky">Clickable Items with Secondary Controls</md-subheader>
<md-list-item ng-click="navigateTo(setting.extraScreen, $event)" ng-repeat="setting in settings">
<md-icon md-svg-icon="{{setting.icon}}"></md-icon>
Expand Down
39 changes: 38 additions & 1 deletion src/components/list/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ function mdListDirective($mdTheming) {
* that is inside the list, but does not wrap the contents._
*/
function mdListItemDirective($mdAria, $mdConstant, $mdUtil, $timeout) {
var proxiedTypes = ['md-checkbox', 'md-switch'];
var proxiedTypes = ['md-checkbox', 'md-switch', 'md-menu'];
return {
restrict: 'E',
controller: 'MdListController',
Expand Down Expand Up @@ -112,9 +112,13 @@ function mdListItemDirective($mdAria, $mdConstant, $mdUtil, $timeout) {
tEl.addClass('_md-no-proxy');
}
}

wrapSecondaryItems();
setupToggleAria();

if (hasProxiedElement && proxyElement.nodeName === "MD-MENU") {
setupProxiedMenu();
}

function setupToggleAria() {
var toggleTypes = ['md-switch', 'md-checkbox'];
Expand All @@ -131,6 +135,35 @@ function mdListItemDirective($mdAria, $mdConstant, $mdUtil, $timeout) {
}
}

function setupProxiedMenu() {
var menuEl = angular.element(proxyElement);

var isEndAligned = menuEl.parent().hasClass('_md-secondary-container') ||
proxyElement.parentNode.firstElementChild !== proxyElement;

var xAxisPosition = 'left';

if (isEndAligned) {
// When the proxy item is aligned at the end of the list, we have to set the origin to the end.
xAxisPosition = 'right';
}

// Set the position mode / origin of the proxied menu.
if (!menuEl.attr('md-position-mode')) {
menuEl.attr('md-position-mode', xAxisPosition + ' target');
}

// Apply menu open binding to menu button
var menuOpenButton = menuEl.children().eq(0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a weird line

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is used by the original menu too (See here). I already had find, and validating the right parent. I think using the first children is the right way to get the triggering element.

if (!hasClickEvent(menuOpenButton[0])) {
menuOpenButton.attr('ng-click', '$mdOpenMenu($event)');
}

if (!menuOpenButton.attr('aria-label')) {
menuOpenButton.attr('aria-label', 'Open List Menu');
}
}

function wrapIn(type) {
if (type == 'div') {
itemContainer = angular.element('<div class="_md-no-style _md-list-item-inner">');
Expand Down Expand Up @@ -277,6 +310,7 @@ function mdListItemDirective($mdAria, $mdConstant, $mdUtil, $timeout) {

}
}

function computeClickable() {
if (proxies.length == 1 || hasClick) {
$element.addClass('md-clickable');
Expand Down Expand Up @@ -313,6 +347,9 @@ function mdListItemDirective($mdAria, $mdConstant, $mdUtil, $timeout) {
if (!parentButton && clickChild.contains(e.target)) {
angular.forEach(proxies, function(proxy) {
if (e.target !== proxy && !proxy.contains(e.target)) {
if (proxy.nodeName === 'MD-MENU') {
proxy = proxy.children[0];
}
angular.element(proxy).triggerHandler('click');
}
});
Expand Down
55 changes: 55 additions & 0 deletions src/components/list/list.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,61 @@ describe('mdListItem directive', function() {
expect(button[0].hasAttribute('ng-disabled')).toBeTruthy();
});

describe('with a md-menu', function() {
it('should forward click events on the md-menu trigger button', function() {
var template =
'<md-list-item>' +
'<md-menu>' +
'<md-button ng-click="openMenu()"></md-button>' +
'</md-menu>' +
'</md-list-item>';

var listItem = setup(template);
var cntr = listItem[0].querySelector('div');
var openMenu = jasmine.createSpy('openMenu');

$rootScope.openMenu = openMenu;

if (cntr && cntr.click) {
cntr.click();
expect(openMenu).toHaveBeenCalled();
}

});

it('should detect the menu position mode when md-menu is aligned at right', function() {
var template =
'<md-list-item>' +
'<span>Menu should be aligned right</span>' +
'<md-menu>' +
'<md-button ng-click="openMenu()"></md-button>' +
'</md-menu>' +
'</md-list-item>';

var listItem = setup(template);

var mdMenu = listItem.find('md-menu');

expect(mdMenu.attr('md-position-mode')).toBe('right target');
});

it('should detect the menu position mode when md-menu is aligned at left', function() {
var template =
'<md-list-item>' +
'<md-menu>' +
'<md-button ng-click="openMenu()"></md-button>' +
'</md-menu>' +
'<span>Menu should be aligned left</span>' +
'</md-list-item>';

var listItem = setup(template);

var mdMenu = listItem.find('md-menu');

expect(mdMenu.attr('md-position-mode')).toBe('left target');
});
});

describe('with a clickable item', function() {

it('should wrap secondary icons in a md-button', function() {
Expand Down
11 changes: 10 additions & 1 deletion src/components/menu/js/menuServiceProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -461,8 +461,17 @@ function MenuProvider($$interimElementProvider) {
position.left = willFitRight ? originNodeRect.right - originNode.style.left : originNodeRect.left - originNode.style.left - openMenuNodeRect.width;
transformOrigin += willFitRight ? 'left' : 'right';
break;
case 'right':
if (rtl) {
position.left = originNodeRect.right - originNodeRect.width;
transformOrigin += 'left';
} else {
position.left = originNodeRect.right - openMenuNodeRect.width;
transformOrigin += 'right';
}
break;
case 'left':
if(rtl) {
if (rtl) {
position.left = originNodeRect.right - openMenuNodeRect.width;
transformOrigin += 'right';
} else {
Expand Down