From a08ad700f6bdb2c54246bb42b71f5bf4dd7f144f Mon Sep 17 00:00:00 2001 From: Pem Taira Date: Mon, 4 Apr 2016 08:29:46 +0100 Subject: [PATCH] fix(modal): ensure correct index is set - Fixes index set to avoid potential concurrent z-index values for multiple modals Closes #5733 Fixes #5670 --- src/modal/modal.js | 20 ++++++- src/modal/test/modal.spec.js | 101 +++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 1 deletion(-) diff --git a/src/modal/modal.js b/src/modal/modal.js index e28d39187a..1d93a8a5f1 100644 --- a/src/modal/modal.js +++ b/src/modal/modal.js @@ -257,6 +257,8 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p var $modalStack = { NOW_CLOSING_EVENT: 'modal.stack.now-closing' }; + var topModalIndex = 0; + var previousTopOpenedModal = null; //Modal focus behavior var tabableSelector = 'a[href], area[href], input:not([disabled]), ' + @@ -278,6 +280,12 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p topBackdropIndex = i; } } + + // If any backdrop exist, ensure that it's index is always + // right below the top modal + if (topBackdropIndex > -1 && topBackdropIndex < topModalIndex) { + topBackdropIndex = topModalIndex; + } return topBackdropIndex; } @@ -293,6 +301,10 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p //clean up the stack openedWindows.remove(modalInstance); + previousTopOpenedModal = openedWindows.top(); + if (previousTopOpenedModal) { + topModalIndex = parseInt(previousTopOpenedModal.value.modalDomEl.attr('index'), 10); + } removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() { var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS; @@ -432,6 +444,10 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p modalBodyClass = modal.openedClass || OPENED_MODAL_CLASS; toggleTopWindowClass(false); + + // Store the current top first, to determine what index we ought to use + // for the current top modal + previousTopOpenedModal = openedWindows.top(); openedWindows.add(modalInstance, { deferred: modal.deferred, @@ -468,13 +484,15 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p $animate.enter(backdropDomEl, appendToElement); } + // Set the top modal index based on the index of the previous top modal + topModalIndex = previousTopOpenedModal ? parseInt(previousTopOpenedModal.value.modalDomEl.attr('index'), 10) + 1 : 0; var angularDomEl = angular.element('
'); angularDomEl.attr({ 'template-url': modal.windowTemplateUrl, 'window-class': modal.windowClass, 'window-top-class': modal.windowTopClass, 'size': modal.size, - 'index': openedWindows.length() - 1, + 'index': topModalIndex, 'animate': 'animate' }).html(modal.content); if (modal.animation) { diff --git a/src/modal/test/modal.spec.js b/src/modal/test/modal.spec.js index 8aa87ce9f9..8de6c8497a 100644 --- a/src/modal/test/modal.spec.js +++ b/src/modal/test/modal.spec.js @@ -1615,6 +1615,107 @@ describe('$uibModal', function() { expect($document.find('div.modal1')).toHaveClass('modal-top'); expect($document).toHaveModalsOpen(1); }); + + it('should have top modal with highest index', function() { + var modal2Index = null; + var modal3Index = null; + + var modal1Instance = { + result: $q.defer(), + opened: $q.defer(), + closed: $q.defer(), + rendered: $q.defer(), + close: function(result) { + return $uibModalStack.close(modal1Instance, result); + }, + dismiss: function(reason) { + return $uibModalStack.dismiss(modal1Instance, reason); + } + }; + var modal2Instance = { + result: $q.defer(), + opened: $q.defer(), + closed: $q.defer(), + rendered: $q.defer(), + close: function(result) { + return $uibModalStack.close(modal2Instance, result); + }, + dismiss: function(reason) { + return $uibModalStack.dismiss(modal2Instance, reason); + } + }; + var modal3Instance = { + result: $q.defer(), + opened: $q.defer(), + closed: $q.defer(), + rendered: $q.defer(), + close: function(result) { + return $uibModalStack.close(modal13nstance, result); + }, + dismiss: function(reason) { + return $uibModalStack.dismiss(modal3Instance, reason); + } + }; + + var modal1 = $uibModalStack.open(modal1Instance, { + appendTo: angular.element(document.body), + scope: $rootScope.$new(), + deferred: modal1Instance.result, + renderDeferred: modal1Instance.rendered, + closedDeferred: modal1Instance.closed, + content: '
Modal1
' + }); + + expect($document).toHaveModalsOpen(0); + $rootScope.$digest(); + $animate.flush(); + expect($document).toHaveModalsOpen(1); + + expect(parseInt($uibModalStack.getTop().value.modalDomEl.attr('index'), 10)).toEqual(0); + + var modal2 = $uibModalStack.open(modal2Instance, { + appendTo: angular.element(document.body), + scope: $rootScope.$new(), + deferred: modal2Instance.result, + renderDeferred: modal2Instance.rendered, + closedDeferred: modal2Instance.closed, + content: '
Modal2
' + }); + + modal2Instance.rendered.promise.then(function() { + modal2Index = parseInt($uibModalStack.getTop().value.modalDomEl.attr('index'), 10); + }); + + expect($document).toHaveModalsOpen(1); + $rootScope.$digest(); + $animate.flush(); + expect($document).toHaveModalsOpen(2); + + expect(modal2Index).toEqual(1); + close(modal1Instance); + expect($document).toHaveModalsOpen(1); + + var modal3 = $uibModalStack.open(modal3Instance, { + appendTo: angular.element(document.body), + scope: $rootScope.$new(), + deferred: modal3Instance.result, + renderDeferred: modal3Instance.rendered, + closedDeferred: modal3Instance.closed, + content: '
Modal3
' + }); + + modal3Instance.rendered.promise.then(function() { + modal3Index = parseInt($uibModalStack.getTop().value.modalDomEl.attr('index'), 10); + }); + + expect($document).toHaveModalsOpen(1); + $rootScope.$digest(); + $animate.flush(); + expect($document).toHaveModalsOpen(2); + + expect(modal3Index).toEqual(2); + expect(modal2Index).toBeLessThan(modal3Index); + }); }); describe('modal.closing event', function() {