UNPKG

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

649 lines (499 loc) 20 kB
describe('mdSidenav', function() { beforeEach(module('material.components.sidenav')); function setup(attrs, skipInitialDigest) { var el; inject(function($compile, $rootScope) { var parent = angular.element('<div>'); el = angular.element('<md-sidenav ' + (attrs || '') + '>'); parent.append(el); $compile(parent)($rootScope); !skipInitialDigest && $rootScope.$apply(); }); return el; } describe('directive', function() { it('should have `._md` class indicator', inject(function($rootScope) { var element = setup('md-is-open="show"'); $rootScope.$apply('show = true'); expect(element.hasClass('_md')).toBe(true); })); it('should bind isOpen attribute', inject(function($rootScope, $material) { var el = setup('md-is-open="show"'); $rootScope.$apply('show = true'); $material.flushOutstandingAnimations(); expect(el.hasClass('md-closed')).toBe(false); expect(el.parent().find('md-backdrop').length).toBe(1); $rootScope.$apply('show = false'); $material.flushOutstandingAnimations(); expect(el.hasClass('md-closed')).toBe(true); expect(el.parent().find('md-backdrop').length).toBe(0); })); it('should close on escape', inject(function($rootScope, $material, $mdConstant, $timeout) { var el = setup('md-is-open="show"'); $rootScope.$apply('show = true'); $material.flushOutstandingAnimations(); el.parent().triggerHandler({ type: 'keydown', keyCode: $mdConstant.KEY_CODE.ESCAPE }); $timeout.flush(); expect($rootScope.show).toBe(false); })); it('should close on backdrop click', inject(function($rootScope, $material, $timeout) { var el = setup('md-is-open="show"'); $rootScope.$apply('show = true'); $material.flushOutstandingAnimations(); el.parent().find('md-backdrop').triggerHandler('click'); $timeout.flush(); expect($rootScope.show).toBe(false); })); it('should show a backdrop by default', inject(function($rootScope, $material) { var el = setup('md-is-open="show"'); $rootScope.$apply('show = true'); $material.flushOutstandingAnimations(); var backdrop = el.parent().find('md-backdrop'); expect(backdrop.length).toBe(1); })); it('should not show a backdrop if md-disable-backdrop is set to true', inject(function($rootScope, $material) { var el = setup('md-is-open="show" md-disable-backdrop'); $rootScope.$apply('show = true'); $material.flushOutstandingAnimations(); var backdrop = el.parent().find('md-backdrop'); expect(backdrop.length).toBe(0); })); it('should focus sidenav on open', inject(function($rootScope, $material, $document) { jasmine.mockElementFocus(this); var el = setup('md-is-open="show"'); $rootScope.$apply('show = true'); $material.flushOutstandingAnimations(); expect($document.activeElement).toBe(el[0]); })); it('should focus child with md-sidenav-focus', inject(function($rootScope, $material, $document, $compile) { jasmine.mockElementFocus(this); var parent = angular.element('<div>'); var markup = '<md-sidenav md-is-open="show">' + ' <md-input-container><label>Label</label>' + ' <input type="text" md-sidenav-focus>' + ' </md-input-container>' + '<md-sidenav>'; var sidenavEl = angular.element(markup); parent.append(sidenavEl); $compile(parent)($rootScope); $rootScope.$apply('show = true'); var focusEl = sidenavEl.find('input'); $material.flushOutstandingAnimations(); expect($document.activeElement).toBe(focusEl[0]); })); it('should focus child with md-autofocus', inject(function($rootScope, $material, $document, $compile) { jasmine.mockElementFocus(this); var parent = angular.element('<div>'); var markup = '<md-sidenav md-is-open="show">' + '<md-input-container><label>Label</label>' + '<input type="text" md-autofocus>' + '</md-input-container>' + '<md-sidenav>'; var sidenavEl = angular.element(markup); parent.append(sidenavEl); $compile(parent)($rootScope); $rootScope.$apply('show = true'); var focusEl = sidenavEl.find('input'); $material.flushOutstandingAnimations(); expect($document.activeElement).toBe(focusEl[0]); })); it('should focus on last md-sidenav-focus element', inject(function($rootScope, $material, $document, $compile) { jasmine.mockElementFocus(this); var parent = angular.element('<div>'); var markup = '<md-sidenav md-is-open="show">' + '<md-button md-sidenav-focus>Button</md-button>' + '<md-input-container><label>Label</label>' + '<input type="text" md-sidenav-focus>' + '</md-input-container>' + '<md-sidenav>'; var sidenavEl = angular.element(markup); parent.append(sidenavEl); $compile(parent)($rootScope); $rootScope.$apply('show = true'); $material.flushOutstandingAnimations(); var focusEl = sidenavEl.find('input'); expect($document.activeElement).toBe(focusEl[0]); })); it('should lock open when is-locked-open is true', inject(function($rootScope, $material, $document) { var el = setup('md-is-open="show" md-is-locked-open="lock"'); expect(el.hasClass('md-locked-open')).toBe(false); $rootScope.$apply('lock = true'); expect(el.hasClass('md-locked-open')).toBe(true); $rootScope.$apply('show = true'); $material.flushOutstandingAnimations(); expect(el.parent().find('md-backdrop').hasClass('md-locked-open')).toBe(true); })); it('should expose $mdMedia service as $media local in is-locked-open attribute', function() { var mdMediaSpy = jasmine.createSpy('$mdMedia'); module(function($provide) { $provide.value('$mdMedia', mdMediaSpy); }); inject(function($rootScope, $animate, $document, $mdMedia) { var el = setup('md-is-locked-open="$mdMedia(123)"'); expect($mdMedia).toHaveBeenCalledWith(123); }); }); it('should trigger a resize event when opening', inject(function($rootScope, $animate, $$rAF, $window) { var el = setup('md-is-open="show"'); var obj = { callback: function() {} }; spyOn(obj, 'callback'); angular.element($window).on('resize', obj.callback); $rootScope.$apply('show = true'); $animate.flush(); $$rAF.flush(); expect(obj.callback).toHaveBeenCalled(); angular.element($window).off('resize', obj.callback); }) ); describe('parent scroll prevention', function() { it('should prevent scrolling on the parent element', inject(function($rootScope) { var parent = setup('md-is-open="isOpen"').parent()[0]; expect(parent.style.overflow).toBeFalsy(); $rootScope.$apply('isOpen = true'); expect(parent.style.overflow).toBe('hidden'); })); it('should prevent scrolling on a custom element', inject(function($compile, $rootScope) { var preventScrollTarget = angular.element('<div id="prevent-scroll-target"></div>'); var parent = angular.element( '<div>' + '<md-sidenav md-disable-scroll-target="#prevent-scroll-target" md-is-open="isOpen"></md-sidenav>' + '</div>' ); preventScrollTarget.append(parent); angular.element(document.body).append(preventScrollTarget); $compile(preventScrollTarget)($rootScope); expect(preventScrollTarget[0].style.overflow).toBeFalsy(); expect(parent[0].style.overflow).toBeFalsy(); $rootScope.$apply('isOpen = true'); expect(preventScrollTarget[0].style.overflow).toBe('hidden'); expect(parent[0].style.overflow).toBeFalsy(); preventScrollTarget.remove(); })); it('should log a warning and fall back to the parent if the custom scroll target does not exist', inject(function($rootScope, $log) { spyOn($log, 'warn'); var parent = setup('md-is-open="isOpen" md-disable-scroll-target="does-not-exist"').parent()[0]; $rootScope.$apply('isOpen = true'); expect($log.warn).toHaveBeenCalled(); expect(parent.style.overflow).toBe('hidden'); })); }); }); describe('controller', function() { it('should create controller', function() { var el = setup(); var controller = el.controller('mdSidenav'); expect(controller).not.toBe(undefined); }); it('should open and close and toggle', inject(function($timeout) { var el = setup(); var scope = el.isolateScope(); var controller = el.controller('mdSidenav'); // Should start closed expect(el.hasClass('md-closed')).toBe(true); controller.open(); scope.$apply(); expect(el.hasClass('md-closed')).toBe(false); controller.close(); scope.$apply(); expect(el.hasClass('md-closed')).toBe(true); controller.toggle(); scope.$apply(); expect(el.hasClass('md-closed')).toBe(false); })); }); describe("focus", function() { var $material, $mdInteraction, $mdConstant; var triggerElement; beforeEach(inject(function($injector) { $material = $injector.get('$material'); $mdInteraction = $injector.get('$mdInteraction'); $mdConstant = $injector.get('$mdInteraction'); triggerElement = angular.element('<button>Trigger Element</button>'); document.body.appendChild(triggerElement[0]); })); afterEach(function() { triggerElement.remove(); }); function dispatchEvent(eventName) { angular.element(document.body).triggerHandler(eventName); } function flush() { $material.flushInterimElement(); } function blur() { if ('documentMode' in document) { document.body.focus(); } else { triggerElement.blur(); } } it("should restore after sidenav triggered by keyboard", function() { var sidenavEl = setup(''); var controller = sidenavEl.controller('mdSidenav'); triggerElement.focus(); dispatchEvent('keydown'); controller.$toggleOpen(true); flush(); blur(); controller.$toggleOpen(false); flush(); expect($mdInteraction.getLastInteractionType()).toBe("keyboard"); expect(document.activeElement).toBe(triggerElement[0]); }); it("should not restore after sidenav triggered by mouse", function() { var sidenavEl = setup(''); var controller = sidenavEl.controller('mdSidenav'); triggerElement.focus(); dispatchEvent('mousedown'); controller.$toggleOpen(true); flush(); blur(); controller.$toggleOpen(false); flush(); expect($mdInteraction.getLastInteractionType()).toBe("mouse"); expect(document.activeElement).not.toBe(triggerElement[0]); }); }); describe("controller Promise API", function() { var $material, $rootScope, $timeout; function flush() { $material.flushInterimElement(); } beforeEach(inject(function(_$material_, _$rootScope_, _$timeout_) { $material = _$material_; $rootScope = _$rootScope_; $timeout = _$timeout_; })); it('should open(), close(), and toggle() with promises', function() { var el = setup(); var scope = el.isolateScope(); var controller = el.controller('mdSidenav'); var openDone = 0, closeDone = 0, toggleDone = 0; var onOpen = function() { openDone++; }; var onClose = function() { closeDone++; }; var onToggle = function() { toggleDone++; }; controller .open() .then(onOpen) .then(controller.close) .then(onClose); flush(); expect(openDone).toBe(1); flush(); expect(closeDone).toBe(1); controller .close() .then(onClose); flush(); expect(closeDone).toBe(2); expect(scope.isOpen).toBe(false); controller .toggle() .then(onToggle); flush(); expect(toggleDone).toBe(1); expect(scope.isOpen).toBe(true); }); it('should open() to work multiple times before close()', function() { var el = setup(); var controller = el.controller('mdSidenav'); var openDone = 0, closeDone = 0; var onOpen = function() { openDone++; }; var onClose = function() { closeDone++; }; controller .open() .then(onOpen) .then(controller.open) .then(onOpen); flush(); expect(openDone).toBe(2); expect(closeDone).toBe(0); expect(el.hasClass('md-closed')).toBe(false); controller .close() .then(onClose); flush(); expect(openDone).toBe(2); expect(closeDone).toBe(1); expect(el.hasClass('md-closed')).toBe(true); }); }); describe('$mdSidenav Service', function() { var $rootScope, $timeout; beforeEach(inject(function(_$rootScope_, _$timeout_) { $rootScope = _$rootScope_; $timeout = _$timeout_; })); it('should grab instance', inject(function($mdSidenav) { var el = setup('md-component-id="left"'); var scope = el.isolateScope(); var instance = $mdSidenav('left'); expect(instance).toBeTruthy(); instance.open(); scope.$apply(); expect(el.hasClass('md-closed')).toBe(false); instance.close(); scope.$apply(); expect(el.hasClass('md-closed')).toBe(true); instance.toggle(); scope.$apply(); expect(el.hasClass('md-closed')).toBe(false); instance.toggle(); scope.$apply(); expect(el.hasClass('md-closed')).toBe(true); })); it('exposes state', inject(function($mdSidenav) { var el = setup('md-component-id="stateTest" md-is-open="shouldOpen" md-is-locked-open="shouldLockOpen"'); var scope = el.scope(); var instance = $mdSidenav('stateTest'); expect(instance.isOpen()).toBe(false); expect(instance.isLockedOpen()).toBe(false); scope.shouldOpen = true; scope.shouldLockOpen = true; scope.$digest(); expect(instance.isOpen()).toBe(true); expect(instance.isLockedOpen()).toBe(true); scope.shouldOpen = false; scope.shouldLockOpen = true; scope.$digest(); expect(instance.isOpen()).toBe(false); expect(instance.isLockedOpen()).toBe(true); })); }); describe('$mdSidenav lookups', function() { var $rootScope, $timeout, $mdSidenav; beforeEach(inject(function(_$rootScope_, _$timeout_, _$mdSidenav_) { $rootScope = _$rootScope_; $timeout = _$timeout_; $mdSidenav = _$mdSidenav_; })); it('should find an instantiation using `$mdSidenav(id)`', function() { var el = setup('md-component-id="left"'); $timeout.flush(); // Lookup instance still available in the component registry var instance = $mdSidenav('left'); expect(instance).toBeTruthy(); }); it('should support data bindings', function() { // It should work on init. $rootScope.leftComponentId = 'left'; setup('md-component-id="{{ leftComponentId }}"', true); expect($mdSidenav($rootScope.leftComponentId, false)).toBeTruthy(); // It should also work if the data binding has changed. $rootScope.$apply('leftComponentId = "otherLeft"'); expect($mdSidenav($rootScope.leftComponentId, false)).toBeTruthy(); }); it('should find a deferred instantiation using `$mdSidenav(id, true)`', function() { var instance; // Lookup deferred (not existing) instance $mdSidenav('left', true).then(function(inst) { instance = inst; }); expect(instance).toBeUndefined(); // Instantiate `left` sidenav component var el = setup('md-component-id="left"'); $timeout.flush(); expect(instance).toBeDefined(); expect(instance.isOpen()).toBeFalsy(); // Lookup instance still available in the component registry instance = $mdSidenav('left', true); expect(instance).toBeTruthy(); }); it('should find a deferred instantiation using `$mdSidenav().waitFor(id)` ', function() { var instance; // Lookup deferred (not existing) instance $mdSidenav().waitFor('left').then(function(inst) { instance = inst; }); expect(instance).toBeUndefined(); // Instantiate `left` sidenav component var el = setup('md-component-id="left"'); $timeout.flush(); expect(instance).toBeDefined(); expect(instance.isOpen()).toBeFalsy(); // Lookup instance still available in the component registry instance = undefined; instance = $mdSidenav('left'); expect(instance).toBeTruthy(); }); it('should not find a lazy instantiation without waiting `$mdSidenav(id)`', function() { var instance = $mdSidenav('left'); expect(instance.isOpen).toBeDefined(); // returns legacy API with noops instance = $mdSidenav('left', false); // since enableWait == false, return false expect(instance).toBeFalsy(); // Instantiate `left` sidenav component var el = setup('md-component-id="left"'); $timeout.flush(); instance = $mdSidenav('left'); // returns instance expect(instance).toBeDefined(); expect(instance.isOpen()).toBeFalsy(); }); it('should not find a lazy instantiation without waiting `$mdSidenav().find(id)`', function() { var instance = $mdSidenav().find('left'); expect(instance).toBeUndefined(); // Instantiate `left` sidenav component var el = setup('md-component-id="left"'); $timeout.flush(); instance = $mdSidenav().find('left'); expect(instance).toBeDefined(); expect(instance.isOpen()).toBeFalsy(); }); describe('onClose', function () { it('should call callback on escape', inject(function($mdSidenav, $rootScope, $material, $mdConstant, $timeout) { var el = setup('md-component-id="left" md-is-open="show"'); var callback = jasmine.createSpy("callback spy"); $mdSidenav('left') .onClose(callback); $rootScope.$apply('show = true'); $material.flushOutstandingAnimations(); el.parent().triggerHandler({ type: 'keydown', keyCode: $mdConstant.KEY_CODE.ESCAPE }); $timeout.flush(); expect($rootScope.show).toBe(false); expect(callback).toHaveBeenCalled(); })); it('should call callback on backdrop click', inject(function($mdSidenav, $rootScope, $material, $timeout) { var el = setup('md-component-id="left" md-is-open="show"'); var callback = jasmine.createSpy("callback spy"); $mdSidenav('left') .onClose(callback); $rootScope.$apply('show = true'); $material.flushOutstandingAnimations(); el.parent().find('md-backdrop').triggerHandler('click'); $timeout.flush(); expect($rootScope.show).toBe(false); expect(callback).toHaveBeenCalled(); })); it('should call callback on close', inject(function($mdSidenav, $rootScope, $material, $timeout) { var el = setup('md-component-id="left"'); var callback = jasmine.createSpy("callback spy"); $mdSidenav('left') .onClose(callback) .open(); $timeout.flush(); expect(el.hasClass('md-closed')).toBe(false); $mdSidenav('left') .close(); $timeout.flush(); expect(callback).toHaveBeenCalled(); })); }); }); });