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

Commit

Permalink
fix(modal): Improve ARIA support by adding a live region.
Browse files Browse the repository at this point in the history
chore(build): Fix package.json so grunt-cli does not need to be globally installed.
chore(demo): Add command to run demo locally.
  • Loading branch information
nickheiner-usds committed Aug 25, 2016
1 parent 9881a27 commit ec671af
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ addons:
before_install:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- npm install --quiet -g grunt-cli karma
- npm install --quiet -g karma

script: grunt
sudo: false
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
],
"main": "index.js",
"scripts": {
"demo": "grunt after-test && static dist -a 0.0.0.0 -H '{\"Cache-Control\": \"no-cache, must-revalidate\"}'",
"test": "grunt"
},
"repository": {
Expand All @@ -26,6 +27,7 @@
"angular-mocks": "1.5.8",
"angular-sanitize": "1.5.8",
"grunt": "^0.4.5",
"grunt-cli": "^1.2.0",
"grunt-contrib-concat": "^1.0.0",
"grunt-contrib-copy": "^1.0.0",
"grunt-contrib-uglify": "^1.0.1",
Expand All @@ -44,6 +46,7 @@
"load-grunt-tasks": "^3.3.0",
"lodash": "^4.1.0",
"marked": "^0.3.5",
"node-static": "^0.7.8",
"semver": "^5.0.1",
"shelljs": "^0.6.0"
},
Expand Down
2 changes: 1 addition & 1 deletion src/modal/docs/readme.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
`$uibModal` is a service to create modal windows.
Creating modals is straightforward: create a template, a controller and reference them when using `$uibModal`.
Creating modals is straightforward: create a template and controller, and reference them when using `$uibModal`.

The `$uibModal` service has only one method: `open(options)`.

Expand Down
51 changes: 49 additions & 2 deletions src/modal/modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
// {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}.
scope.$isRendered = true;

// Deferred object that will be resolved when this modal is render.
// Deferred object that will be resolved when this modal is rendered.
var modalRenderDeferObj = $q.defer();
// Resolve render promise post-digest
scope.$$postDigest(function() {
Expand Down Expand Up @@ -254,6 +254,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
};
var topModalIndex = 0;
var previousTopOpenedModal = null;
var ARIA_HIDDEN_ATTRIBUTE_NAME = 'data-bootstrap-modal-aria-hidden-count';

//Modal focus behavior
var tabbableSelector = 'a[href], area[href], input:not([disabled]):not([tabindex=\'-1\']), ' +
Expand Down Expand Up @@ -529,6 +530,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
'role': 'dialog',
'aria-labelledby': modal.ariaLabelledBy,
'aria-describedby': modal.ariaDescribedBy,
'aria-live': 'polite',
'size': modal.size,
'index': topModalIndex,
'animate': 'animate',
Expand All @@ -555,20 +557,65 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p

openedWindows.top().value.modalDomEl = angularDomEl;
openedWindows.top().value.modalOpener = modalOpener;

applyAriaHidden(angularDomEl[0]);

function applyAriaHidden(el) {
if (!el || el.tagName === 'BODY') {
return;
}

getSiblings(el).forEach(function(sibling) {
var elemIsAlreadyHidden = sibling.getAttribute('aria-hidden') === 'true',
ariaHiddenCount =
parseInt(sibling.getAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME)) ||
elemIsAlreadyHidden ? 1 : 0;

sibling.setAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME, ariaHiddenCount + 1);
sibling.setAttribute('aria-hidden', 'true');
});

return applyAriaHidden(el.parentElement);

function getSiblings(el) {
var children = el.parentElement ? el.parentElement.children : [];

return Array.prototype.filter.call(children, function(child) {
return child !== el;
});
}
}
};

function broadcastClosing(modalWindow, resultOrReason, closing) {
return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented;
}

$modalStack.close = function(modalInstance, result) {
var modalWindow = openedWindows.get(modalInstance);
var modalWindow;

Array.prototype.forEach.call(
document.querySelectorAll('[' + ARIA_HIDDEN_ATTRIBUTE_NAME + ']'),
function(hiddenEl) {
var ariaHiddenCount = parseInt(hiddenEl.getAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME)),
newHiddenCount = ariaHiddenCount - 1;
hiddenEl.setAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME, newHiddenCount);

if (!newHiddenCount) {
hiddenEl.removeAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME);
hiddenEl.removeAttribute('aria-hidden');
}
}
);

modalWindow = openedWindows.get(modalInstance);
if (modalWindow && broadcastClosing(modalWindow, result, true)) {
modalWindow.value.modalScope.$$uibDestructionScheduled = true;
modalWindow.value.deferred.resolve(result);
removeModalWindow(modalInstance, modalWindow.value.modalOpener);
return true;
}

return !modalWindow;
};

Expand Down
10 changes: 10 additions & 0 deletions src/modal/test/modal.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1586,6 +1586,16 @@ describe('$uibModal', function() {
expect($document.find('.modal').attr('aria-describedby')).toEqual('modal-description');
});
});

describe('ariaLive', function() {
it('should add the aria-live property to the modal', function() {
open({
template: '<p>Modal content</p>'
});

expect($document.find('.modal').attr('aria-live')).toEqual('polite');
});
});
});

describe('modal window', function() {
Expand Down

0 comments on commit ec671af

Please sign in to comment.