Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions specs/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ if (!String.prototype.includes) {
};
}

/**
* hide the native close watcher; we don't have a way to test it yet,
* the implementation will fall back to using normal keydown events
* as usual.
*/
if (window.CloseWatcher) {
delete window.CloseWatcher;
}

/**
* Return the class list object from `document.body`.
* @return {Array}
Expand Down
35 changes: 34 additions & 1 deletion src/components/ModalPortal.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export default class ModalPortal extends Component {

this.shouldClose = null;
this.moveFromContentToOverlay = null;
this.closeWatcher = null;
}

componentDidMount() {
Expand Down Expand Up @@ -138,6 +139,7 @@ export default class ModalPortal extends Component {
}
clearTimeout(this.closeTimer);
cancelAnimationFrame(this.openAnimationFrame);
this.destroyCloseWatcher();
}

setOverlayRef = overlay => {
Expand Down Expand Up @@ -226,6 +228,27 @@ export default class ModalPortal extends Component {
portalOpenInstances.deregister(this);
};

setupCloseWatcher = () => {
if (typeof CloseWatcher !== "undefined" && this.props.shouldCloseOnEsc) {
this.closeWatcher = new window.CloseWatcher();
this.closeWatcher.oncancel = event => {
if (!this.props.shouldCloseOnEsc) {
event.preventDefault();
}
};
this.closeWatcher.onclose = event => {
this.requestClose(event);
};
}
};

destroyCloseWatcher = () => {
if (this.closeWatcher) {
this.closeWatcher.destroy();
this.closeWatcher = null;
}
};

open = () => {
this.beforeOpen();
if (this.state.afterOpen && this.state.beforeClose) {
Expand All @@ -237,6 +260,8 @@ export default class ModalPortal extends Component {
focusManager.markForFocusLater();
}

this.setupCloseWatcher();

this.setState({ isOpen: true }, () => {
this.openAnimationFrame = requestAnimationFrame(() => {
this.setState({ afterOpen: true });
Expand Down Expand Up @@ -277,6 +302,7 @@ export default class ModalPortal extends Component {
};

closeWithoutTimeout = () => {
this.destroyCloseWatcher();
this.setState(
{
beforeClose: false,
Expand All @@ -293,7 +319,14 @@ export default class ModalPortal extends Component {
scopeTab(this.content, event);
}

if (this.props.shouldCloseOnEsc && isEscKey(event)) {
// Handle escape key for closing when CloseWatcher is not supported.
// CloseWatcher emits for both native close (Android back) and Escape.
// Without it, we can fall back to ordinary keydown events from Escape.
if (
typeof CloseWatcher === "undefined" &&
this.props.shouldCloseOnEsc &&
isEscKey(event)
) {
event.stopPropagation();
this.requestClose(event);
}
Expand Down