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

Commit

Permalink
fix(modal): retain focus if child has focus
Browse files Browse the repository at this point in the history
- If a child element in the modal has focus before animation completes, do not refocus

Closes #4904
Fixes #4903
  • Loading branch information
Alex Knowles authored and wesleycho committed Nov 15, 2015
1 parent 6d5b1d9 commit 726ccc3
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 14 deletions.
32 changes: 19 additions & 13 deletions src/modal/modal.js
Expand Up @@ -105,8 +105,8 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap'])
}
}])

.directive('uibModalWindow', ['$uibModalStack', '$q', '$animate', '$animateCss',
function($modalStack, $q, $animate, $animateCss) {
.directive('uibModalWindow', ['$uibModalStack', '$q', '$animate', '$animateCss', '$document',
function($modalStack, $q, $animate, $animateCss, $document) {
return {
scope: {
index: '@'
Expand Down Expand Up @@ -172,19 +172,25 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap'])


$q.when(animationPromise).then(function() {
var inputWithAutofocus = element[0].querySelector('[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 something within the freshly-opened modal already has focus (perhaps via a
* directive that causes focus). then no need to try and focus anything.
*/
if (inputWithAutofocus) {
inputWithAutofocus.focus();
} else {
element[0].focus();
if (!($document[0].activeElement && element[0].contains($document[0].activeElement))) {
var inputWithAutofocus = element[0].querySelector('[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 (inputWithAutofocus) {
inputWithAutofocus.focus();
} else {
element[0].focus();
}
}
});

Expand Down
26 changes: 25 additions & 1 deletion src/modal/test/modal.spec.js
Expand Up @@ -22,6 +22,12 @@ describe('$uibModal', function () {
scope.text = ctrl.text;
}
};
}).directive('focusMe', function() {
return {
link: function(scope, elem, attrs) {
elem.focus();
}
};
});
}));

Expand Down Expand Up @@ -413,6 +419,24 @@ describe('$uibModal', function () {
openAndCloseModalWithAutofocusElement();
});

it('should not focus on the element that has autofocus attribute when the modal is opened and something in the modal already has focus and the animations have finished', function() {
function openAndCloseModalWithAutofocusElement() {

var modal = open({template: '<div><input type="text" id="auto-focus-element" autofocus><input type="text" id="pre-focus-element" focus-me></div>'});
$animate.flush();
$rootScope.$digest();
expect(angular.element('#auto-focus-element')).not.toHaveFocus();
expect(angular.element('#pre-focus-element')).toHaveFocus();

close(modal, 'closed ok');

expect(modal.result).toBeResolvedWith('closed ok');
}

openAndCloseModalWithAutofocusElement();
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: '<div><input type="text" id="auto-focus-element" autofocus></div>'});
Expand Down Expand Up @@ -846,7 +870,7 @@ describe('$uibModal', function () {
expect($document.find('.modal-backdrop')).not.toHaveClass('fade');
});
});

describe('appendTo', function() {
it('should be added to body by default', function() {
var modal = open({template: '<div>Content</div>'});
Expand Down

0 comments on commit 726ccc3

Please sign in to comment.