diff --git a/ui/src/Components/MainModal/MainModalContent.js b/ui/src/Components/MainModal/MainModalContent.js index 67b0d88ab..ee10a468d 100644 --- a/ui/src/Components/MainModal/MainModalContent.js +++ b/ui/src/Components/MainModal/MainModalContent.js @@ -1,16 +1,12 @@ import React, { Component } from "react"; -import ReactDOM from "react-dom"; import PropTypes from "prop-types"; import { observer } from "mobx-react"; import { observable, action } from "mobx"; -import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock"; - import { AlertStore } from "Stores/AlertStore"; import { Settings } from "Stores/Settings"; import { Configuration } from "./Configuration"; -import { MountModalBackdrop } from "Components/Animations/MountModal"; import { Help } from "./Help"; const Tab = ({ title, active, onClick }) => ( @@ -39,8 +35,7 @@ const MainModalContent = observer( static propTypes = { alertStore: PropTypes.instanceOf(AlertStore).isRequired, settingsStore: PropTypes.instanceOf(Settings).isRequired, - onHide: PropTypes.func.isRequired, - isVisible: PropTypes.bool.isRequired + onHide: PropTypes.func.isRequired }; tab = observable( @@ -53,58 +48,40 @@ const MainModalContent = observer( { setTab: action.bound } ); - componentDidMount() { - disableBodyScroll(document.querySelector(".modal")); - } - - componentWillUnmount() { - enableBodyScroll(document.querySelector(".modal")); - } - render() { - const { alertStore, settingsStore, onHide, isVisible } = this.props; + const { alertStore, settingsStore, onHide } = this.props; - return ReactDOM.createPortal( + return ( -
-
-
-
- -
-
- {this.tab.current === TabNames.Help ? : null} - {this.tab.current === TabNames.Configuration ? ( - - ) : null} -
-
- - Version: {alertStore.info.version} - -
-
-
+
+
- -
- - , - document.body +
+ {this.tab.current === TabNames.Help ? : null} + {this.tab.current === TabNames.Configuration ? ( + + ) : null} +
+
+ + Version: {alertStore.info.version} + +
+ ); } } diff --git a/ui/src/Components/MainModal/MainModalContent.test.js b/ui/src/Components/MainModal/MainModalContent.test.js index f9b033f4a..495f8a683 100644 --- a/ui/src/Components/MainModal/MainModalContent.test.js +++ b/ui/src/Components/MainModal/MainModalContent.test.js @@ -28,7 +28,6 @@ const FakeModal = () => { alertStore={alertStore} settingsStore={settingsStore} onHide={onHide} - isVisible={true} /> ); }; @@ -46,7 +45,18 @@ const ValidateSetTab = (title, callArg) => { describe("", () => { it("matches snapshot", () => { - const tree = FakeModal(); + // we have multiple fragments and enzyme only renders the first one + // in html() and text(), debug() would work but it's noisy + // https://github.com/airbnb/enzyme/issues/1213 + const tree = mount( + + + + ); expect(toDiffableHtml(tree.html())).toMatchSnapshot(); }); diff --git a/ui/src/Components/MainModal/__snapshots__/MainModalContent.test.js.snap b/ui/src/Components/MainModal/__snapshots__/MainModalContent.test.js.snap index eff9a7e04..2a6a1d42c 100644 --- a/ui/src/Components/MainModal/__snapshots__/MainModalContent.test.js.snap +++ b/ui/src/Components/MainModal/__snapshots__/MainModalContent.test.js.snap @@ -2,129 +2,121 @@ exports[` matches snapshot 1`] = ` " -
-
-
-
- -
-
-
-
- -
- - - 10s - - -
-
-
- - - - 30s - - -
-
-
-
- - - 120s - - -
-
-
-
-
- -
- - - 1 - - -
-
-
- - - - 5 - - -
-
-
-
- - - 10 - - -
-
-
-
-
- - Version: unknown + +
+
-
+ +
-
+
+
+
+ +
+ + + 10s + + +
+
+
+ + + + 30s + + +
+
+
+
+ + + 120s + + +
+
+
+
+
+ +
+ + + 1 + + +
+
+
+ + + + 5 + + +
+
+
+
+ + + 10 + + +
+
+
+
+
+ + Version: unknown + +
+ " `; diff --git a/ui/src/Components/MainModal/index.js b/ui/src/Components/MainModal/index.js index 5f8387743..66948d4ed 100644 --- a/ui/src/Components/MainModal/index.js +++ b/ui/src/Components/MainModal/index.js @@ -9,8 +9,8 @@ import { faCog } from "@fortawesome/free-solid-svg-icons/faCog"; import { AlertStore } from "Stores/AlertStore"; import { Settings } from "Stores/Settings"; -import { MountModal } from "Components/Animations/MountModal"; import { TooltipWrapper } from "Components/TooltipWrapper"; +import { Modal } from "Components/Modal"; import { MainModalContent } from "./MainModalContent"; const MainModal = observer( @@ -33,14 +33,6 @@ const MainModal = observer( { toggle: action.bound, hide: action.bound } ); - componentDidUpdate() { - document.body.classList.toggle("modal-open", this.toggle.show); - } - - componentWillUnmount() { - document.body.classList.remove("modal-open"); - } - render() { const { alertStore, settingsStore } = this.props; @@ -56,14 +48,14 @@ const MainModal = observer( - + - + ); } diff --git a/ui/src/Components/Modal/index.js b/ui/src/Components/Modal/index.js new file mode 100644 index 000000000..7fe8274c2 --- /dev/null +++ b/ui/src/Components/Modal/index.js @@ -0,0 +1,66 @@ +import React, { Component } from "react"; +import ReactDOM from "react-dom"; +import PropTypes from "prop-types"; + +import { observer } from "mobx-react"; + +import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock"; + +import { + MountModal, + MountModalBackdrop +} from "Components/Animations/MountModal"; + +const Modal = observer( + class Modal extends Component { + static propTypes = { + isOpen: PropTypes.bool.isRequired, + children: PropTypes.node.isRequired + }; + + toggleBodyClass = isOpen => { + document.body.classList.toggle("modal-open", isOpen); + if (isOpen) { + disableBodyScroll(document.querySelector(".modal")); + } else { + enableBodyScroll(document.querySelector(".modal")); + } + }; + + componentDidMount() { + const { isOpen } = this.props; + this.toggleBodyClass(isOpen); + } + + componentDidUpdate() { + const { isOpen } = this.props; + this.toggleBodyClass(isOpen); + } + + componentWillUnmount() { + this.toggleBodyClass(false); + } + + render() { + const { isOpen, children } = this.props; + + return ReactDOM.createPortal( + + +
+
+
{children}
+
+
+
+ +
+ + , + document.body + ); + } + } +); + +export { Modal }; diff --git a/ui/src/Components/Modal/index.test.js b/ui/src/Components/Modal/index.test.js new file mode 100644 index 000000000..758373ee0 --- /dev/null +++ b/ui/src/Components/Modal/index.test.js @@ -0,0 +1,31 @@ +import React from "react"; + +import { mount } from "enzyme"; + +import { Modal } from "."; + +const MountedModal = isOpen => { + return mount( + +
+ + ); +}; + +describe("", () => { + it("'modal-open' class is appended to body node when modal is visible", () => { + MountedModal(true); + expect(document.body.className.split(" ")).toContain("modal-open"); + }); + + it("'modal-open' class is removed from body node after modal is hidden", () => { + MountedModal(false); + expect(document.body.className.split(" ")).not.toContain("modal-open"); + }); + + it("'modal-open' class is removed from body node after modal is unmounted", () => { + const tree = MountedModal(true); + tree.unmount(); + expect(document.body.className.split(" ")).not.toContain("modal-open"); + }); +}); diff --git a/ui/src/Components/NavBar/__snapshots__/index.test.js.snap b/ui/src/Components/NavBar/__snapshots__/index.test.js.snap deleted file mode 100644 index 918b93382..000000000 --- a/ui/src/Components/NavBar/__snapshots__/index.test.js.snap +++ /dev/null @@ -1,283 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` matches snapshot with 0 alerts 1`] = ` -" -
- -
-" -`; - -exports[` matches snapshot with 5 alerts 1`] = ` -" -
- -
-" -`; diff --git a/ui/src/Components/NavBar/index.test.js b/ui/src/Components/NavBar/index.test.js index c416621cf..1beb2bd81 100644 --- a/ui/src/Components/NavBar/index.test.js +++ b/ui/src/Components/NavBar/index.test.js @@ -54,17 +54,6 @@ const ValidateNavClass = (totalFilters, expectedClass) => { }; describe("", () => { - it("matches snapshot with 0 alerts", () => { - const tree = RenderNavbar(); - expect(toDiffableHtml(tree.html())).toMatchSnapshot(); - }); - - it("matches snapshot with 5 alerts", () => { - alertStore.info.totalAlerts = 5; - const tree = RenderNavbar(); - expect(toDiffableHtml(tree.html())).toMatchSnapshot(); - }); - it("navbar-brand shows 15 alerts with totalAlerts=15", () => { alertStore.info.totalAlerts = 15; const tree = RenderNavbar(); diff --git a/ui/src/Components/SilenceModal/SilenceModalContent.js b/ui/src/Components/SilenceModal/SilenceModalContent.js index 33243f048..7703e2c2f 100644 --- a/ui/src/Components/SilenceModal/SilenceModalContent.js +++ b/ui/src/Components/SilenceModal/SilenceModalContent.js @@ -1,15 +1,11 @@ import React, { Component } from "react"; -import ReactDOM from "react-dom"; import PropTypes from "prop-types"; import { observer } from "mobx-react"; -import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock"; - import { AlertStore } from "Stores/AlertStore"; import { SilenceFormStore, SilenceFormStage } from "Stores/SilenceFormStore"; import { Settings } from "Stores/Settings"; -import { MountModalBackdrop } from "Components/Animations/MountModal"; import { SilenceForm } from "./SilenceForm"; import { SilencePreview } from "./SilencePreview"; import { SilenceSubmitController } from "./SilenceSubmit/SilenceSubmitController"; @@ -23,14 +19,6 @@ const SilenceModalContent = observer( onHide: PropTypes.func.isRequired }; - componentDidMount() { - disableBodyScroll(document.querySelector(".modal")); - } - - componentWillUnmount() { - enableBodyScroll(document.querySelector(".modal")); - } - render() { const { alertStore, @@ -39,59 +27,46 @@ const SilenceModalContent = observer( onHide } = this.props; - return ReactDOM.createPortal( + return ( -
-
-
-
-
- {silenceFormStore.data.silenceID === null - ? silenceFormStore.data.currentStage === - SilenceFormStage.UserInput - ? "Add new silence" - : silenceFormStore.data.currentStage === - SilenceFormStage.Preview - ? "Preview silenced alerts" - : "Silence submitted" - : `Editing silence ${silenceFormStore.data.silenceID}`} -
- -
-
- {silenceFormStore.data.currentStage === - SilenceFormStage.UserInput ? ( - - ) : silenceFormStore.data.currentStage === - SilenceFormStage.Preview ? ( - - ) : ( - - )} -
-
-
+
+
+ {silenceFormStore.data.silenceID === null + ? silenceFormStore.data.currentStage === + SilenceFormStage.UserInput + ? "Add new silence" + : silenceFormStore.data.currentStage === + SilenceFormStage.Preview + ? "Preview silenced alerts" + : "Silence submitted" + : `Editing silence ${silenceFormStore.data.silenceID}`} +
+
- -
- - , - document.body +
+ {silenceFormStore.data.currentStage === + SilenceFormStage.UserInput ? ( + + ) : silenceFormStore.data.currentStage === + SilenceFormStage.Preview ? ( + + ) : ( + + )} +
+ ); } } diff --git a/ui/src/Components/SilenceModal/index.js b/ui/src/Components/SilenceModal/index.js index 9d58e8a2e..b70f13809 100644 --- a/ui/src/Components/SilenceModal/index.js +++ b/ui/src/Components/SilenceModal/index.js @@ -9,7 +9,7 @@ import { faBellSlash } from "@fortawesome/free-solid-svg-icons/faBellSlash"; import { AlertStore } from "Stores/AlertStore"; import { SilenceFormStore } from "Stores/SilenceFormStore"; import { Settings } from "Stores/Settings"; -import { MountModal } from "Components/Animations/MountModal"; +import { Modal } from "Components/Modal"; import { TooltipWrapper } from "Components/TooltipWrapper"; import { SilenceModalContent } from "./SilenceModalContent"; @@ -35,19 +35,6 @@ const SilenceModal = observer( } }; - componentDidUpdate() { - const { silenceFormStore } = this.props; - - document.body.classList.toggle( - "modal-open", - silenceFormStore.toggle.visible - ); - } - - componentWillUnmount() { - document.body.classList.remove("modal-open"); - } - render() { const { alertStore, silenceFormStore, settingsStore } = this.props; @@ -63,14 +50,14 @@ const SilenceModal = observer( - + - + ); }