From cb17c461f0549d9cfbac9741f0a6e78882db52e3 Mon Sep 17 00:00:00 2001 From: Jeff Browning Date: Mon, 5 Oct 2015 16:33:25 -0400 Subject: [PATCH 1/2] feat(modal): Add $modalInstance.reposition() This function recalculates and resets the top position of the modal window. This can be useful when the content of the modal can dynamically change its height. --- src/modal/docs/demo.html | 1 + src/modal/docs/demo.js | 6 +++++- src/modal/docs/readme.md | 1 + src/modal/modal.js | 29 +++++++++++++++++++++++------ src/modal/test/modal.spec.js | 31 ++++++++++++++++++++----------- 5 files changed, 50 insertions(+), 18 deletions(-) diff --git a/src/modal/docs/demo.html b/src/modal/docs/demo.html index 3788c85..59cdd93 100644 --- a/src/modal/docs/demo.html +++ b/src/modal/docs/demo.html @@ -7,6 +7,7 @@

I'm a modal!

Selected: {{ selected.item }}

+ × diff --git a/src/modal/docs/demo.js b/src/modal/docs/demo.js index 5a7a2e4..f4c342d 100644 --- a/src/modal/docs/demo.js +++ b/src/modal/docs/demo.js @@ -32,6 +32,10 @@ angular.module('foundationDemoApp').controller('ModalInstanceCtrl', function ($s item: $scope.items[0] }; + $scope.reposition = function () { + $modalInstance.reposition(); + }; + $scope.ok = function () { $modalInstance.close($scope.selected.item); }; @@ -39,4 +43,4 @@ angular.module('foundationDemoApp').controller('ModalInstanceCtrl', function ($s $scope.cancel = function () { $modalInstance.dismiss('cancel'); }; -}); \ No newline at end of file +}); diff --git a/src/modal/docs/readme.md b/src/modal/docs/readme.md index a4f721a..63764fe 100644 --- a/src/modal/docs/readme.md +++ b/src/modal/docs/readme.md @@ -17,6 +17,7 @@ The `open` method returns a modal instance, an object with the following propert * `close(result)` - a method that can be used to close a modal, passing a result * `dismiss(reason)` - a method that can be used to dismiss a modal, passing a reason +* `reposition()` - a method that can be used to re-calculate the top position of the modal * `result` - a promise that is resolved when a modal is closed and rejected when a modal is dismissed * `opened` - a promise that is resolved when a modal gets opened after downloading content's template and resolving all variables diff --git a/src/modal/modal.js b/src/modal/modal.js index 902b23c..b705bed 100644 --- a/src/modal/modal.js +++ b/src/modal/modal.js @@ -118,7 +118,7 @@ angular.module('mm.foundation.modal', ['mm.foundation.transition']) var OPENED_MODAL_CLASS = 'modal-open'; - var backdropDomEl, backdropScope; + var backdropDomEl, backdropScope, cssTop; var openedWindows = $$stackedMap.createNew(); var $modalStack = {}; @@ -199,6 +199,14 @@ angular.module('mm.foundation.modal', ['mm.foundation.transition']) } } + function computeModalMarginTop(modalElement, offset) { + if (angular.isUndefined(offset)) { + offset = 0; + } + var scrollY = $window.pageYOffset || 0; + return offset + scrollY; + } + $document.bind('keydown', function (evt) { var modal; @@ -235,13 +243,10 @@ angular.module('mm.foundation.modal', ['mm.foundation.transition']) // distance to top var faux = angular.element('
'); body.append(faux[0]); - var marginTop = parseInt(getComputedStyle(faux[0]).top) || 0; + cssTop = parseInt(getComputedStyle(faux[0]).top) || 0; + var openAt = computeModalMarginTop(faux, cssTop); faux.remove(); - // Using pageYOffset instead of scrollY to ensure compatibility with IE - var scrollY = $window.pageYOffset || 0; - var openAt = scrollY + marginTop; - var angularDomEl = angular.element('
') .attr({ 'window-class': modal.windowClass, @@ -256,6 +261,15 @@ angular.module('mm.foundation.modal', ['mm.foundation.transition']) body.addClass(OPENED_MODAL_CLASS); }; + $modalStack.reposition = function (modalInstance) { + var modalWindow = openedWindows.get(modalInstance).value; + if (modalWindow) { + var modalDomEl = modalWindow.modalDomEl; + var top = computeModalMarginTop(modalDomEl, cssTop); + modalDomEl.css('top', top + "px"); + } + }; + $modalStack.close = function (modalInstance, result) { var modalWindow = openedWindows.get(modalInstance).value; if (modalWindow) { @@ -330,6 +344,9 @@ angular.module('mm.foundation.modal', ['mm.foundation.transition']) }, dismiss: function (reason) { $modalStack.dismiss(modalInstance, reason); + }, + reposition: function () { + $modalStack.reposition(modalInstance); } }; diff --git a/src/modal/test/modal.spec.js b/src/modal/test/modal.spec.js index 5cf6a6e..32b568d 100644 --- a/src/modal/test/modal.spec.js +++ b/src/modal/test/modal.spec.js @@ -151,16 +151,14 @@ describe('$modal', function () { } describe('modal invoked with y offsets', function () { + it('should create the modal at the correct location based on window y position', function () { + $window.pageYOffset = 400; - it('should create the modal at the correct location based on window y position', function () { - $window.pageYOffset = 400; - - var modal = open({template: '
Content
'}); - expect($document).toHaveModalsOpen(1); - expect($document).toHaveModalOpenWithStyle('top', '400px'); - }); - - }); + var modal = open({template: '
Content
'}); + expect($document).toHaveModalsOpen(1); + expect($document).toHaveModalOpenWithStyle('top', '400px'); + }); + }); describe('basic scenarios with default options', function () { @@ -265,12 +263,23 @@ describe('$modal', function () { $timeout.flush(); expect($rootScope.$$childTail).toEqual(null); }); + + describe("$modalInstance.reposition()", function() { + it('should re-calculate the modal margin top', function () { + $window.pageYOffset = 400; + var modal = open({template: '
Content
'}); + expect($document).toHaveModalOpenWithStyle('top', '400px'); + + $window.pageYOffset = 500; + modal.reposition(); + expect($document).toHaveModalOpenWithStyle('top', '500px'); + }); + }); }); describe('default options can be changed in a provider', function () { - + it('should allow overriding default options in a provider', function () { - $modalProvider.options.backdrop = false; var modal = open({template: '
Content
'}); From 3029c24f4f585e7081e8c72ace41408224ede76c Mon Sep 17 00:00:00 2001 From: Jeff Browning Date: Tue, 13 Oct 2015 14:30:46 -0400 Subject: [PATCH 2/2] chore(modal): Refactor repositioning :lipstick: --- src/modal/docs/demo.html | 2 +- src/modal/modal.js | 8 ++++---- src/modal/test/modal.spec.js | 15 ++++++++++----- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/modal/docs/demo.html b/src/modal/docs/demo.html index 59cdd93..b24e55c 100644 --- a/src/modal/docs/demo.html +++ b/src/modal/docs/demo.html @@ -7,7 +7,7 @@

I'm a modal!

Selected: {{ selected.item }}

- + × diff --git a/src/modal/modal.js b/src/modal/modal.js index 238d872..9e02fb3 100644 --- a/src/modal/modal.js +++ b/src/modal/modal.js @@ -199,7 +199,7 @@ angular.module('mm.foundation.modal', ['mm.foundation.transition']) } } - function computeModalMarginTop(modalElement, offset) { + function calculateModalTop(modalElement, offset) { if (angular.isUndefined(offset)) { offset = 0; } @@ -244,8 +244,8 @@ angular.module('mm.foundation.modal', ['mm.foundation.transition']) // distance to top var faux = angular.element('
'); parent.append(faux[0]); - cssTop = parseInt(getComputedStyle(faux[0]).top) || 0; - var openAt = computeModalMarginTop(faux, cssTop); + cssTop = parseInt($window.getComputedStyle(faux[0]).top) || 0; + var openAt = calculateModalTop(faux, cssTop); faux.remove(); var angularDomEl = angular.element('
') @@ -266,7 +266,7 @@ angular.module('mm.foundation.modal', ['mm.foundation.transition']) var modalWindow = openedWindows.get(modalInstance).value; if (modalWindow) { var modalDomEl = modalWindow.modalDomEl; - var top = computeModalMarginTop(modalDomEl, cssTop); + var top = calculateModalTop(modalDomEl, cssTop); modalDomEl.css('top', top + "px"); } }; diff --git a/src/modal/test/modal.spec.js b/src/modal/test/modal.spec.js index 5deee1c..c34efba 100644 --- a/src/modal/test/modal.spec.js +++ b/src/modal/test/modal.spec.js @@ -1,7 +1,7 @@ describe('$modal', function () { var $rootScope, $document, $compile, $templateCache, $timeout, $q, $window, $provide; var $modal, $modalProvider; - var mockWindow = {}; + var mockWindow, mockComputedStyle; var triggerKeyDown = function (element, keyCode) { var e = $.Event("keydown"); @@ -27,13 +27,18 @@ describe('$modal', function () { var mockdocument = angular.element(document); $provide.value('$document', mockdocument); - mockwindow = { + mockComputedStyle = { + top: 0 + }; + + mockWindow = { location: "val", document: mockdocument, pageYOffset: 4, - this_is_a_mock_window: true + this_is_a_mock_window: true, + getComputedStyle: jasmine.createSpy("$window.getComputedStyle").andReturn(mockComputedStyle) }; - $provide.value('$window', mockwindow); + $provide.value('$window', mockWindow); })); @@ -317,7 +322,7 @@ describe('$modal', function () { }); describe('default options can be changed in a provider', function () { - + it('should allow overriding default options in a provider', function () { $modalProvider.options.backdrop = false; var modal = open({template: '
Content
'});