From 0f1be28958ec8f5e7595b35d62fa1ca31e5aee8f Mon Sep 17 00:00:00 2001 From: DIonysos Dajka Date: Thu, 11 Jun 2020 12:03:03 +0200 Subject: [PATCH] feat(Modal): Add PreventModalAriaHidden component --- src/Modal/ModalManager.js | 4 ++-- src/Modal/README.mdx | 16 ++++++++++++- src/Modal/accessiblyHideModalBackground.js | 27 ++++++++++++++++++++++ src/Modal/index.js | 1 + 4 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 src/Modal/accessiblyHideModalBackground.js diff --git a/src/Modal/ModalManager.js b/src/Modal/ModalManager.js index eda82501..e32ba1ba 100644 --- a/src/Modal/ModalManager.js +++ b/src/Modal/ModalManager.js @@ -9,7 +9,7 @@ import React, { } from 'react'; import useEventListener from '../useEventListener'; -import {hideOthers} from 'aria-hidden'; +import {hideForModal} from './accessiblyHideModalBackground'; const noop = () => {}; @@ -100,7 +100,7 @@ function ModalManager({children}) { useEffect(() => { if (modalStack.length) { const topMostModal = modalStack[modalStack.length - 1]; - const undoAriaHideOthers = hideOthers(topMostModal?.ref.current); + const undoAriaHideOthers = hideForModal(topMostModal?.ref.current); return function cleanUp() { undoAriaHideOthers(); diff --git a/src/Modal/README.mdx b/src/Modal/README.mdx index e57ef922..688b541d 100644 --- a/src/Modal/README.mdx +++ b/src/Modal/README.mdx @@ -18,7 +18,7 @@ React components and hooks for building accessible modals (dialogs). - Handles focus management: - Contain keyboard focus inside of the topmost modal - Return keyboard focus to the element that triggered the modal after it is closed - - Contain screen readers from navigating outside of the topmost modal + - Contain screen reader navigation within the topmost modal - Close the topmost modal when the Esc key is pressed - Prevent scrolling "behind" the modal @@ -153,3 +153,17 @@ function Modal({children, name, onRequestClose, ...otherProps}) { ); } ``` + +### Preventing content outside of modals from being hidden to screen readers + +When a modal is open, the `aria-hidden` attribute will be applied to any elements outside of it. This is done to prevent users of screen reader software from accidentally navigating outside of the modal boundaries. + +If you want to prevent some elements (such as a live region used for accessible status updates) from being hidden in this way, you can wrap them in the `PreventModalAriaHidden` component: + +```jsx +import {PreventModalAriaHidden} from 'base5-ui/Modal'; + + + ... + +``` diff --git a/src/Modal/accessiblyHideModalBackground.js b/src/Modal/accessiblyHideModalBackground.js new file mode 100644 index 00000000..94b19c05 --- /dev/null +++ b/src/Modal/accessiblyHideModalBackground.js @@ -0,0 +1,27 @@ +import React from 'react'; +import {hideOthers} from 'aria-hidden'; +import PropTypes from 'prop-types'; + +const PREVENT_ARIA_HIDDEN_ATTRIBUTE = 'data-prevent-aria-hidden'; + +// hide everything except modalElement and elements with +// the PREVENT_ARIA_HIDDEN_ATTRIBUTE +function hideForModal(modalElement) { + return hideOthers([ + modalElement, + ...document.querySelectorAll(`[${PREVENT_ARIA_HIDDEN_ATTRIBUTE}]`), + ]); +} + +// A helper component that applies the PREVENT_ARIA_HIDDEN_ATTRIBUTE +// to its (only) child component +function PreventModalAriaHidden({children}) { + return React.cloneElement(React.Children.only(children), { + [PREVENT_ARIA_HIDDEN_ATTRIBUTE]: true, + }); +} +PreventModalAriaHidden.propTypes = { + children: PropTypes.element.isRequired, +}; + +export {hideForModal, PreventModalAriaHidden}; diff --git a/src/Modal/index.js b/src/Modal/index.js index c24dc28d..376631da 100644 --- a/src/Modal/index.js +++ b/src/Modal/index.js @@ -8,5 +8,6 @@ export { } from './ModalManager'; export {BodyScrollLock, useScrollLockStyles} from './BodyScrollLock'; +export {PreventModalAriaHidden} from './accessiblyHideModalBackground'; export default Modal;