Skip to content
This repository was archived by the owner on May 29, 2019. It is now read-only.

Commit 937a1f3

Browse files
wawyedwesleycho
authored andcommitted
fix(modal): Wait for animation before focus.
- focus modal only after animation completes Closes #4300 Fixes #4274
1 parent 5a44eb2 commit 937a1f3

File tree

2 files changed

+71
-18
lines changed

2 files changed

+71
-18
lines changed

src/modal/modal.js

+21-16
Original file line numberDiff line numberDiff line change
@@ -203,13 +203,15 @@ angular.module('ui.bootstrap.modal', [])
203203
});
204204

205205
modalRenderDeferObj.promise.then(function() {
206+
var animationPromise = null;
207+
206208
if (attrs.modalInClass) {
207209
if ($animateCss) {
208-
$animateCss(element, {
210+
animationPromise = $animateCss(element, {
209211
addClass: attrs.modalInClass
210212
}).start();
211213
} else {
212-
$animate.addClass(element, attrs.modalInClass);
214+
animationPromise = $animate.addClass(element, attrs.modalInClass);
213215
}
214216

215217
scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) {
@@ -224,20 +226,23 @@ angular.module('ui.bootstrap.modal', [])
224226
});
225227
}
226228

227-
var inputsWithAutofocus = element[0].querySelectorAll('[autofocus]');
228-
/**
229-
* Auto-focusing of a freshly-opened modal element causes any child elements
230-
* with the autofocus attribute to lose focus. This is an issue on touch
231-
* based devices which will show and then hide the onscreen keyboard.
232-
* Attempts to refocus the autofocus element via JavaScript will not reopen
233-
* the onscreen keyboard. Fixed by updated the focusing logic to only autofocus
234-
* the modal element if the modal does not contain an autofocus element.
235-
*/
236-
if (inputsWithAutofocus.length) {
237-
inputsWithAutofocus[0].focus();
238-
} else {
239-
element[0].focus();
240-
}
229+
230+
$q.when(animationPromise).then(function() {
231+
var inputsWithAutofocus = element[0].querySelectorAll('[autofocus]');
232+
/**
233+
* Auto-focusing of a freshly-opened modal element causes any child elements
234+
* with the autofocus attribute to lose focus. This is an issue on touch
235+
* based devices which will show and then hide the onscreen keyboard.
236+
* Attempts to refocus the autofocus element via JavaScript will not reopen
237+
* the onscreen keyboard. Fixed by updated the focusing logic to only autofocus
238+
* the modal element if the modal does not contain an autofocus element.
239+
*/
240+
if (inputsWithAutofocus.length) {
241+
inputsWithAutofocus[0].focus();
242+
} else {
243+
element[0].focus();
244+
}
245+
});
241246

242247
// Notify {@link $modalStack} that modal is rendered.
243248
var modal = $modalStack.getTop();

src/modal/test/modal.spec.js

+50-2
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,8 @@ describe('$modal', function () {
292292
expect(document.activeElement.tagName).toBe('A');
293293

294294
var modal = open({template: '<div>Content<button>inside modal</button></div>'});
295+
$animate.flush();
296+
$rootScope.$digest();
295297
expect(document.activeElement.tagName).toBe('DIV');
296298
expect($document).toHaveModalsOpen(1);
297299

@@ -315,6 +317,8 @@ describe('$modal', function () {
315317
expect(document.activeElement.tagName).toBe('A');
316318

317319
var modal = open({template: '<div>Content</div>'});
320+
$animate.flush();
321+
$rootScope.$digest();
318322
expect(document.activeElement.tagName).toBe('DIV');
319323
expect($document).toHaveModalsOpen(1);
320324

@@ -377,10 +381,11 @@ describe('$modal', function () {
377381
expect(modal.opened).toBeRejectedWith('ko');
378382
});
379383

380-
it('should focus on the element that has autofocus attribute when the modal is open/reopen', function() {
384+
it('should focus on the element that has autofocus attribute when the modal is open/reopen and the animations have finished', function() {
381385
function openAndCloseModalWithAutofocusElement() {
382386
var modal = open({template: '<div><input type="text" id="auto-focus-element" autofocus></div>'});
383-
387+
$animate.flush();
388+
$rootScope.$digest();
384389
expect(angular.element('#auto-focus-element')).toHaveFocus();
385390

386391
close(modal, 'closed ok');
@@ -392,6 +397,45 @@ describe('$modal', function () {
392397
openAndCloseModalWithAutofocusElement();
393398
});
394399

400+
it('should wait until the in animation is finished before attempting to focus the modal or autofocus element', function() {
401+
function openAndCloseModalWithAutofocusElement() {
402+
var modal = open({template: '<div><input type="text" id="auto-focus-element" autofocus></div>'});
403+
expect(angular.element('#auto-focus-element')).not.toHaveFocus();
404+
405+
$animate.flush();
406+
$rootScope.$digest();
407+
408+
expect(angular.element('#auto-focus-element')).toHaveFocus();
409+
410+
close(modal, 'closed ok');
411+
412+
expect(modal.result).toBeResolvedWith('closed ok');
413+
}
414+
415+
function openAndCloseModalWithOutAutofocusElement() {
416+
var link = '<a href>Link</a>';
417+
var element = angular.element(link);
418+
angular.element(document.body).append(element);
419+
element.focus();
420+
expect(document.activeElement.tagName).toBe('A');
421+
422+
var modal = open({template: '<div><input type="text"></div>'});
423+
expect(document.activeElement.tagName).toBe('A');
424+
425+
$animate.flush();
426+
$rootScope.$digest();
427+
428+
expect(document.activeElement.tagName).toBe('DIV');
429+
430+
close(modal, 'closed ok');
431+
432+
expect(modal.result).toBeResolvedWith('closed ok');
433+
}
434+
435+
openAndCloseModalWithAutofocusElement();
436+
openAndCloseModalWithOutAutofocusElement();
437+
});
438+
395439
it('should change focus to first element when tab key was pressed', function() {
396440
var initialPage = angular.element('<a href="#" id="cannot-get-focus-from-modal">Outland link</a>');
397441
angular.element(document.body).append(initialPage);
@@ -918,11 +962,15 @@ describe('$modal', function () {
918962
expect(document.activeElement.tagName).toBe('A');
919963

920964
var modal1 = open({template: '<div>Modal1<button id="focus">inside modal1</button></div>'});
965+
$animate.flush();
966+
$rootScope.$digest();
921967
document.getElementById('focus').focus();
922968
expect(document.activeElement.tagName).toBe('BUTTON');
923969
expect($document).toHaveModalsOpen(1);
924970

925971
var modal2 = open({template: '<div>Modal2</div>'});
972+
$animate.flush();
973+
$rootScope.$digest();
926974
expect(document.activeElement.tagName).toBe('DIV');
927975
expect($document).toHaveModalsOpen(2);
928976

0 commit comments

Comments
 (0)