angular-material-npfixed
Version:
The Angular Material project is an implementation of Material Design in Angular.js. This project provides a set of reusable, well-tested, and accessible Material Design UI components. Angular Material is supported internally at Google by the Angular.js, M
615 lines (460 loc) • 20.7 kB
JavaScript
describe('mdListItem directive', function() {
var attachedElements = [];
var $compile, $rootScope;
beforeEach(module(
'material.components.list',
'material.components.checkbox',
'material.components.switch',
'material.components.button'
));
beforeEach(inject(function(_$compile_, _$rootScope_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
}));
afterEach(function() {
attachedElements.forEach(function(element) {
element.remove();
});
attachedElements = [];
});
function setup(html) {
var el;
inject(function($compile, $rootScope) {
el = $compile(html)($rootScope);
$rootScope.$apply();
});
attachedElements.push(el);
return el;
}
describe('md-list-item', function() {
it('should have `._md` class indicator', inject(function($compile, $rootScope) {
var element = $compile('<md-list><md-list-item></md-list-item></md-list>')($rootScope.$new());
expect(element.find('md-list-item').hasClass('_md')).toBe(true);
}));
});
it('forwards click events for md-checkbox', function() {
var listItem = setup(
'<md-list-item>' +
'<md-checkbox ng-model="modelVal"></md-checkbox>' +
'</md-list-item>');
var cntr = listItem[0].querySelector('div');
cntr.click();
expect($rootScope.modelVal).toBe(true);
cntr.click();
expect($rootScope.modelVal).toBe(false);
});
it('forwards click events for md-switch', function() {
var listItem = setup(
'<md-list-item>' +
'<md-switch ng-model="modelVal"></md-switch>' +
'</md-list-item>');
var cntr = listItem[0].querySelector('div');
cntr.click();
expect($rootScope.modelVal).toBe(true);
cntr.click();
expect($rootScope.modelVal).toBe(false);
});
it('should not wrap when proxies are disabled', function() {
var listItem = setup(
'<md-list-item class="md-no-proxy">' +
'<md-switch ng-model="modelVal"></md-switch>' +
'</md-list-item>'
);
var switchEl = listItem[0].querySelector('md-switch');
// If proxies are disabled, the list will not wrap anything.
expect(switchEl.parentNode).toBe(listItem[0]);
listItem.triggerHandler('click');
expect($rootScope.modelVal).toBeFalsy();
switchEl.click();
expect($rootScope.modelVal).toBeTruthy();
expect(listItem).not.toHaveClass('md-clickable')
});
it('should not trigger the proxy element, when clicking on a slider', function() {
var listItem = setup(
'<md-list-item>' +
'<md-slider></md-slider>' +
'<md-switch ng-model="modelVal"></md-switch>' +
'</md-list-item>');
var slider = listItem.find('md-slider')[0];
slider.click();
expect($rootScope.modelVal).toBeFalsy();
});
it('should convert spacebar keypress events as clicks', inject(function($mdConstant) {
var listItem = setup(
'<md-list-item>' +
'<md-checkbox ng-model="modelVal"></md-checkbox>' +
'</md-list-item>');
var checkbox = angular.element(listItem[0].querySelector('md-checkbox'));
expect($rootScope.modelVal).toBeFalsy();
checkbox.triggerHandler({
type: 'keypress',
keyCode: $mdConstant.KEY_CODE.SPACE
});
expect($rootScope.modelVal).toBe(true);
}));
it('should not convert spacebar keypress for text areas', inject(function($mdConstant) {
var listItem = setup(
'<md-list-item>' +
'<textarea ng-model="modelVal">' +
'</md-list-item>');
var inputEl = angular.element(listItem[0].querySelector('textarea')[0]);
expect($rootScope.modelVal).toBeFalsy();
inputEl.triggerHandler({
type: 'keypress',
keyCode: $mdConstant.KEY_CODE.SPACE
});
expect($rootScope.modelVal).toBeFalsy();
}));
it('should not convert spacebar keypress for editable elements', inject(function($mdConstant) {
var listItem = setup(
'<md-list-item>' +
'<div contenteditable="true"></div>' +
'</md-list-item>');
var editableEl = listItem.find('div');
var onClickSpy = jasmine.createSpy('onClickSpy');
// We need to append our element to the DOM because the browser won't detect `contentEditable` when the element
// is hidden in the DOM. See the related issue for chromium:
// https://code.google.com/p/chromium/issues/detail?id=313082
document.body.appendChild(listItem[0]);
editableEl.on('click', onClickSpy);
// We need to dispatch the keypress natively, because otherwise the `keypress` won't be triggered in the list.
var event = document.createEvent('Event');
event.keyCode = $mdConstant.KEY_CODE.SPACE;
event.initEvent('keypress', true, true);
editableEl[0].dispatchEvent(event);
expect(onClickSpy).not.toHaveBeenCalled();
document.body.removeChild(listItem[0]);
}));
it('creates buttons when used with ng-click', function() {
var listItem = setup(
'<md-list-item ng-click="sayHello()" ng-disabled="true">' +
'<p>Hello world</p>' +
'</md-list-item>');
// List items, which are clickable always contain a button wrap at the top level.
var buttonWrap = listItem.children().eq(0);
expect(listItem).toHaveClass('_md-button-wrap');
// The button wrap should contain the button executor, the inner content and the
// secondary item container as children.
expect(buttonWrap.children().length).toBe(3);
var buttonExecutor = buttonWrap.children()[0];
// The list item should forward the click and disabled attributes.
expect(buttonExecutor.hasAttribute('ng-click')).toBe(true);
expect(buttonExecutor.hasAttribute('ng-disabled')).toBe(true);
var innerContent = buttonWrap.children()[1];
expect(innerContent.nodeName).toBe('DIV');
expect(innerContent.firstElementChild.nodeName).toBe('P');
});
it('creates buttons when used with ng-dblclick', function() {
var listItem = setup(
'<md-list-item ng-dblclick="sayHello()" ng-disabled="true">' +
'<p>Hello world</p>' +
'</md-list-item>');
// List items, which are clickable always contain a button wrap at the top level.
var buttonWrap = listItem.children().eq(0);
expect(listItem).toHaveClass('_md-button-wrap');
// The button wrap should contain the button executor, the inner content and the
// secondary item container as children.
expect(buttonWrap.children().length).toBe(3);
var buttonExecutor = buttonWrap.children()[0];
// The list item should forward the click and disabled attributes.
expect(buttonExecutor.hasAttribute('ng-dblclick')).toBe(true);
expect(buttonExecutor.hasAttribute('ng-disabled')).toBe(true);
var innerContent = buttonWrap.children()[1];
expect(innerContent.nodeName).toBe('DIV');
expect(innerContent.firstElementChild.nodeName).toBe('P');
});
it('creates buttons when used with ui-sref', function() {
var listItem = setup(
'<md-list-item ui-sref="somestate">' +
'<p>Hello world</p>' +
'</md-list-item>');
// List items, which are clickable always contain a button wrap at the top level.
var buttonWrap = listItem.children().eq(0);
expect(listItem).toHaveClass('_md-button-wrap');
// The button wrap should contain the button executor, the inner content and the
// secondary item container as children.
expect(buttonWrap.children().length).toBe(3);
var buttonExecutor = buttonWrap.children()[0];
// The list item should forward the ui-sref attribute.
expect(buttonExecutor.hasAttribute('ui-sref')).toBe(true);
var innerContent = buttonWrap.children()[1];
expect(innerContent.nodeName).toBe('DIV');
expect(innerContent.firstElementChild.nodeName).toBe('P');
});
it('creates buttons when used with href', function() {
var listItem = setup(
'<md-list-item href="/somewhere">' +
'<p>Hello world</p>' +
'</md-list-item>');
// List items, which are clickable always contain a button wrap at the top level.
var buttonWrap = listItem.children().eq(0);
expect(listItem).toHaveClass('_md-button-wrap');
// The button wrap should contain the button executor, the inner content and the
// secondary item container as children.
expect(buttonWrap.children().length).toBe(3);
var buttonExecutor = buttonWrap.children()[0];
// The list item should forward the href attribute.
expect(buttonExecutor.hasAttribute('href')).toBe(true);
var innerContent = buttonWrap.children()[1];
expect(innerContent.nodeName).toBe('DIV');
expect(innerContent.firstElementChild.nodeName).toBe('P');
});
it('should forward the md-no-focus class', function() {
var listItem = setup(
'<md-list-item ng-click="null" class="md-no-focus">' +
'<p>Clickable - Without Focus Style</p>' +
'</md-list-item>');
// List items, which are clickable always contain a button wrap at the top level.
var buttonWrap = listItem.children().eq(0);
expect(listItem).toHaveClass('_md-button-wrap');
// The button wrap should contain the button executor, the inner content and the
// secondary item container as children.
expect(buttonWrap.children().length).toBe(3);
var buttonExecutor = buttonWrap.children();
// The list item should forward the href and md-no-focus-style attribute.
expect(buttonExecutor.attr('ng-click')).toBeTruthy();
expect(buttonExecutor.hasClass('md-no-focus')).toBe(true);
});
it('moves aria-label to primary action', function() {
var listItem = setup('<md-list-item ng-click="sayHello()" aria-label="Hello"></md-list-item>');
var buttonWrap = listItem.children().eq(0);
expect(listItem).toHaveClass('_md-button-wrap');
// The actual click button will be a child of the button.md-no-style wrapper.
var buttonExecutor = buttonWrap.children()[0];
expect(buttonExecutor.nodeName).toBe('BUTTON');
expect(buttonExecutor.getAttribute('aria-label')).toBe('Hello');
});
it('moves secondary items outside of the button', function() {
var listItem = setup(
'<md-list-item ng-click="sayHello()">' +
'<p>Hello World</p>' +
'<md-icon class="md-secondary" ng-click="goWild()"></md-icon>' +
'</md-list-item>');
// First child is our button wrap
var firstChild = listItem.children().eq(0);
expect(firstChild[0].nodeName).toBe('DIV');
expect(listItem).toHaveClass('_md-button-wrap');
// It should contain three elements, the button overlay, inner content
// and the secondary container.
expect(firstChild.children().length).toBe(3);
var secondaryContainer = firstChild.children().eq(2);
expect(secondaryContainer).toHaveClass('md-secondary-container');
// The secondary container should contain the md-icon,
// which has been transformed to an icon button.
expect(secondaryContainer.children()[0].nodeName).toBe('BUTTON');
});
it('should copy ng-show to the generated button parent of a clickable secondary item', function() {
var listItem = setup(
'<md-list-item ng-click="sayHello()">' +
'<p>Hello World</p>' +
'<md-icon class="md-secondary" ng-show="isShown" ng-click="goWild()"></md-icon>' +
'</md-list-item>');
// First child is our button wrap
var firstChild = listItem.children().eq(0);
expect(firstChild[0].nodeName).toBe('DIV');
expect(listItem).toHaveClass('_md-button-wrap');
// It should contain three elements, the button overlay, inner content
// and the secondary container.
expect(firstChild.children().length).toBe(3);
var secondaryContainer = firstChild.children().eq(2);
expect(secondaryContainer).toHaveClass('md-secondary-container');
// The secondary container should contain the md-icon,
// which has been transformed to an icon button.
var iconButton = secondaryContainer.children()[0];
expect(iconButton.nodeName).toBe('BUTTON');
expect(iconButton.hasAttribute('ng-show')).toBe(true);
// The actual `md-icon` element, should not have the ng-show attribute anymore.
expect(iconButton.firstElementChild.hasAttribute('ng-show')).toBe(false);
});
it('moves multiple md-secondary items outside of the button', function() {
var listItem = setup(
'<md-list-item ng-click="sayHello()">' +
'<p>Hello World</p>' +
'<md-icon class="md-secondary" ng-click="goWild()"></md-icon>' +
'<md-icon class="md-secondary" ng-click="goWild2()"></md-icon>' +
'</md-list-item>');
// First child is our button wrap
var firstChild = listItem.children().eq(0);
expect(firstChild[0].nodeName).toBe('DIV');
expect(listItem).toHaveClass('_md-button-wrap');
// It should contain three elements, the button overlay, inner content,
// and the secondary container.
expect(firstChild.children().length).toBe(3);
var secondaryContainer = firstChild.children().eq(2);
expect(secondaryContainer).toHaveClass('md-secondary-container');
// The secondary container should hold the two secondary items.
expect(secondaryContainer.children().length).toBe(2);
expect(secondaryContainer.children()[0].nodeName).toBe('BUTTON');
expect(secondaryContainer.children()[1].nodeName).toBe('BUTTON');
});
it('should not detect a normal button as a proxy element', function() {
var listItem = setup('<md-list-item><md-button ng-click="sayHello()">Hello</md-button></md-list-item>');
expect(listItem.hasClass('md-no-proxy')).toBeTruthy();
});
it('should not detect a secondary button as a proxy element', function() {
var listItem = setup(
'<md-list-item>' +
' <div>Content Here</div>' +
' <md-button class="md-secondary" ng-click="sayHello()">Hello</md-button>' +
'</md-list-item>'
);
expect(listItem.hasClass('md-no-proxy')).toBeTruthy();
});
it('should copy md-icon.md-secondary attributes to the button', function() {
var listItem = setup(
'<md-list-item>' +
' <div>Content Here</div>' +
' <md-checkbox></md-checkbox>' +
' <md-icon class="md-secondary" ng-click="sayHello()" ng-disabled="true">Hello</md-icon>' +
'</md-list-item>'
);
var button = listItem.find('button');
expect(button[0].hasAttribute('ng-click')).toBeTruthy();
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');
});
it('should apply an aria-label if not specified', function() {
var template =
'<md-list-item>' +
'<span>Aria Label Menu</span>' +
'<md-menu>' +
'<md-button ng-click="openMenu()"></md-button>' +
'</md-menu>' +
'</md-list-item>';
var listItem = setup(template);
var mdMenuButton = listItem[0].querySelector('md-menu > button');
expect(mdMenuButton.getAttribute('aria-label')).toBe('Open List Menu');
});
it('should apply $mdMenuOpen to the button if not present', function() {
var template =
'<md-list-item>' +
'<span>Aria Label Menu</span>' +
'<md-menu>' +
'<md-button>Should Open the Menu</md-button>' +
'</md-menu>' +
'</md-list-item>';
var listItem = setup(template);
var mdMenuButton = listItem[0].querySelector('md-menu > button');
expect(mdMenuButton.getAttribute('ng-click')).toBe('$mdMenu.open($event)');
});
});
describe('aria-label', function() {
it('should copy label to the button executor element', function() {
var listItem = setup('<md-list-item ng-click="null" aria-label="Test">');
var buttonEl = listItem.find('button');
// The aria-label attribute should be moved to the button element.
expect(buttonEl.attr('aria-label')).toBe('Test');
expect(listItem.attr('aria-label')).toBeFalsy();
});
it('should determine the label from the content if not set', function() {
var listItem = setup(
'<md-list-item ng-click="null">' +
'<span>Content</span>' +
'<span aria-hidden="true">Hidden</span>' +
'</md-list-item>'
);
var buttonEl = listItem.find('button');
// The aria-label attribute should be determined from the content.
expect(buttonEl.attr('aria-label')).toBe('Content');
});
it('should determine the label from the bound content if aria-label is not set', function() {
var listItem = setup(
'<md-list-item ng-click="null">' +
'<span>{{ content }}</span>' +
'<span aria-hidden="true">Hidden</span>' +
'</md-list-item>'
);
$rootScope.$apply('content = "Content"');
var buttonEl = listItem.find('button');
// The aria-label attribute should be determined from the content.
expect(buttonEl.attr('aria-label')).toBe('Content');
});
it('should warn when label is missing and content is empty', inject(function($log) {
// Clear the log stack to assert that a new warning has been added.
$log.reset();
setup('<md-list-item ng-click="null">');
// Expect $log to have one $mdAria warning, because the button misses an aria-label.
expect($log.warn.logs.length).toBe(1);
}));
});
describe('with a clickable item', function() {
it('should wrap secondary icons in a md-button', function() {
var listItem = setup(
'<md-list-item ng-click="something()">' +
' <p>Content Here</p>' +
' <md-icon class="md-secondary" ng-click="heart()">heart</md-icon>' +
'</md-list-item>'
);
var buttons = listItem.find('button');
// Check that we wrapped the icon
expect(listItem[0].querySelector('button > md-icon')).toBeTruthy();
// Check the button actions
expect(buttons[0].attributes['ng-click'].value).toBe("something()");
expect(buttons[1].attributes['ng-click'].value).toBe("heart()");
});
it('should not wrap secondary buttons in a md-button', function() {
var listItem = setup(
'<md-list-item ng-click="something()">' +
' <p>Content Here</p>' +
' <button class="md-secondary" ng-click="like()">Like</button>' +
'</md-list-item>'
);
// There should be two buttons (the button executor, and the secondary item)
expect(listItem.find('button').length).toBe(2);
// Check that we didn't wrap the button in an md-button
expect(listItem[0].querySelector('md-button button.md-secondary')).toBeFalsy();
});
it('should not wrap secondary md-buttons in a md-button', function() {
var listItem = setup(
'<md-list-item ng-click="something()">' +
' <p>Content Here</p>' +
' <md-button class="md-secondary" ng-click="like()">Like</md-button>' +
'</md-list-item>'
);
// There should be 2 buttons that are siblings
expect(listItem.find('button').length).toBe(2);
// Check that we didn't wrap the md-button in an md-button
expect(listItem[0].querySelector('button.md-button button.md-button.md-secondary')).toBeFalsy();
});
});
});