diff --git a/docs/README.md b/docs/README.md index e63ac121..36f58725 100644 --- a/docs/README.md +++ b/docs/README.md @@ -84,6 +84,11 @@ import ReactModal from 'react-modal'; Note: By disabling the esc key from closing the modal you may introduce an accessibility issue. */ shouldCloseOnEsc={true} + /* + Boolean indicating if the modal should restore focus to the element that + had focus prior to its display. + */ + shouldReturnFocusAfterClose={true} /* String indicating the role of the modal, allowing the 'dialog' role to be applied if desired. */ diff --git a/src/components/Modal.js b/src/components/Modal.js index e18ffa1a..f3e037c3 100644 --- a/src/components/Modal.js +++ b/src/components/Modal.js @@ -54,6 +54,7 @@ export default class Modal extends Component { ariaHideApp: PropTypes.bool, shouldFocusAfterRender: PropTypes.bool, shouldCloseOnOverlayClick: PropTypes.bool, + shouldReturnFocusAfterClose: PropTypes.bool, parentSelector: PropTypes.func, aria: PropTypes.object, role: PropTypes.string, @@ -71,6 +72,7 @@ export default class Modal extends Component { shouldFocusAfterRender: true, shouldCloseOnEsc: true, shouldCloseOnOverlayClick: true, + shouldReturnFocusAfterClose: true, parentSelector() { return document.body; } diff --git a/src/components/ModalPortal.js b/src/components/ModalPortal.js index 0a4354f6..434a9990 100644 --- a/src/components/ModalPortal.js +++ b/src/components/ModalPortal.js @@ -44,6 +44,7 @@ export default class ModalPortal extends Component { closeTimeoutMS: PropTypes.number, shouldFocusAfterRender: PropTypes.bool, shouldCloseOnOverlayClick: PropTypes.bool, + shouldReturnFocusAfterClose: PropTypes.bool, role: PropTypes.string, contentLabel: PropTypes.string, aria: PropTypes.object, @@ -137,8 +138,23 @@ export default class ModalPortal extends Component { afterClose = () => { // Remove body class bodyClassList.remove(this.props.bodyOpenClassName); - focusManager.returnFocus(); - focusManager.teardownScopedFocus(); + + if (this.shouldReturnFocus()) { + focusManager.returnFocus(); + focusManager.teardownScopedFocus(); + } + }; + + shouldReturnFocus = () => { + // Don't restore focus to the element that had focus prior to + // the modal's display if: + // 1. Focus was never shifted to the modal in the first place + // (shouldFocusAfterRender = false) + // 2. Explicit direction to not restore focus + return ( + this.props.shouldFocusAfterRender || + this.props.shouldReturnFocusAfterClose + ); }; open = () => { @@ -147,8 +163,11 @@ export default class ModalPortal extends Component { clearTimeout(this.closeTimer); this.setState({ beforeClose: false }); } else { - focusManager.setupScopedFocus(this.node); - focusManager.markForFocusLater(); + if (this.shouldReturnFocus()) { + focusManager.setupScopedFocus(this.node); + focusManager.markForFocusLater(); + } + this.setState({ isOpen: true }, () => { this.setState({ afterOpen: true });