diff --git a/ui/src/Components/ManagedSilence/DeleteSilence.js b/ui/src/Components/ManagedSilence/DeleteSilence.js index 5130351ff..829d9e2cc 100644 --- a/ui/src/Components/ManagedSilence/DeleteSilence.js +++ b/ui/src/Components/ManagedSilence/DeleteSilence.js @@ -274,7 +274,8 @@ const DeleteSilence = observer( alertStore: PropTypes.instanceOf(AlertStore).isRequired, silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired, cluster: PropTypes.string.isRequired, - silence: APISilence.isRequired + silence: APISilence.isRequired, + onModalExit: PropTypes.func }; toggle = observable( @@ -288,7 +289,13 @@ const DeleteSilence = observer( ); render() { - const { alertStore, silenceFormStore, cluster, silence } = this.props; + const { + alertStore, + silenceFormStore, + cluster, + silence, + onModalExit + } = this.props; return ( @@ -306,6 +313,7 @@ const DeleteSilence = observer( isOpen={this.toggle.visible} isUpper={true} toggleOpen={this.toggle.toggle} + onExited={onModalExit} > { let isExpired = moment(silence.endsAt) < moment(); let expiresClass = ""; @@ -126,6 +127,7 @@ const SilenceDetails = ({ silenceFormStore={silenceFormStore} cluster={cluster} silence={silence} + onModalExit={onDeleteModalClose} /> )} @@ -138,7 +140,8 @@ SilenceDetails.propTypes = { silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired, cluster: PropTypes.string.isRequired, silence: APISilence.isRequired, - onEditSilence: PropTypes.func.isRequired + onEditSilence: PropTypes.func.isRequired, + onDeleteModalClose: PropTypes.func }; export { SilenceDetails }; diff --git a/ui/src/Components/ManagedSilence/index.js b/ui/src/Components/ManagedSilence/index.js index 4b1fbfcac..e9d23f062 100644 --- a/ui/src/Components/ManagedSilence/index.js +++ b/ui/src/Components/ManagedSilence/index.js @@ -24,7 +24,8 @@ const ManagedSilence = observer( silence: APISilence.isRequired, alertStore: PropTypes.instanceOf(AlertStore).isRequired, silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired, - onDidUpdate: PropTypes.func + onDidUpdate: PropTypes.func, + onDeleteModalClose: PropTypes.func }; // store collapse state, by default only silence comment is visible @@ -61,7 +62,13 @@ const ManagedSilence = observer( } render() { - const { cluster, silence, alertStore, silenceFormStore } = this.props; + const { + cluster, + silence, + alertStore, + silenceFormStore, + onDeleteModalClose + } = this.props; return ( @@ -94,6 +101,7 @@ const ManagedSilence = observer( alertStore={alertStore} silenceFormStore={silenceFormStore} onEditSilence={this.onEditSilence} + onDeleteModalClose={onDeleteModalClose} /> )} diff --git a/ui/src/Components/Modal/index.js b/ui/src/Components/Modal/index.js index 11f0ca53a..9b39009be 100644 --- a/ui/src/Components/Modal/index.js +++ b/ui/src/Components/Modal/index.js @@ -65,7 +65,11 @@ const Modal = observer( } componentWillUnmount() { - this.toggleBodyClass(false); + const { isOpen } = this.props; + + if (isOpen) { + this.toggleBodyClass(false); + } } render() { diff --git a/ui/src/Components/Modal/index.test.js b/ui/src/Components/Modal/index.test.js index c48ea5fcd..79d8dc54c 100644 --- a/ui/src/Components/Modal/index.test.js +++ b/ui/src/Components/Modal/index.test.js @@ -24,6 +24,12 @@ describe("", () => { expect(document.body.className.split(" ")).toContain("modal-open"); }); + it("'modal-open' class is not removed from body node after hidden modal is unmounted", () => { + const tree = MountedModal(false); + tree.unmount(); + expect(document.body.className.split(" ")).toContain("modal-open"); + }); + it("'modal-open' class is removed from body node after modal is unmounted", () => { const tree = MountedModal(true); tree.unmount(); diff --git a/ui/src/Components/SilenceModal/Browser/index.js b/ui/src/Components/SilenceModal/Browser/index.js index 834cc8837..c5e20aa96 100644 --- a/ui/src/Components/SilenceModal/Browser/index.js +++ b/ui/src/Components/SilenceModal/Browser/index.js @@ -53,7 +53,8 @@ const Browser = observer( static propTypes = { alertStore: PropTypes.instanceOf(AlertStore).isRequired, silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired, - settingsStore: PropTypes.instanceOf(Settings).isRequired + settingsStore: PropTypes.instanceOf(Settings).isRequired, + onDeleteModalClose: PropTypes.func.isRequired }; fetchTimer = null; @@ -149,7 +150,12 @@ const Browser = observer( } render() { - const { alertStore, silenceFormStore, settingsStore } = this.props; + const { + alertStore, + silenceFormStore, + settingsStore, + onDeleteModalClose + } = this.props; return ( @@ -226,6 +232,7 @@ const Browser = observer( silence={silence.silence} alertStore={alertStore} silenceFormStore={silenceFormStore} + onDeleteModalClose={onDeleteModalClose} /> ))} diff --git a/ui/src/Components/SilenceModal/Browser/index.test.js b/ui/src/Components/SilenceModal/Browser/index.test.js index 412346f77..045396ecd 100644 --- a/ui/src/Components/SilenceModal/Browser/index.test.js +++ b/ui/src/Components/SilenceModal/Browser/index.test.js @@ -54,6 +54,8 @@ afterEach(() => { clear(); }); +const MockOnDeleteModalClose = jest.fn(); + const MockSilenceList = count => { let silences = []; for (var index = 1; index <= count; index++) { @@ -73,6 +75,7 @@ const MountedBrowser = () => { alertStore={alertStore} silenceFormStore={silenceFormStore} settingsStore={settingsStore} + onDeleteModalClose={MockOnDeleteModalClose} /> ); }; diff --git a/ui/src/Components/SilenceModal/SilenceModalContent.js b/ui/src/Components/SilenceModal/SilenceModalContent.js index 41e561eb9..57ca29113 100644 --- a/ui/src/Components/SilenceModal/SilenceModalContent.js +++ b/ui/src/Components/SilenceModal/SilenceModalContent.js @@ -25,7 +25,8 @@ const SilenceModalContent = observer( silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired, settingsStore: PropTypes.instanceOf(Settings).isRequired, onHide: PropTypes.func.isRequired, - previewOpen: PropTypes.bool + previewOpen: PropTypes.bool, + onDeleteModalClose: PropTypes.func.isRequired }; static defaultProps = { previewOpen: false @@ -37,7 +38,8 @@ const SilenceModalContent = observer( silenceFormStore, settingsStore, onHide, - previewOpen + previewOpen, + onDeleteModalClose } = this.props; return ( @@ -106,6 +108,7 @@ const SilenceModalContent = observer( alertStore={alertStore} silenceFormStore={silenceFormStore} settingsStore={settingsStore} + onDeleteModalClose={onDeleteModalClose} /> ) : null} diff --git a/ui/src/Components/SilenceModal/SilenceModalContent.test.js b/ui/src/Components/SilenceModal/SilenceModalContent.test.js index 9519f79ac..352b54cf1 100644 --- a/ui/src/Components/SilenceModal/SilenceModalContent.test.js +++ b/ui/src/Components/SilenceModal/SilenceModalContent.test.js @@ -32,6 +32,7 @@ const ShallowSilenceModalContent = () => { settingsStore={settingsStore} silenceFormStore={silenceFormStore} onHide={MockOnHide} + onDeleteModalClose={jest.fn()} /> ); }; diff --git a/ui/src/Components/SilenceModal/index.js b/ui/src/Components/SilenceModal/index.js index 8411de973..85914336e 100644 --- a/ui/src/Components/SilenceModal/index.js +++ b/ui/src/Components/SilenceModal/index.js @@ -28,6 +28,16 @@ const SilenceModal = observer( settingsStore: PropTypes.instanceOf(Settings).isRequired }; + constructor(props) { + super(props); + + this.modalRef = React.createRef(); + } + + remountModal = () => { + this.modalRef.current.toggleBodyClass(true); + }; + render() { const { alertStore, silenceFormStore, settingsStore } = this.props; @@ -48,6 +58,7 @@ const SilenceModal = observer( diff --git a/ui/src/Components/SilenceModal/index.stories.js b/ui/src/Components/SilenceModal/index.stories.js index ebd5b2193..4f99502d3 100644 --- a/ui/src/Components/SilenceModal/index.stories.js +++ b/ui/src/Components/SilenceModal/index.stories.js @@ -2,14 +2,16 @@ import React from "react"; import { storiesOf } from "@storybook/react"; +import { MockSilence } from "__mocks__/Alerts"; import { AlertStore } from "Stores/AlertStore"; import { Settings } from "Stores/Settings"; import { SilenceFormStore, NewEmptyMatcher, - MatcherValueToObject + MatcherValueToObject, + SilenceTabNames } from "Stores/SilenceFormStore"; -import { SilenceModalContent, TabNames } from "./SilenceModalContent"; +import { SilenceModalContent } from "./SilenceModalContent"; import "Percy.scss"; @@ -52,6 +54,8 @@ storiesOf("SilenceModal", module) silenceFormStore.data.comment = "fake silence"; silenceFormStore.data.resetStartEnd(); + silenceFormStore.tab.current = SilenceTabNames.Editor; + return ( {}} previewOpen={true} - openTab={TabNames.Editor} + onDeleteModalClose={() => {}} /> ); }) @@ -68,13 +72,41 @@ storiesOf("SilenceModal", module) const settingsStore = new Settings(); const silenceFormStore = new SilenceFormStore(); + silenceFormStore.tab.current = SilenceTabNames.Browser; + + alertStore.data.upstreams = { + instances: [ + { + name: "am1", + cluster: "am", + clusterMembers: ["am1"], + uri: "http://localhost:9093", + publicURI: "http://example.com", + error: "", + version: "0.15.3", + headers: {} + } + ], + clusters: { am: ["am1"] } + }; + + let silences = []; + for (var index = 1; index <= 18; index++) { + const silence = MockSilence(); + silence.id = `silence${index}`; + silences.push({ + cluster: "am", + silence: silence + }); + } + return ( {}} - openTab={TabNames.Browser} + onDeleteModalClose={() => {}} /> ); }); diff --git a/ui/src/Components/SilenceModal/index.test.js b/ui/src/Components/SilenceModal/index.test.js index 8e39ad585..a1209e68d 100644 --- a/ui/src/Components/SilenceModal/index.test.js +++ b/ui/src/Components/SilenceModal/index.test.js @@ -127,4 +127,11 @@ describe("", () => { tree.unmount(); expect(document.body.className.split(" ")).not.toContain("modal-open"); }); + + it("'modal-open' class is preserved on body node after remountModal is called", () => { + silenceFormStore.toggle.visible = true; + const tree = MountedSilenceModal(); + tree.instance().remountModal(); + expect(document.body.className.split(" ")).toContain("modal-open"); + }); });