diff --git a/docs/accessibility/index.md b/docs/accessibility/index.md
index 5ba13f18..505ae9f8 100644
--- a/docs/accessibility/index.md
+++ b/docs/accessibility/index.md
@@ -23,6 +23,9 @@ rewritten:
Modal.setAppElement(document.getElementById('root'));
```
+Using a selector that matches multiple elements or passing a list of DOM
+elements will hide all of the elements.
+
If you are already applying the `aria-hidden` attribute to your app content
through other means, you can pass the `ariaHideApp={false}` prop to your modal
to avoid getting a warning that your app element is not specified.
diff --git a/specs/Modal.spec.js b/specs/Modal.spec.js
index d13f1fe3..6131db84 100644
--- a/specs/Modal.spec.js
+++ b/specs/Modal.spec.js
@@ -65,6 +65,16 @@ export default () => {
ReactDOM.unmountComponentAtNode(node);
});
+ it("accepts array of appElement as a prop", () => {
+ const el1 = document.createElement("div");
+ const el2 = document.createElement("div");
+ const node = document.createElement("div");
+ ReactDOM.render(, node);
+ el1.getAttribute("aria-hidden").should.be.eql("true");
+ el2.getAttribute("aria-hidden").should.be.eql("true");
+ ReactDOM.unmountComponentAtNode(node);
+ });
+
it("renders into the body, not in context", () => {
const node = document.createElement("div");
class App extends Component {
@@ -108,6 +118,36 @@ export default () => {
ReactDOM.unmountComponentAtNode(node);
});
+ // eslint-disable-next-line max-len
+ it("allow setting appElement of type string matching multiple elements", () => {
+ const el1 = document.createElement("div");
+ el1.id = "id1";
+ document.body.appendChild(el1);
+ const el2 = document.createElement("div");
+ el2.id = "id2";
+ document.body.appendChild(el2);
+ const node = document.createElement("div");
+ class App extends Component {
+ render() {
+ return (
+
+
+ hello
+
+
+ );
+ }
+ }
+ const appElement = "#id1, #id2";
+ Modal.setAppElement(appElement);
+ ReactDOM.render(, node);
+ el1.getAttribute("aria-hidden").should.be.eql("true");
+ el2.getAttribute("aria-hidden").should.be.eql("true");
+ ReactDOM.unmountComponentAtNode(node);
+ document.body.removeChild(el1);
+ document.body.removeChild(el2);
+ });
+
it("default parentSelector should be document.body.", () => {
const modal = renderModal({ isOpen: true });
modal.props.parentSelector().should.be.eql(document.body);
diff --git a/src/components/Modal.js b/src/components/Modal.js
index aeaea8dd..cb6bc53d 100644
--- a/src/components/Modal.js
+++ b/src/components/Modal.js
@@ -3,7 +3,11 @@ import ReactDOM from "react-dom";
import PropTypes from "prop-types";
import ModalPortal from "./ModalPortal";
import * as ariaAppHider from "../helpers/ariaAppHider";
-import SafeHTMLElement, { canUseDOM } from "../helpers/safeHTMLElement";
+import SafeHTMLElement, {
+ SafeNodeList,
+ SafeHTMLCollection,
+ canUseDOM
+} from "../helpers/safeHTMLElement";
import { polyfill } from "react-lifecycles-compat";
@@ -52,7 +56,12 @@ class Modal extends Component {
beforeClose: PropTypes.string.isRequired
})
]),
- appElement: PropTypes.instanceOf(SafeHTMLElement),
+ appElement: PropTypes.oneOfType([
+ PropTypes.instanceOf(SafeHTMLElement),
+ PropTypes.instanceOf(SafeHTMLCollection),
+ PropTypes.instanceOf(SafeNodeList),
+ PropTypes.arrayOf(PropTypes.instanceOf(SafeHTMLElement))
+ ]),
onAfterOpen: PropTypes.func,
onRequestClose: PropTypes.func,
closeTimeoutMS: PropTypes.number,
diff --git a/src/components/ModalPortal.js b/src/components/ModalPortal.js
index ca05eb8d..b7ac6cea 100644
--- a/src/components/ModalPortal.js
+++ b/src/components/ModalPortal.js
@@ -4,7 +4,10 @@ import * as focusManager from "../helpers/focusManager";
import scopeTab from "../helpers/scopeTab";
import * as ariaAppHider from "../helpers/ariaAppHider";
import * as classList from "../helpers/classList";
-import SafeHTMLElement from "../helpers/safeHTMLElement";
+import SafeHTMLElement, {
+ SafeHTMLCollection,
+ SafeNodeList
+} from "../helpers/safeHTMLElement";
import portalOpenInstances from "../helpers/portalOpenInstances";
import "../helpers/bodyTrap";
@@ -43,7 +46,12 @@ export default class ModalPortal extends Component {
bodyOpenClassName: PropTypes.string,
htmlOpenClassName: PropTypes.string,
ariaHideApp: PropTypes.bool,
- appElement: PropTypes.instanceOf(SafeHTMLElement),
+ appElement: PropTypes.oneOfType([
+ PropTypes.instanceOf(SafeHTMLElement),
+ PropTypes.instanceOf(SafeHTMLCollection),
+ PropTypes.instanceOf(SafeNodeList),
+ PropTypes.arrayOf(PropTypes.instanceOf(SafeHTMLElement))
+ ]),
onAfterOpen: PropTypes.func,
onAfterClose: PropTypes.func,
onRequestClose: PropTypes.func,
diff --git a/src/helpers/ariaAppHider.js b/src/helpers/ariaAppHider.js
index 017b17db..2ee47710 100644
--- a/src/helpers/ariaAppHider.js
+++ b/src/helpers/ariaAppHider.js
@@ -16,14 +16,21 @@ export function setElement(element) {
if (typeof useElement === "string" && canUseDOM) {
const el = document.querySelectorAll(useElement);
assertNodeList(el, useElement);
- useElement = "length" in el ? el[0] : el;
+ useElement = el;
}
globalElement = useElement || globalElement;
return globalElement;
}
export function validateElement(appElement) {
- if (!appElement && !globalElement) {
+ const el = appElement || globalElement;
+ if (el) {
+ return Array.isArray(el) ||
+ el instanceof HTMLCollection ||
+ el instanceof NodeList
+ ? el
+ : [el];
+ } else {
warning(
false,
[
@@ -35,21 +42,19 @@ export function validateElement(appElement) {
].join(" ")
);
- return false;
+ return [];
}
-
- return true;
}
export function hide(appElement) {
- if (validateElement(appElement)) {
- (appElement || globalElement).setAttribute("aria-hidden", "true");
+ for (let el of validateElement(appElement)) {
+ el.setAttribute("aria-hidden", "true");
}
}
export function show(appElement) {
- if (validateElement(appElement)) {
- (appElement || globalElement).removeAttribute("aria-hidden");
+ for (let el of validateElement(appElement)) {
+ el.removeAttribute("aria-hidden");
}
}
diff --git a/src/helpers/safeHTMLElement.js b/src/helpers/safeHTMLElement.js
index 5f650887..8ae67884 100644
--- a/src/helpers/safeHTMLElement.js
+++ b/src/helpers/safeHTMLElement.js
@@ -4,6 +4,10 @@ const EE = ExecutionEnvironment;
const SafeHTMLElement = EE.canUseDOM ? window.HTMLElement : {};
+export const SafeHTMLCollection = EE.canUseDOM ? window.HTMLCollection : {};
+
+export const SafeNodeList = EE.canUseDOM ? window.NodeList : {};
+
export const canUseDOM = EE.canUseDOM;
export default SafeHTMLElement;