mirror of
https://github.com/prymitive/karma
synced 2026-05-19 04:26:41 +00:00
fix(ui): workaround multiple modals conflicting with each other
This commit is contained in:
@@ -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 (
|
||||
<React.Fragment>
|
||||
@@ -306,6 +313,7 @@ const DeleteSilence = observer(
|
||||
isOpen={this.toggle.visible}
|
||||
isUpper={true}
|
||||
toggleOpen={this.toggle.toggle}
|
||||
onExited={onModalExit}
|
||||
>
|
||||
<DeleteSilenceModalContent
|
||||
alertStore={alertStore}
|
||||
|
||||
@@ -24,7 +24,8 @@ const SilenceDetails = ({
|
||||
silenceFormStore,
|
||||
silence,
|
||||
cluster,
|
||||
onEditSilence
|
||||
onEditSilence,
|
||||
onDeleteModalClose
|
||||
}) => {
|
||||
let isExpired = moment(silence.endsAt) < moment();
|
||||
let expiresClass = "";
|
||||
@@ -126,6 +127,7 @@ const SilenceDetails = ({
|
||||
silenceFormStore={silenceFormStore}
|
||||
cluster={cluster}
|
||||
silence={silence}
|
||||
onModalExit={onDeleteModalClose}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -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 };
|
||||
|
||||
@@ -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 (
|
||||
<MountFade in={true}>
|
||||
@@ -94,6 +101,7 @@ const ManagedSilence = observer(
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
onEditSilence={this.onEditSilence}
|
||||
onDeleteModalClose={onDeleteModalClose}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -65,7 +65,11 @@ const Modal = observer(
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.toggleBodyClass(false);
|
||||
const { isOpen } = this.props;
|
||||
|
||||
if (isOpen) {
|
||||
this.toggleBodyClass(false);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -24,6 +24,12 @@ describe("<Modal />", () => {
|
||||
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();
|
||||
|
||||
@@ -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 (
|
||||
<React.Fragment>
|
||||
@@ -226,6 +232,7 @@ const Browser = observer(
|
||||
silence={silence.silence}
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
onDeleteModalClose={onDeleteModalClose}
|
||||
/>
|
||||
))}
|
||||
</Provider>
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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}
|
||||
</div>
|
||||
|
||||
@@ -32,6 +32,7 @@ const ShallowSilenceModalContent = () => {
|
||||
settingsStore={settingsStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
onHide={MockOnHide}
|
||||
onDeleteModalClose={jest.fn()}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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(
|
||||
</TooltipWrapper>
|
||||
</li>
|
||||
<Modal
|
||||
ref={this.modalRef}
|
||||
isOpen={silenceFormStore.toggle.visible}
|
||||
toggleOpen={silenceFormStore.toggle.toggle}
|
||||
onExited={silenceFormStore.data.resetProgress}
|
||||
@@ -64,6 +75,7 @@ const SilenceModal = observer(
|
||||
silenceFormStore={silenceFormStore}
|
||||
settingsStore={settingsStore}
|
||||
onHide={silenceFormStore.toggle.hide}
|
||||
onDeleteModalClose={this.remountModal}
|
||||
/>
|
||||
</React.Suspense>
|
||||
</Modal>
|
||||
|
||||
@@ -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 (
|
||||
<SilenceModalContent
|
||||
alertStore={alertStore}
|
||||
@@ -59,7 +63,7 @@ storiesOf("SilenceModal", module)
|
||||
settingsStore={settingsStore}
|
||||
onHide={() => {}}
|
||||
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 (
|
||||
<SilenceModalContent
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
settingsStore={settingsStore}
|
||||
onHide={() => {}}
|
||||
openTab={TabNames.Browser}
|
||||
onDeleteModalClose={() => {}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -127,4 +127,11 @@ describe("<SilenceModal />", () => {
|
||||
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");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user