From 2acc9259754eb5076cdbb7d694dea776cdcfef94 Mon Sep 17 00:00:00 2001 From: Chris Chua Date: Sun, 15 Dec 2013 23:04:56 -0800 Subject: [PATCH] fix(modal): Support close animation Tests now have to wait for animation before checking for open modals. Tests should actually check for the 'in' class. Setting up both the transition end handler and a timeout ensures that the modal is always closed even if there was an issue with the transition. This was the implementation used by Twitter Bootstrap modal JS code. Note that unbinding the transition end event within the event handler isn't necessary, and, worse is currently buggy in jqLite (see https://github.com/angular/angular.js/pull/5109 ). Note that the scope is already destroyed when the dom is removed so the $destroy call isn't needed. Closes #1341 --- src/modal/modal.js | 33 ++++++++++++++++++++++++++------- src/modal/test/modal.spec.js | 5 ++++- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/modal/modal.js b/src/modal/modal.js index 12ddfa8ee4..a78db01194 100644 --- a/src/modal/modal.js +++ b/src/modal/modal.js @@ -1,4 +1,4 @@ -angular.module('ui.bootstrap.modal', []) +angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition']) /** * A helper, internal data structure that acts as a map but also allows getting / removing @@ -106,8 +106,8 @@ angular.module('ui.bootstrap.modal', []) }; }]) - .factory('$modalStack', ['$document', '$compile', '$rootScope', '$$stackedMap', - function ($document, $compile, $rootScope, $$stackedMap) { + .factory('$modalStack', ['$transition', '$timeout', '$document', '$compile', '$rootScope', '$$stackedMap', + function ($transition, $timeout, $document, $compile, $rootScope, $$stackedMap) { var OPENED_MODAL_CLASS = 'modal-open'; @@ -140,17 +140,36 @@ angular.module('ui.bootstrap.modal', []) openedWindows.remove(modalInstance); //remove window DOM element - modalWindow.modalDomEl.remove(); + removeAfterAnimating(modalWindow.modalDomEl, modalWindow.modalScope); body.toggleClass(OPENED_MODAL_CLASS, openedWindows.length() > 0); //remove backdrop if no longer needed if (backdropDomEl && backdropIndex() == -1) { - backdropDomEl.remove(); + removeAfterAnimating(backdropDomEl, backdropScope); backdropDomEl = undefined; } + } - //destroy scope - modalWindow.modalScope.$destroy(); + function removeAfterAnimating(domEl, scope) { + // Closing animation + $timeout(function () { + scope.animate = false; + }); + + var transitionEndEventName = $transition.transitionEndEventName; + if (transitionEndEventName) { + // transition out + var timeout = $timeout(function () { + domEl.remove(); + }, 500); + + domEl.bind(transitionEndEventName, function () { + $timeout.cancel(timeout); + domEl.remove(); + }); + } else { + domEl.remove(); + } } $document.bind('keydown', function (evt) { diff --git a/src/modal/test/modal.spec.js b/src/modal/test/modal.spec.js index 697eec6861..a75126df33 100644 --- a/src/modal/test/modal.spec.js +++ b/src/modal/test/modal.spec.js @@ -1,4 +1,4 @@ -describe('$modal', function () { +ddescribe('$modal', function () { var $rootScope, $document, $compile, $templateCache, $timeout, $q; var $modal, $modalProvider; @@ -104,6 +104,7 @@ describe('$modal', function () { function dismiss(modal, reason) { modal.dismiss(reason); + $timeout.flush(); $rootScope.$digest(); } @@ -144,6 +145,7 @@ describe('$modal', function () { expect($document).toHaveModalsOpen(1); triggerKeyDown($document, 27); + $timeout.flush(); $rootScope.$digest(); expect($document).toHaveModalsOpen(0); @@ -155,6 +157,7 @@ describe('$modal', function () { expect($document).toHaveModalsOpen(1); $document.find('body > div.modal-backdrop').click(); + $timeout.flush(); $rootScope.$digest(); expect($document).toHaveModalsOpen(0);