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

Commit

Permalink
fix(modal): filter out non-tabbable elements
Browse files Browse the repository at this point in the history
- Ensure non-tabbable elements with taxindex of -1 are not tabbed to

Closes #5963
Fixes #5955
  • Loading branch information
atrinh authored and wesleycho committed Jun 9, 2016
1 parent 90fa2f6 commit 35ced04
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 4 deletions.
8 changes: 4 additions & 4 deletions src/modal/modal.js
Expand Up @@ -261,9 +261,9 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
var previousTopOpenedModal = null;

//Modal focus behavior
var tabableSelector = 'a[href], area[href], input:not([disabled]), ' +
'button:not([disabled]),select:not([disabled]), textarea:not([disabled]), ' +
'iframe, object, embed, *[tabindex], *[contenteditable=true]';
var tabbableSelector = 'a[href], area[href], input:not([disabled]):not([tabindex=\'-1\']), ' +
'button:not([disabled]):not([tabindex=\'-1\']),select:not([disabled]):not([tabindex=\'-1\']), textarea:not([disabled]):not([tabindex=\'-1\']), ' +
'iframe, object, embed, *[tabindex]:not([tabindex=\'-1\']), *[contenteditable=true]';
var scrollbarPadding;

function isVisible(element) {
Expand Down Expand Up @@ -599,7 +599,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
if (modalWindow) {
var modalDomE1 = modalWindow.value.modalDomEl;
if (modalDomE1 && modalDomE1.length) {
var elements = modalDomE1[0].querySelectorAll(tabableSelector);
var elements = modalDomE1[0].querySelectorAll(tabbableSelector);
return elements ?
Array.prototype.filter.call(elements, function(element) {
return isVisible(element);
Expand Down
46 changes: 46 additions & 0 deletions src/modal/test/modal.spec.js
Expand Up @@ -807,6 +807,52 @@ describe('$uibModal', function() {

initialPage.remove();
});

it('should change focus to next tabbable element when tab is pressed', function() {
var initialPage = angular.element('<a href="#" id="cannot-get-focus-from-modal">Outland link</a>');
angular.element(document.body).append(initialPage);
initialPage.focus();

open({
template:'<button id="tab-focus-button1" tabindex="-1">Skip me!</button><a href="#" id="tab-focus-link1">a</a>' +
'<a href="#" id="tab-focus-link2">b</a><a href="#" id="tab-focus-link3">c</a>' +
'<button id="tab-focus-button2" tabindex="-1">Skip me!</button>',
keyboard: false
});
$rootScope.$digest();
expect($document).toHaveModalsOpen(1);

$('#tab-focus-link3').focus();
expect(document.activeElement.getAttribute('id')).toBe('tab-focus-link3');

triggerKeyDown(angular.element(document.activeElement), 9, false);
expect(document.activeElement.getAttribute('id')).toBe('tab-focus-link1');

initialPage.remove();
});

it('should change focus to previous tabbable element when shift+tab is pressed', function() {
var initialPage = angular.element('<a href="#" id="cannot-get-focus-from-modal">Outland link</a>');
angular.element(document.body).append(initialPage);
initialPage.focus();

open({
template:'<button id="tab-focus-button1" tabindex="-1">Skip me!</button><a href="#" id="tab-focus-link1">a</a>' +
'<a href="#" id="tab-focus-link2">b</a><a href="#" id="tab-focus-link3">c</a>' +
'<button id="tab-focus-button2" tabindex="-1">Skip me!</button>',
keyboard: false
});
$rootScope.$digest();
expect($document).toHaveModalsOpen(1);

$('#tab-focus-link1').focus();
expect(document.activeElement.getAttribute('id')).toBe('tab-focus-link1');

triggerKeyDown(angular.element(document.activeElement), 9, true);
expect(document.activeElement.getAttribute('id')).toBe('tab-focus-link3');

initialPage.remove();
});
});

describe('default options can be changed in a provider', function() {
Expand Down

0 comments on commit 35ced04

Please sign in to comment.