mirror of
https://github.com/prymitive/karma
synced 2026-05-11 03:46:48 +00:00
committed by
Łukasz Mierzwa
parent
6a4cbe80f9
commit
d913232869
@@ -145,8 +145,7 @@ describe("<SilenceModalContent /> Editor", () => {
|
||||
it("renders SilenceSubmitController when silenceFormStore.data.currentStage is 'Submit'", () => {
|
||||
silenceFormStore.data.currentStage = SilenceFormStage.Submit;
|
||||
const tree = MountedSilenceModalContent();
|
||||
const ctrl = tree.find("SilenceSubmitController");
|
||||
expect(ctrl).toHaveLength(1);
|
||||
expect(tree.html()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React, { memo } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
@@ -8,7 +8,7 @@ import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { SilenceSubmitProgress } from "./SilenceSubmitProgress";
|
||||
|
||||
const SilenceSubmitController = ({ silenceFormStore, alertStore }) => {
|
||||
const SilenceSubmitController = memo(({ silenceFormStore, alertStore }) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div>
|
||||
@@ -34,7 +34,7 @@ const SilenceSubmitController = ({ silenceFormStore, alertStore }) => {
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
});
|
||||
SilenceSubmitController.propTypes = {
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired,
|
||||
|
||||
@@ -31,8 +31,7 @@ describe("<SilenceSubmitController />", () => {
|
||||
value: ["am2", "am3"],
|
||||
});
|
||||
const tree = ShallowSilenceSubmitController();
|
||||
const alertmanagers = tree.find("SilenceSubmitProgress");
|
||||
expect(alertmanagers).toHaveLength(2);
|
||||
expect(tree.find("div").at(0).children()).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("resets the form on 'Back' button click", () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState, memo } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
@@ -10,79 +10,84 @@ import { APISilenceMatcher } from "Models/API";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { useFetchAny } from "Hooks/useFetchAny";
|
||||
|
||||
const SilenceSubmitProgress = ({ alertStore, cluster, members, payload }) => {
|
||||
const [upstreams, setUpstreams] = useState([]);
|
||||
const { response, error, inProgress, responseURI } = useFetchAny(upstreams);
|
||||
const [publicURIs, setPublicURIs] = useState({});
|
||||
const SilenceSubmitProgress = memo(
|
||||
({ alertStore, cluster, members, payload }) => {
|
||||
const [upstreams, setUpstreams] = useState([]);
|
||||
const { response, error, inProgress, responseURI } = useFetchAny(upstreams);
|
||||
const [publicURIs, setPublicURIs] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
let uris = {};
|
||||
let membersToTry = [];
|
||||
for (const member of members) {
|
||||
if (alertStore.data.isReadOnlyAlertmanager(member)) {
|
||||
console.error(`Alertmanager instance "${member}" is read-only`);
|
||||
} else {
|
||||
const am = alertStore.data.getAlertmanagerByName(member);
|
||||
if (am === undefined) {
|
||||
console.error(`Alertmanager instance "${member}" not found`);
|
||||
useEffect(() => {
|
||||
let uris = {};
|
||||
let membersToTry = [];
|
||||
for (const member of members) {
|
||||
if (alertStore.data.isReadOnlyAlertmanager(member)) {
|
||||
console.error(`Alertmanager instance "${member}" is read-only`);
|
||||
} else {
|
||||
const uri = `${am.uri}/api/v2/silences`;
|
||||
membersToTry.push({
|
||||
uri: uri,
|
||||
options: {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload),
|
||||
credentials: am.corsCredentials,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...am.headers,
|
||||
const am = alertStore.data.getAlertmanagerByName(member);
|
||||
if (am === undefined) {
|
||||
console.error(`Alertmanager instance "${member}" not found`);
|
||||
} else {
|
||||
const uri = `${am.uri}/api/v2/silences`;
|
||||
membersToTry.push({
|
||||
uri: uri,
|
||||
options: {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload),
|
||||
credentials: am.corsCredentials,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...am.headers,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
uris[uri] = am.publicURI;
|
||||
});
|
||||
uris[uri] = am.publicURI;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (membersToTry.length) {
|
||||
setPublicURIs(uris);
|
||||
setUpstreams(membersToTry);
|
||||
}
|
||||
}, [alertStore.data, members, payload]);
|
||||
if (membersToTry.length) {
|
||||
setPublicURIs(uris);
|
||||
setUpstreams(membersToTry);
|
||||
}
|
||||
}, [alertStore.data, members, payload]);
|
||||
|
||||
return (
|
||||
<div className="d-flex mb-2">
|
||||
<div className="p-2 flex-fill my-auto flex-grow-0 flex-shrink-0">
|
||||
{inProgress ? (
|
||||
<FontAwesomeIcon icon={faCircleNotch} spin />
|
||||
) : error ? (
|
||||
<FontAwesomeIcon icon={faExclamationCircle} className="text-danger" />
|
||||
) : (
|
||||
<FontAwesomeIcon icon={faCheckCircle} className="text-success" />
|
||||
)}
|
||||
return (
|
||||
<div className="d-flex mb-2">
|
||||
<div className="p-2 flex-fill my-auto flex-grow-0 flex-shrink-0">
|
||||
{inProgress ? (
|
||||
<FontAwesomeIcon icon={faCircleNotch} spin />
|
||||
) : error ? (
|
||||
<FontAwesomeIcon
|
||||
icon={faExclamationCircle}
|
||||
className="text-danger"
|
||||
/>
|
||||
) : (
|
||||
<FontAwesomeIcon icon={faCheckCircle} className="text-success" />
|
||||
)}
|
||||
</div>
|
||||
<div className="p-2 mr-1 flex-fill my-auto flex-grow-0 flex-shrink-0">
|
||||
{cluster}
|
||||
</div>
|
||||
<div
|
||||
className={`p-2 flex-fill flex-grow-1 flex-shrink-1 rounded text-center ${
|
||||
error ? "bg-light" : ""
|
||||
}`}
|
||||
>
|
||||
{error ? (
|
||||
error
|
||||
) : response && responseURI ? (
|
||||
<a
|
||||
href={`${publicURIs[responseURI]}/#/silences/${response.silenceID}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{response.silenceID}
|
||||
</a>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-2 mr-1 flex-fill my-auto flex-grow-0 flex-shrink-0">
|
||||
{cluster}
|
||||
</div>
|
||||
<div
|
||||
className={`p-2 flex-fill flex-grow-1 flex-shrink-1 rounded text-center ${
|
||||
error ? "bg-light" : ""
|
||||
}`}
|
||||
>
|
||||
{error ? (
|
||||
error
|
||||
) : response && responseURI ? (
|
||||
<a
|
||||
href={`${publicURIs[responseURI]}/#/silences/${response.silenceID}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{response.silenceID}
|
||||
</a>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
);
|
||||
}
|
||||
);
|
||||
SilenceSubmitProgress.propTypes = {
|
||||
cluster: PropTypes.string.isRequired,
|
||||
members: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<SilenceModalContent /> Editor renders SilenceSubmitController when silenceFormStore.data.currentStage is 'Submit' 1`] = `"<div class=\\"modal-header py-2\\"><nav class=\\"nav nav-pills nav-justified w-100\\"><span class=\\"nav-item nav-link cursor-pointer mx-1 px-2 active\\">Silence submitted</span><span class=\\"nav-item nav-link cursor-pointer mx-1 px-2 components-tab-inactive\\">Browse</span><button type=\\"button\\" class=\\"close\\"><span>×</span></button></nav></div><div class=\\"modal-body \\"><div></div><div class=\\"d-flex flex-row-reverse\\"><button type=\\"button\\" class=\\"btn btn-primary\\"><svg aria-hidden=\\"true\\" focusable=\\"false\\" data-prefix=\\"fas\\" data-icon=\\"arrow-left\\" class=\\"svg-inline--fa fa-arrow-left fa-w-14 pr-1\\" role=\\"img\\" xmlns=\\"http://www.w3.org/2000/svg\\" viewBox=\\"0 0 448 512\\"><path fill=\\"currentColor\\" d=\\"M257.5 445.1l-22.2 22.2c-9.4 9.4-24.6 9.4-33.9 0L7 273c-9.4-9.4-9.4-24.6 0-33.9L201.4 44.7c9.4-9.4 24.6-9.4 33.9 0l22.2 22.2c9.5 9.5 9.3 25-.4 34.3L136.6 216H424c13.3 0 24 10.7 24 24v32c0 13.3-10.7 24-24 24H136.6l120.5 114.8c9.8 9.3 10 24.8.4 34.3z\\"></path></svg>Back</button></div></div>"`;
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useObserver } from "mobx-react-lite";
|
||||
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faBellSlash } from "@fortawesome/free-solid-svg-icons/faBellSlash";
|
||||
@@ -20,55 +20,53 @@ const SilenceModalContent = React.lazy(() =>
|
||||
}))
|
||||
);
|
||||
|
||||
const SilenceModal = observer(
|
||||
({ alertStore, silenceFormStore, settingsStore }) => {
|
||||
const onDeleteModalClose = React.useCallback(() => {
|
||||
const event = new CustomEvent("remountModal");
|
||||
window.dispatchEvent(event);
|
||||
}, []);
|
||||
const SilenceModal = ({ alertStore, silenceFormStore, settingsStore }) => {
|
||||
const onDeleteModalClose = React.useCallback(() => {
|
||||
const event = new CustomEvent("remountModal");
|
||||
window.dispatchEvent(event);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<li
|
||||
className={`nav-item components-navbar-button ${
|
||||
silenceFormStore.toggle.visible ? "border-info" : ""
|
||||
}`}
|
||||
>
|
||||
<TooltipWrapper title="New silence">
|
||||
<span
|
||||
id="components-new-silence"
|
||||
className="nav-link cursor-pointer"
|
||||
onClick={silenceFormStore.toggle.toggle}
|
||||
>
|
||||
<FontAwesomeIcon icon={faBellSlash} />
|
||||
</span>
|
||||
</TooltipWrapper>
|
||||
</li>
|
||||
<Modal
|
||||
isOpen={silenceFormStore.toggle.visible}
|
||||
toggleOpen={silenceFormStore.toggle.toggle}
|
||||
onExited={silenceFormStore.data.resetProgress}
|
||||
>
|
||||
<React.Suspense
|
||||
fallback={
|
||||
<h1 className="display-1 text-placeholder p-5 m-auto">
|
||||
<FontAwesomeIcon icon={faSpinner} size="lg" spin />
|
||||
</h1>
|
||||
}
|
||||
return useObserver(() => (
|
||||
<React.Fragment>
|
||||
<li
|
||||
className={`nav-item components-navbar-button ${
|
||||
silenceFormStore.toggle.visible ? "border-info" : ""
|
||||
}`}
|
||||
>
|
||||
<TooltipWrapper title="New silence">
|
||||
<span
|
||||
id="components-new-silence"
|
||||
className="nav-link cursor-pointer"
|
||||
onClick={silenceFormStore.toggle.toggle}
|
||||
>
|
||||
<SilenceModalContent
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
settingsStore={settingsStore}
|
||||
onHide={silenceFormStore.toggle.hide}
|
||||
onDeleteModalClose={onDeleteModalClose}
|
||||
/>
|
||||
</React.Suspense>
|
||||
</Modal>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
);
|
||||
<FontAwesomeIcon icon={faBellSlash} />
|
||||
</span>
|
||||
</TooltipWrapper>
|
||||
</li>
|
||||
<Modal
|
||||
isOpen={silenceFormStore.toggle.visible}
|
||||
toggleOpen={silenceFormStore.toggle.toggle}
|
||||
onExited={silenceFormStore.data.resetProgress}
|
||||
>
|
||||
<React.Suspense
|
||||
fallback={
|
||||
<h1 className="display-1 text-placeholder p-5 m-auto">
|
||||
<FontAwesomeIcon icon={faSpinner} size="lg" spin />
|
||||
</h1>
|
||||
}
|
||||
>
|
||||
<SilenceModalContent
|
||||
alertStore={alertStore}
|
||||
silenceFormStore={silenceFormStore}
|
||||
settingsStore={settingsStore}
|
||||
onHide={silenceFormStore.toggle.hide}
|
||||
onDeleteModalClose={onDeleteModalClose}
|
||||
/>
|
||||
</React.Suspense>
|
||||
</Modal>
|
||||
</React.Fragment>
|
||||
));
|
||||
};
|
||||
SilenceModal.propTypes = {
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired,
|
||||
|
||||
Reference in New Issue
Block a user