diff --git a/src/components/dialog/dialog.js b/src/components/dialog/dialog.js index 1cdeb3bcc63..891e53cb4d4 100644 --- a/src/components/dialog/dialog.js +++ b/src/components/dialog/dialog.js @@ -489,15 +489,15 @@ function MdDialogProvider($$interimElementProvider) { * Remove function for all dialogs */ function onRemove(scope, element, options) { - angular.element($document[0].body).removeClass('md-dialog-is-showing'); - options.deactivateListeners(); options.unlockScreenReader(); - options.hideBackdrop(); return dialogPopOut(element, options) - .then(function () { + .finally(function () { + angular.element($document[0].body).removeClass('md-dialog-is-showing'); + options.hideBackdrop(); element.remove(); + options.origin.focus(); }); } @@ -531,6 +531,13 @@ function MdDialogProvider($$interimElementProvider) { */ function activateListeners(element, options) { var removeListeners = [ ]; + var smartClose = function() { + // Only 'confirm' dialogs have a cancel button... escape/clickOutside will + // cancel or fallback to hide. + var closeFn = ( options.$type == 'alert' ) ? $mdDialog.hide : $mdDialog.cancel; + + $mdUtil.nextTick( closeFn, true ); + }; if (options.escapeToClose) { var target = options.parent; @@ -539,7 +546,7 @@ function MdDialogProvider($$interimElementProvider) { ev.stopPropagation(); ev.preventDefault(); - $mdUtil.nextTick($mdDialog.cancel); + smartClose(); } }; @@ -561,7 +568,7 @@ function MdDialogProvider($$interimElementProvider) { ev.stopPropagation(); ev.preventDefault(); - $mdUtil.nextTick($mdDialog.cancel); + smartClose(); } }; diff --git a/src/components/dialog/dialog.spec.js b/src/components/dialog/dialog.spec.js index d9e8a9067cc..e24dcf9dea2 100644 --- a/src/components/dialog/dialog.spec.js +++ b/src/components/dialog/dialog.spec.js @@ -104,6 +104,40 @@ describe('$mdDialog', function() { expect($document.activeElement).toBe(parent[0].querySelector('md-dialog-content')); })); + + it('should remove `md-dialog-container` on click and remove', inject(function($mdDialog, $rootScope, $timeout) { + jasmine.mockElementFocus(this); + var container, parent = angular.element('
'); + + $mdDialog.show( + $mdDialog.alert({ + template: + '' + + '' + + '

Muppets are the best

' + + '
' + + '
', + parent: parent, + clickOutsideToClose: true + }) + ); + + $rootScope.$apply(); + triggerTransitionEnd( parent.find('md-dialog') ); + + container = angular.element(parent[0].querySelector('.md-dialog-container')); + container.triggerHandler({ + type: 'click', + target: container[0] + }); + + $timeout.flush(); + triggerTransitionEnd( parent.find('md-dialog') ); + + container = angular.element(parent[0].querySelector('.md-dialog-container')); + expect(container.length).toBe(0); + })); + }); describe('#confirm()', function() { @@ -167,7 +201,7 @@ describe('$mdDialog', function() { '' + '
' + '', - parent: parent + parent: parent, }); $rootScope.$apply(); @@ -175,6 +209,79 @@ describe('$mdDialog', function() { expect($document.activeElement).toBe(parent[0].querySelector('.dialog-close')); })); + + it('should remove `md-dialog-container` after click outside', inject(function($mdDialog, $rootScope, $timeout) { + jasmine.mockElementFocus(this); + var container, parent = angular.element('
'); + + $mdDialog.show( + $mdDialog.confirm({ + template: + '' + + '' + + '

Muppets are the best

' + + '
' + + '
', + parent: parent, + clickOutsideToClose: true, + ok : 'OK', + cancel : 'CANCEL' + }) + ); + + $rootScope.$apply(); + triggerTransitionEnd( parent.find('md-dialog') ); + + container = angular.element(parent[0].querySelector('.md-dialog-container')); + container.triggerHandler({ + type: 'click', + target: container[0] + }); + + $timeout.flush(); + triggerTransitionEnd( parent.find('md-dialog') ); + + container = angular.element(parent[0].querySelector('.md-dialog-container')); + expect(container.length).toBe(0); + })); + + it('should remove `md-dialog-container` after ESCAPE key', inject(function($mdDialog, $rootScope, $timeout, $mdConstant) { + jasmine.mockElementFocus(this); + var container, parent = angular.element('
'); + var response; + + $mdDialog.show( + $mdDialog.confirm({ + template: + '' + + '' + + '

Muppets are the best

' + + '
' + + '
', + parent: parent, + clickOutsideToClose: true, + escapeToClose: true, + ok : 'OK', + cancel : 'CANCEL' + }) + ).catch(function(reason){ + response = reason; + }); + + $rootScope.$apply(); + triggerTransitionEnd( parent.find('md-dialog') ); + + parent.triggerHandler({type: 'keyup', + keyCode: $mdConstant.KEY_CODE.ESCAPE + }); + $timeout.flush(); + triggerTransitionEnd( parent.find('md-dialog') ); + + container = angular.element(parent[0].querySelector('.md-dialog-container')); + + expect(container.length).toBe(0); + expect(response).toBe(false); + })); }); describe('#build()', function() { @@ -331,7 +438,11 @@ describe('$mdDialog', function() { var container = angular.element(parent[0].querySelector('.md-dialog-container')); - container.triggerHandler('click'); + container.triggerHandler({ + type: 'click', + target: container[0] + }); + $timeout.flush(); $animate.triggerCallbacks(); diff --git a/src/core/services/interimElement/interimElement.js b/src/core/services/interimElement/interimElement.js index 626665a01bc..a3d30db44fe 100644 --- a/src/core/services/interimElement/interimElement.js +++ b/src/core/services/interimElement/interimElement.js @@ -258,7 +258,7 @@ function InterimElementProvider() { */ function show(options) { if (stack.length) { - return service.cancel().then(function() { + return service.cancel().finally(function() { return show(options); }); } else { @@ -314,6 +314,7 @@ function InterimElementProvider() { .remove() .then(function() { interimElement.deferred.reject(reason); + return interimElement.deferred.promise; }); } @@ -417,7 +418,7 @@ function InterimElementProvider() { // Trigger onRemoving callback *before* the remove operation starts (options.onRemoving || angular.noop)(options.scope, element); - return $q.when(ret).then(function() { + return $q.when(ret).finally(function() { if (!options.preserveScope) options.scope.$destroy(); removeDone = true; }); diff --git a/src/core/services/interimElement/interimElement.spec.js b/src/core/services/interimElement/interimElement.spec.js index 4449ae09488..b13f594accc 100644 --- a/src/core/services/interimElement/interimElement.spec.js +++ b/src/core/services/interimElement/interimElement.spec.js @@ -46,10 +46,10 @@ describe('$$interimElement service', function() { it('should not call onShow or onRemove on failing to load templates', function() { createInterimProvider('interimTest'); inject(function($q, $rootScope, $rootElement, interimTest, $httpBackend, $animate) { - $compilerSpy.and.callFake(function() { - var deferred = $q.defer(); - deferred.reject(); - return deferred.promise; + $compilerSpy.and.callFake(function(reason) { + return $q(function(resolve,reject){ + reject(reason || false); + }); }); $httpBackend.when('GET', '/fail-url.html').respond(500, ''); var onShowCalled = false, onHideCalled = false; @@ -59,8 +59,9 @@ describe('$$interimElement service', function() { onRemove: function() { onHideCalled = true; } }); $animate.triggerCallbacks(); - interimTest.hide(); + interimTest.cancel(); $animate.triggerCallbacks(); + expect(onShowCalled).toBe(false); expect(onHideCalled).toBe(false); }); @@ -501,13 +502,13 @@ describe('$$interimElement service', function() { $compilerSpy.and.callFake(function(opts) { var el = $compile(opts.template); - var deferred = $q.defer(); - deferred.resolve({ - link: el, - locals: {} + return $q(function(resolve){ + resolve({ + link: el, + locals: {} + }); + !$rootScope.$$phase && $rootScope.$apply(); }); - !$rootScope.$$phase && $rootScope.$apply(); - return deferred.promise; }); }); } diff --git a/src/core/util/animate.js b/src/core/util/animate.js index eab615ea491..aeaa725f92e 100644 --- a/src/core/util/animate.js +++ b/src/core/util/animate.js @@ -52,7 +52,7 @@ function AnimateDomUtils($mdUtil, $$rAF, $q, $timeout, $mdConstant) { * Announce completion or failure via promise handlers */ waitTransitionEnd: function (element, opts) { - var TIMEOUT = 10000; // fallback is 10 secs + var TIMEOUT = 3000; // fallback is 3 secs return $q(function(resolve, reject){ opts = opts || { };