From 22c34badbcac032543210218d6caaacfb96de1f6 Mon Sep 17 00:00:00 2001 From: Thomas Burleson Date: Thu, 27 Aug 2015 18:28:36 -0500 Subject: [PATCH] fix(dialog): improve support for template and templateUrl options Using $mdDialog to show html content without a mdDialog parent node will throw an error: ```js $mdDialog.show({ targetEvent: ev, controller : DialogCtrl, parent : angular.element(document.body), template : '
click here
' } ``` If a mdDialog template does not contain a wrapper `md-dialog` node, then auto-wrap. If a `$mdUtil.extractElementByName` does not find the target element, warn the user. Fixes #3191. Fixes #4206. --- src/components/dialog/dialog.js | 10 ++- src/components/dialog/dialog.spec.js | 47 +++++++++++--- src/components/sidenav/sidenav.scss | 91 ++++++---------------------- src/core/services/aria/aria.js | 3 +- src/core/services/aria/aria.spec.js | 17 ++++++ src/core/util/util.js | 4 +- 6 files changed, 91 insertions(+), 81 deletions(-) diff --git a/src/components/dialog/dialog.js b/src/components/dialog/dialog.js index 10121554dc9..c6e5772c1eb 100644 --- a/src/components/dialog/dialog.js +++ b/src/components/dialog/dialog.js @@ -447,7 +447,15 @@ function MdDialogProvider($$interimElementProvider) { focusOnOpen: true, disableParentScroll: true, transformTemplate: function(template) { - return '
' + template + '
'; + return '
' + validatedTemplate(template) + '
'; + + /** + * The specified template should contain a wrapper element.... + */ + function validatedTemplate(template) { + template || "" + return /<\/md-dialog>/g.test(template) ? template : "" + template + ""; + } } }; diff --git a/src/components/dialog/dialog.spec.js b/src/components/dialog/dialog.spec.js index fb8db593063..2841c7ca9e8 100644 --- a/src/components/dialog/dialog.spec.js +++ b/src/components/dialog/dialog.spec.js @@ -348,6 +348,39 @@ describe('$mdDialog', function() { expect(closing).toBe(true); })); + + it('should not wrap content with existing md-dialog', inject(function($mdDialog, $rootScope) { + + var template = '
Hello
'; + var parent = angular.element('
'); + + $mdDialog.show({ + template: template, + parent: parent + }); + + $rootScope.$apply(); + + var container = parent[0].querySelectorAll('md-dialog'); + expect(container.length).toBe(1); + })); + + it('should wrap raw content with md-dialog', inject(function($mdDialog, $rootScope) { + + var template = '
Hello
'; + var parent = angular.element('
'); + + $mdDialog.show({ + template: template, + parent: parent + }); + + $rootScope.$apply(); + + var container = parent[0].querySelectorAll('md-dialog'); + expect(container.length).toBe(1); + })); + it('should append dialog with container', inject(function($mdDialog, $rootScope) { var template = 'Hello'; @@ -367,7 +400,7 @@ describe('$mdDialog', function() { it('should escapeToClose == true', inject(function($mdDialog, $rootScope, $rootElement, $timeout, $animate, $mdConstant) { var parent = angular.element('
'); $mdDialog.show({ - template: '', + template: '', parent: parent, escapeToClose: true }); @@ -391,7 +424,7 @@ describe('$mdDialog', function() { it('should escapeToClose == false', inject(function($mdDialog, $rootScope, $rootElement, $timeout, $animate, $mdConstant) { var parent = angular.element('
'); $mdDialog.show({ - template: '', + template: '', parent: parent, escapeToClose: false }); @@ -411,7 +444,7 @@ describe('$mdDialog', function() { var parent = angular.element('
'); $mdDialog.show({ - template: '', + template: '', parent: parent, clickOutsideToClose: true }); @@ -434,7 +467,7 @@ describe('$mdDialog', function() { var parent = angular.element('
'); $mdDialog.show({ - template: '', + template: '', parent: parent, clickOutsideToClose: false }); @@ -458,7 +491,7 @@ describe('$mdDialog', function() { spyOn($mdUtil, 'disableScrollAround'); var parent = angular.element('
'); $mdDialog.show({ - template: '', + template: '', parent: parent, disableParentScroll: true }); @@ -469,7 +502,7 @@ describe('$mdDialog', function() { it('should hasBackdrop == true', inject(function($mdDialog, $animate, $rootScope) { var parent = angular.element('
'); $mdDialog.show({ - template: '', + template: '', parent: parent, hasBackdrop: true }); @@ -482,7 +515,7 @@ describe('$mdDialog', function() { it('should hasBackdrop == false', inject(function($mdDialog, $rootScope) { var parent = angular.element('
'); $mdDialog.show({ - template: '', + template: '', parent: parent, hasBackdrop: false }); diff --git a/src/components/sidenav/sidenav.scss b/src/components/sidenav/sidenav.scss index 6a8bc2a0d8d..7d622d9e467 100644 --- a/src/components/sidenav/sidenav.scss +++ b/src/components/sidenav/sidenav.scss @@ -1,16 +1,26 @@ $sidenav-default-width: 304px !default; $sidenav-min-space: 56px !default; +.md-sidenav-left { + transform: translate3d(-100%, 0, 0); +} + md-sidenav { box-sizing: border-box; - position: absolute; + position: relative; flex-direction: column; z-index: $z-index-sidenav; + top:0; + bottom: 0; + left:0; width: $sidenav-default-width; min-width: $sidenav-default-width; max-width: $sidenav-default-width; - bottom: 0; + + display:none; + transition: 0s ease-out transform; + background-color: white; overflow: auto; @@ -18,84 +28,26 @@ md-sidenav { list-style: none; } - &.md-closed { - display: none; - } - &.md-closed-add, - &.md-closed-remove { - display: flex; - transition: 0.2s ease-in all; - } - - &.md-closed-add.md-closed-add-active, - &.md-closed-remove.md-closed-remove-active { - transition: $swift-ease-out; - } &.md-locked-open-add, &.md-locked-open-remove { - position: static; - display: flex; - transform: translate3d(0, 0, 0); + display: block; } &.md-locked-open { - width: $sidenav-default-width; - min-width: $sidenav-default-width; - max-width: $sidenav-default-width; - } + display: block; + transition-duration: 0.2s; - &.md-locked-open, - &.md-locked-open.md-closed, - &.md-locked-open.md-closed.md-sidenav-left, - &.md-locked-open.md-closed.md-sidenav-right, - &.md-locked-open-remove.md-closed { - position: static; - display: flex; - transform: translate3d(0, 0, 0); - } - &.md-locked-open-remove-active { - transition: width $swift-ease-in-duration $swift-ease-in-timing-function, - min-width $swift-ease-in-duration $swift-ease-in-timing-function; - width: 0; - min-width: 0; - } - - &.md-closed.md-locked-open-add { - width: 0; - min-width: 0; - transform: translate3d(0%, 0, 0); - } - - &.md-closed.md-locked-open-add-active { - transition: width $swift-ease-in-duration $swift-ease-in-timing-function, - min-width $swift-ease-in-duration $swift-ease-in-timing-function; - width: $sidenav-default-width; - min-width: $sidenav-default-width; - transform: translate3d(0%, 0, 0); + &.md-sidenav-left { + transform: translate3d(0, 0, 0); + } } @extend .md-sidenav-left; } -.md-sidenav-backdrop.md-locked-open { - display: none; -} -.md-sidenav-left { - left: 0; - top: 0; - transform: translate3d(0%, 0, 0); - &.md-closed { - transform: translate3d(-100%, 0, 0); - } -} -.md-sidenav-right { - left: 100%; - top: 0; - transform: translate3d(-100%, 0, 0); - &.md-closed { - transform: translate3d(0%, 0, 0); - } +.md-sidenav-backdrop.md-locked-open { + display: none; } @media (max-width: $sidenav-default-width + $sidenav-min-space) { @@ -108,7 +60,4 @@ md-sidenav { .md-sidenav-left { border-right: 1px solid #fff; } - .md-sidenav-right { - border-left: 1px solid #fff; - } } diff --git a/src/core/services/aria/aria.js b/src/core/services/aria/aria.js index 501fa6fadc5..7c736032624 100644 --- a/src/core/services/aria/aria.js +++ b/src/core/services/aria/aria.js @@ -20,7 +20,8 @@ function AriaService($$rAF, $log, $window) { * @param {optional} defaultValue What to set the attr to if no value is found */ function expect(element, attrName, defaultValue) { - var node = element[0] || element; + + var node = angular.element(element)[0] || element; // if node exists and neither it nor its children have the attribute if (node && diff --git a/src/core/services/aria/aria.spec.js b/src/core/services/aria/aria.spec.js index 83de396ff2a..32a0a5a38b7 100644 --- a/src/core/services/aria/aria.spec.js +++ b/src/core/services/aria/aria.spec.js @@ -2,6 +2,14 @@ describe('$mdAria service', function() { beforeEach(module('material.core')); describe('expecting attributes', function(){ + it('should warn if an invalid element is specified', inject(function($compile, $rootScope, $log, $mdAria) { + spyOn($log, 'warn'); + var target = $compile('
')($rootScope); + + $mdAria.expect(null,'aria-label'); + expect($log.warn).not.toHaveBeenCalled(); + })); + it('should warn if element is missing attribute', inject(function($compile, $rootScope, $log, $mdAria) { spyOn($log, 'warn'); var button = $compile('')($rootScope); @@ -20,6 +28,15 @@ describe('$mdAria service', function() { expect($log.warn).toHaveBeenCalled(); })); + it('should warn if element is emtpry attribute', inject(function($compile, $rootScope, $log, $mdAria) { + spyOn($log, 'warn'); + var button = $compile('')($rootScope); + + $mdAria.expect(button, 'aria-label'); + + expect($log.warn).toHaveBeenCalled(); + })); + it('should not warn if child element has attribute', inject(function($compile, $rootScope, $log, $mdAria) { spyOn($log, 'warn'); var button = $compile('')($rootScope); diff --git a/src/core/util/util.js b/src/core/util/util.js index 6f56afe2d99..64ce504953d 100644 --- a/src/core/util/util.js +++ b/src/core/util/util.js @@ -10,7 +10,7 @@ angular .module('material.core') .factory('$mdUtil', UtilFactory); -function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $interpolate) { +function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $interpolate, $log) { // Setup some core variables for the processTemplate method var startSymbol = $interpolate.startSymbol(), endSymbol = $interpolate.endSymbol(), @@ -462,6 +462,8 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in return angular.element(element[i]); } } + + $log.warn( $mdUtil.supplant("Unable to find node '{0}' in element.",[nodeName]) ); return element; },