feat(ui): close modal window when user hits the Escape key

Fixes #842
This commit is contained in:
Łukasz Mierzwa
2019-07-31 19:56:57 +01:00
parent 6b0fce2d6f
commit 312b718b7a
8 changed files with 43 additions and 8 deletions
+8
View File
@@ -12412,6 +12412,14 @@
"prop-types": "^15.6.0"
}
},
"react-hotkeys": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/react-hotkeys/-/react-hotkeys-2.0.0.tgz",
"integrity": "sha512-3n3OU8vLX/pfcJrR3xJ1zlww6KS1kEJt0Whxc4FiGV+MJrQ1mYSYI3qS/11d2MJDFm8IhOXMTFQirfu6AVOF6Q==",
"requires": {
"prop-types": "^15.6.1"
}
},
"react-idle-timer": {
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/react-idle-timer/-/react-idle-timer-4.2.8.tgz",
+1
View File
@@ -34,6 +34,7 @@
"react-datepicker": "2.8.0",
"react-dom": "16.8.6",
"react-highlighter": "0.4.3",
"react-hotkeys": "2.0.0",
"react-idle-timer": "4.2.8",
"react-input-range": "1.3.0",
"react-js-pagination": "3.0.2",
@@ -281,7 +281,7 @@ const DeleteSilence = observer(
<FontAwesomeIcon className="mr-1" icon={faTrash} />
Delete
</span>
<Modal isOpen={this.toggle.visible}>
<Modal isOpen={this.toggle.visible} toggleOpen={this.toggle.toggle}>
<DeleteSilenceModalContent
alertStore={alertStore}
alertmanager={alertmanager}
+1 -1
View File
@@ -59,7 +59,7 @@ const MainModal = observer(
</span>
</TooltipWrapper>
</li>
<Modal isOpen={this.toggle.show}>
<Modal isOpen={this.toggle.show} toggleOpen={this.toggle.toggle}>
<React.Suspense
fallback={
<h1 className="display-1 text-secondary p-5 m-auto">
+12 -3
View File
@@ -6,6 +6,8 @@ import { observer } from "mobx-react";
import { disableBodyScroll, clearAllBodyScrollLocks } from "body-scroll-lock";
import { HotKeys } from "react-hotkeys";
import {
MountModal,
MountModalBackdrop
@@ -16,6 +18,7 @@ const Modal = observer(
static propTypes = {
size: PropTypes.oneOf(["lg", "xl"]),
isOpen: PropTypes.bool.isRequired,
toggleOpen: PropTypes.func.isRequired,
children: PropTypes.node.isRequired
};
static defaultProps = {
@@ -25,11 +28,13 @@ const Modal = observer(
constructor(props) {
super(props);
this.modalRef = React.createRef();
this.HotKeysRef = React.createRef();
}
toggleBodyClass = isOpen => {
document.body.classList.toggle("modal-open", isOpen);
if (isOpen) {
this.HotKeysRef.current.focus();
disableBodyScroll(this.modalRef.current);
} else {
clearAllBodyScrollLocks();
@@ -51,10 +56,14 @@ const Modal = observer(
}
render() {
const { size, isOpen, children, ...props } = this.props;
const { size, isOpen, toggleOpen, children, ...props } = this.props;
return ReactDOM.createPortal(
<React.Fragment>
<HotKeys
innerRef={this.HotKeysRef}
keyMap={{ CLOSE: "Escape" }}
handlers={{ CLOSE: toggleOpen }}
>
<MountModal in={isOpen} unmountOnExit {...props}>
<div ref={this.modalRef} className="modal d-block" role="dialog">
<div className={`modal-dialog modal-${size}`} role="document">
@@ -65,7 +74,7 @@ const Modal = observer(
<MountModalBackdrop in={isOpen} unmountOnExit>
<div className="modal-backdrop d-block" />
</MountModalBackdrop>
</React.Fragment>,
</HotKeys>,
document.body
);
}
+14 -2
View File
@@ -4,14 +4,20 @@ import { mount } from "enzyme";
import { Modal } from ".";
const fakeToggle = jest.fn();
const MountedModal = isOpen => {
return mount(
<Modal isOpen={isOpen}>
<Modal isOpen={isOpen} toggleOpen={fakeToggle}>
<div />
</Modal>
);
};
afterEach(() => {
jest.resetAllMocks();
});
describe("<Modal />", () => {
it("'modal-open' class is appended to body node when modal is visible", () => {
MountedModal(true);
@@ -32,11 +38,17 @@ describe("<Modal />", () => {
it("passes extra props down to the MountModal animation component", () => {
const onExited = jest.fn();
const tree = mount(
<Modal isOpen={true} onExited={onExited}>
<Modal isOpen={true} toggleOpen={fakeToggle} onExited={onExited}>
<div />
</Modal>
);
const mountModal = tree.find("MountModal");
expect(mountModal.props().onExited).toBe(onExited);
});
it("toggleOpen is called after pressing 'esc'", () => {
const tree = MountedModal(true);
tree.simulate("keyDown", { key: "Escape", keyCode: 27, which: 27 });
expect(fakeToggle).toHaveBeenCalled();
});
});
+5 -1
View File
@@ -58,7 +58,11 @@ const OverviewModal = observer(
</div>
</Flash>
</TooltipWrapper>
<Modal size="xl" isOpen={this.toggle.show}>
<Modal
size="xl"
isOpen={this.toggle.show}
toggleOpen={this.toggle.toggle}
>
<React.Suspense
fallback={
<h1 className="display-1 text-secondary p-5 m-auto">
+1
View File
@@ -51,6 +51,7 @@ const SilenceModal = observer(
</li>
<Modal
isOpen={silenceFormStore.toggle.visible}
toggleOpen={silenceFormStore.toggle.toggle}
onExited={silenceFormStore.data.resetProgress}
>
<React.Suspense