Skip to content

Commit

Permalink
dialog: added support for the ariaHideAncestors prop.
Browse files Browse the repository at this point in the history
  • Loading branch information
cannona committed Apr 6, 2020
1 parent e95268b commit 324bdc6
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 8 deletions.
53 changes: 51 additions & 2 deletions packages/dialog/__tests__/dialog.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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]");
Expand Down Expand Up @@ -58,6 +58,39 @@ describe("<Dialog />", () => {
);
expect(label).toHaveTextContent("I am the title now");
});

it("ARIA hides ancestors by default", async () => {
const { findByTestId } = render(
<>
<div data-testid="sibling">Hidden</div>
<BasicOpenDialog />
</>
);
const sibling = await findByTestId("sibling");
expect(isAriaHidden(sibling)).toBe(true);
});

it("ARIA hides ancestors when ariaHideAncestors is true", async () => {
const { findByTestId } = render(
<>
<div data-testid="sibling">Hidden</div>
<BasicOpenDialog ariaHideAncestors={true} />
</>
);
const sibling = await findByTestId("sibling");
expect(isAriaHidden(sibling)).toBe(true);
});

it("doesn't ARIA hide ancestors when ariaHideAncestors is false", async () => {
const { findByTestId } = render(
<>
<div data-testid="sibling">Not hidden</div>
<BasicOpenDialog ariaHideAncestors={false} />
</>
);
const sibling = findByTestId("sibling");
expect(isAriaHidden(sibling)).toBe(false);
});
});

describe("user events", () => {
Expand All @@ -83,7 +116,7 @@ describe("<Dialog />", () => {
});
});

function BasicOpenDialog() {
function BasicOpenDialog(props: DialogProps) {
const [showDialog, setShowDialog] = useState(true);
return (
<div>
Expand All @@ -92,6 +125,7 @@ function BasicOpenDialog() {
aria-label="Announcement"
isOpen={showDialog}
onDismiss={() => setShowDialog(false)}
{...props}
>
<div data-testid="inner">
<button onClick={() => setShowDialog(false)}>Close Dialog</button>
Expand All @@ -102,3 +136,18 @@ function BasicOpenDialog() {
</div>
);
}

/**
* 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) {
while (el) {
if (el.hasAttribute("aria-hidden")) {
return true;
}
el = el.parentElement;
}
return false;
}
30 changes: 24 additions & 6 deletions packages/dialog/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const overlayPropTypes = {
initialFocusRef: () => null,
allowPinchZoom: PropTypes.bool,
onDismiss: PropTypes.func,
ariaHideAncestors: PropTypes.bool,
};

////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -92,6 +93,7 @@ const DialogInner = forwardRef<HTMLDivElement, DialogProps>(
onDismiss = noop,
onMouseDown,
onKeyDown,
ariaHideAncestors,
...props
},
forwardedRef
Expand Down Expand Up @@ -124,11 +126,11 @@ const DialogInner = forwardRef<HTMLDivElement, DialogProps>(
mouseDownTarget.current = event.target;
}

useEffect(
() =>
overlayNode.current ? createAriaHider(overlayNode.current) : void null,
[]
);
useEffect(() => {
if (overlayNode.current && ariaHideAncestors) {
createAriaHider(overlayNode.current);
}
}, [ariaHideAncestors]);

return (
<FocusLock autoFocus returnFocus onActivation={activateFocusLock}>
Expand Down Expand Up @@ -225,7 +227,14 @@ if (__DEV__) {
* @see Docs https://reacttraining.com/reach-ui/dialog#dialog
*/
export const Dialog = forwardRef<HTMLDivElement, DialogProps>(function Dialog(
{ isOpen, onDismiss = noop, initialFocusRef, allowPinchZoom, ...props },
{
isOpen,
onDismiss = noop,
initialFocusRef,
allowPinchZoom,
ariaHideAncestors = true,
...props
},
forwardedRef
) {
return (
Expand All @@ -234,6 +243,7 @@ export const Dialog = forwardRef<HTMLDivElement, DialogProps>(function Dialog(
allowPinchZoom={allowPinchZoom}
isOpen={isOpen}
onDismiss={onDismiss}
ariaHideAncestors={ariaHideAncestors}
>
<DialogContent ref={forwardedRef} {...props} />
</DialogOverlay>
Expand Down Expand Up @@ -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.
*
Expand All @@ -287,6 +304,7 @@ if (__DEV__) {
onDismiss: PropTypes.func,
"aria-label": ariaLabelType,
"aria-labelledby": ariaLabelType,
ariaHideAncestors: PropTypes.bool,
};
}

Expand Down

0 comments on commit 324bdc6

Please sign in to comment.