diff --git a/packages/dialog/__tests__/dialog.test.tsx b/packages/dialog/__tests__/dialog.test.tsx
index 31e8ce471..afc195617 100644
--- a/packages/dialog/__tests__/dialog.test.tsx
+++ b/packages/dialog/__tests__/dialog.test.tsx
@@ -4,7 +4,7 @@ import { useFakeTimers, SinonFakeTimers } from "sinon";
import { axe, toHaveNoViolations } from "jest-axe";
import { fireEvent, render, act, userEvent } from "$test/utils";
import { AxeResults } from "$test/types";
-import { Dialog } from "@reach/dialog";
+import { Dialog, DialogProps } from "@reach/dialog";
function getOverlay(container: Element) {
return container.querySelector("[data-reach-dialog-overlay]");
@@ -58,6 +58,39 @@ describe("", () => {
);
expect(label).toHaveTextContent("I am the title now");
});
+
+ it("ARIA hides ancestors by default", async () => {
+ const { findByTestId } = render(
+ <>
+
Hidden
+
+ >
+ );
+ const sibling = await findByTestId("sibling");
+ expect(isAriaHidden(sibling)).toBe(true);
+ });
+
+ it("ARIA hides ancestors when ariaHideAncestors is true", async () => {
+ const { findByTestId } = render(
+ <>
+ Hidden
+
+ >
+ );
+ const sibling = await findByTestId("sibling");
+ expect(isAriaHidden(sibling)).toBe(true);
+ });
+
+ it("doesn't ARIA hide ancestors when ariaHideAncestors is false", async () => {
+ const { findByTestId } = render(
+ <>
+ Not hidden
+
+ >
+ );
+ const sibling = await findByTestId("sibling");
+ expect(isAriaHidden(sibling)).toBe(false);
+ });
});
describe("user events", () => {
@@ -83,7 +116,7 @@ describe("", () => {
});
});
-function BasicOpenDialog() {
+function BasicOpenDialog(props: DialogProps) {
const [showDialog, setShowDialog] = useState(true);
return (
@@ -92,6 +125,7 @@ function BasicOpenDialog() {
aria-label="Announcement"
isOpen={showDialog}
onDismiss={() => setShowDialog(false)}
+ {...props}
>
@@ -102,3 +136,18 @@ function BasicOpenDialog() {
);
}
+
+/**
+ * Checks the element and its ancestors for an aria-hidden attribute.
+ * If an aria-hidden is found, we assume it is set to true, which should be a
+ * reasonable assumption for the purposes of these tests.
+ */
+function isAriaHidden(el: HTMLElement | null) {
+ while (el) {
+ if (el.hasAttribute("aria-hidden")) {
+ return true;
+ }
+ el = el.parentElement;
+ }
+ return false;
+}
diff --git a/packages/dialog/src/index.tsx b/packages/dialog/src/index.tsx
index 31c009716..251171f00 100644
--- a/packages/dialog/src/index.tsx
+++ b/packages/dialog/src/index.tsx
@@ -28,6 +28,7 @@ const overlayPropTypes = {
initialFocusRef: () => null,
allowPinchZoom: PropTypes.bool,
onDismiss: PropTypes.func,
+ ariaHideAncestors: PropTypes.bool,
};
////////////////////////////////////////////////////////////////////////////////
@@ -92,6 +93,7 @@ const DialogInner = forwardRef
(
onDismiss = noop,
onMouseDown,
onKeyDown,
+ ariaHideAncestors = true,
...props
},
forwardedRef
@@ -124,11 +126,11 @@ const DialogInner = forwardRef(
mouseDownTarget.current = event.target;
}
- useEffect(
- () =>
- overlayNode.current ? createAriaHider(overlayNode.current) : void null,
- []
- );
+ useEffect(() => {
+ if (overlayNode.current && ariaHideAncestors) {
+ createAriaHider(overlayNode.current);
+ }
+ }, [ariaHideAncestors]);
return (
@@ -225,7 +227,14 @@ if (__DEV__) {
* @see Docs https://reacttraining.com/reach-ui/dialog#dialog
*/
export const Dialog = forwardRef(function Dialog(
- { isOpen, onDismiss = noop, initialFocusRef, allowPinchZoom, ...props },
+ {
+ isOpen,
+ onDismiss = noop,
+ initialFocusRef,
+ allowPinchZoom,
+ ariaHideAncestors = true,
+ ...props
+ },
forwardedRef
) {
return (
@@ -234,6 +243,7 @@ export const Dialog = forwardRef(function Dialog(
allowPinchZoom={allowPinchZoom}
isOpen={isOpen}
onDismiss={onDismiss}
+ ariaHideAncestors={ariaHideAncestors}
>
@@ -265,6 +275,13 @@ export type DialogProps = {
* @see Docs https://reacttraining.com/reach-ui/dialog#dialog-ondismiss
*/
onDismiss?: (event?: React.SyntheticEvent) => void;
+ /**
+ * By default, all of the nodes at the document.body root hav aria-hidden set
+ * on them, except for the currently active dialog.
+ *
+ * @see Docs https://reacttraining.com/reach-ui/dialog#aria-hiding-other-elements
+ */
+ ariaHideAncestors?: boolean;
/**
* Accepts any renderable content.
*
@@ -287,6 +304,7 @@ if (__DEV__) {
onDismiss: PropTypes.func,
"aria-label": ariaLabelType,
"aria-labelledby": ariaLabelType,
+ ariaHideAncestors: PropTypes.bool,
};
}