diff --git a/src/modal/modal.js b/src/modal/modal.js index b1f339dd84..4843490fad 100644 --- a/src/modal/modal.js +++ b/src/modal/modal.js @@ -203,13 +203,15 @@ angular.module('ui.bootstrap.modal', []) }); modalRenderDeferObj.promise.then(function() { + var animationPromise = null; + if (attrs.modalInClass) { if ($animateCss) { - $animateCss(element, { + animationPromise = $animateCss(element, { addClass: attrs.modalInClass }).start(); } else { - $animate.addClass(element, attrs.modalInClass); + animationPromise = $animate.addClass(element, attrs.modalInClass); } scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { @@ -224,20 +226,23 @@ angular.module('ui.bootstrap.modal', []) }); } - var inputsWithAutofocus = element[0].querySelectorAll('[autofocus]'); - /** - * Auto-focusing of a freshly-opened modal element causes any child elements - * with the autofocus attribute to lose focus. This is an issue on touch - * based devices which will show and then hide the onscreen keyboard. - * Attempts to refocus the autofocus element via JavaScript will not reopen - * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus - * the modal element if the modal does not contain an autofocus element. - */ - if (inputsWithAutofocus.length) { - inputsWithAutofocus[0].focus(); - } else { - element[0].focus(); - } + + $q.when(animationPromise).then(function() { + var inputsWithAutofocus = element[0].querySelectorAll('[autofocus]'); + /** + * Auto-focusing of a freshly-opened modal element causes any child elements + * with the autofocus attribute to lose focus. This is an issue on touch + * based devices which will show and then hide the onscreen keyboard. + * Attempts to refocus the autofocus element via JavaScript will not reopen + * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus + * the modal element if the modal does not contain an autofocus element. + */ + if (inputsWithAutofocus.length) { + inputsWithAutofocus[0].focus(); + } else { + element[0].focus(); + } + }); // Notify {@link $modalStack} that modal is rendered. var modal = $modalStack.getTop(); diff --git a/src/modal/test/modal.spec.js b/src/modal/test/modal.spec.js index bfe78eada0..66ae1a91fe 100644 --- a/src/modal/test/modal.spec.js +++ b/src/modal/test/modal.spec.js @@ -292,6 +292,8 @@ describe('$modal', function () { expect(document.activeElement.tagName).toBe('A'); var modal = open({template: '
Content
'}); + $animate.flush(); + $rootScope.$digest(); expect(document.activeElement.tagName).toBe('DIV'); expect($document).toHaveModalsOpen(1); @@ -315,6 +317,8 @@ describe('$modal', function () { expect(document.activeElement.tagName).toBe('A'); var modal = open({template: '
Content
'}); + $animate.flush(); + $rootScope.$digest(); expect(document.activeElement.tagName).toBe('DIV'); expect($document).toHaveModalsOpen(1); @@ -377,10 +381,11 @@ describe('$modal', function () { expect(modal.opened).toBeRejectedWith('ko'); }); - it('should focus on the element that has autofocus attribute when the modal is open/reopen', function() { + it('should focus on the element that has autofocus attribute when the modal is open/reopen and the animations have finished', function() { function openAndCloseModalWithAutofocusElement() { var modal = open({template: '
'}); - + $animate.flush(); + $rootScope.$digest(); expect(angular.element('#auto-focus-element')).toHaveFocus(); close(modal, 'closed ok'); @@ -392,6 +397,45 @@ describe('$modal', function () { openAndCloseModalWithAutofocusElement(); }); + it('should wait until the in animation is finished before attempting to focus the modal or autofocus element', function() { + function openAndCloseModalWithAutofocusElement() { + var modal = open({template: '
'}); + expect(angular.element('#auto-focus-element')).not.toHaveFocus(); + + $animate.flush(); + $rootScope.$digest(); + + expect(angular.element('#auto-focus-element')).toHaveFocus(); + + close(modal, 'closed ok'); + + expect(modal.result).toBeResolvedWith('closed ok'); + } + + function openAndCloseModalWithOutAutofocusElement() { + var link = 'Link'; + var element = angular.element(link); + angular.element(document.body).append(element); + element.focus(); + expect(document.activeElement.tagName).toBe('A'); + + var modal = open({template: '
'}); + expect(document.activeElement.tagName).toBe('A'); + + $animate.flush(); + $rootScope.$digest(); + + expect(document.activeElement.tagName).toBe('DIV'); + + close(modal, 'closed ok'); + + expect(modal.result).toBeResolvedWith('closed ok'); + } + + openAndCloseModalWithAutofocusElement(); + openAndCloseModalWithOutAutofocusElement(); + }); + it('should change focus to first element when tab key was pressed', function() { var initialPage = angular.element('Outland link'); angular.element(document.body).append(initialPage); @@ -918,11 +962,15 @@ describe('$modal', function () { expect(document.activeElement.tagName).toBe('A'); var modal1 = open({template: '
Modal1
'}); + $animate.flush(); + $rootScope.$digest(); document.getElementById('focus').focus(); expect(document.activeElement.tagName).toBe('BUTTON'); expect($document).toHaveModalsOpen(1); var modal2 = open({template: '
Modal2
'}); + $animate.flush(); + $rootScope.$digest(); expect(document.activeElement.tagName).toBe('DIV'); expect($document).toHaveModalsOpen(2);