diff --git a/ui/src/Components/SilenceModal/SilenceMatch/MatchCounter.js b/ui/src/Components/SilenceModal/SilenceMatch/MatchCounter.js
index 2f1f2734a..bf9b6669e 100644
--- a/ui/src/Components/SilenceModal/SilenceMatch/MatchCounter.js
+++ b/ui/src/Components/SilenceModal/SilenceMatch/MatchCounter.js
@@ -1,8 +1,7 @@
-import React, { Component } from "react";
+import React, { useEffect, useCallback } from "react";
import PropTypes from "prop-types";
-import { observable, action } from "mobx";
-import { observer } from "mobx-react";
+import { useObserver, useLocalStore } from "mobx-react";
import throttle from "lodash/throttle";
@@ -17,34 +16,20 @@ import { TooltipWrapper } from "Components/TooltipWrapper";
import { FetchGet } from "Common/Fetch";
import { MatcherToFilter, AlertManagersToFilter } from "../Matchers";
-const MatchCounter = observer(
- class MatchCounter extends Component {
- static propTypes = {
- silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired,
- matcher: SilenceFormMatcher.isRequired,
- };
-
- matchedAlerts = observable(
- {
- total: null,
- error: null,
- fetch: null,
- setTotal(value) {
- this.total = value;
- },
- setError(value) {
- this.error = value;
- },
- },
- {
- setTotal: action,
- setError: action,
- }
- );
-
- onFetch = throttle(() => {
- const { silenceFormStore, matcher } = this.props;
+const MatchCounter = ({ silenceFormStore, matcher }) => {
+ const matchedAlerts = useLocalStore(() => ({
+ total: null,
+ error: null,
+ setTotal(value) {
+ this.total = value;
+ },
+ setError(value) {
+ this.error = value;
+ },
+ }));
+ const onFetch = useCallback(
+ throttle(() => {
const filters = [MatcherToFilter(matcher)];
if (silenceFormStore.data.alertmanagers.length) {
filters.push(
@@ -55,66 +40,57 @@ const MatchCounter = observer(
const alertsURI =
FormatBackendURI("alerts.json?") + FormatAlertsQ(filters);
- this.matchedAlerts.fetch = FetchGet(alertsURI, {})
+ FetchGet(alertsURI, {})
.then((result) => {
return result.json();
})
.then((result) => {
- this.matchedAlerts.setTotal(result.totalAlerts);
- this.matchedAlerts.setError(null);
+ matchedAlerts.setTotal(result.totalAlerts);
+ matchedAlerts.setError(null);
})
.catch((err) => {
console.trace(err);
- return this.matchedAlerts.setError(err.message);
+ return matchedAlerts.setError(err.message);
});
- }, 300);
+ }, 300),
+ [matcher.name]
+ );
- onUpdateCounter = () => {
- const { matcher } = this.props;
-
- if (matcher.name === "" || matcher.values.length === 0) {
- this.matchedAlerts.setTotal(0);
- this.matchedAlerts.setError(null);
- return;
- }
-
- this.onFetch();
- };
-
- componentDidMount() {
- this.onUpdateCounter();
+ useEffect(() => {
+ if (matcher.name === "" || matcher.values.length === 0) {
+ matchedAlerts.setTotal(0);
+ matchedAlerts.setError(null);
+ } else {
+ onFetch();
}
+ }, [matchedAlerts, matcher.name, matcher.values.length, onFetch]);
- render() {
- if (this.matchedAlerts.error !== null) {
- return (
-
-
-
- );
- }
-
- return (
-
-
- {this.matchedAlerts.total === null ? (
-
- ) : (
- this.matchedAlerts.total
- )}
-
-
- );
- }
- }
-);
+ return useObserver(() =>
+ matchedAlerts.error !== null ? (
+
+
+
+ ) : (
+
+
+ {matchedAlerts.total === null ? (
+
+ ) : (
+ matchedAlerts.total
+ )}
+
+
+ )
+ );
+};
+MatchCounter.propTypes = {
+ silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired,
+ matcher: SilenceFormMatcher.isRequired,
+};
export { MatchCounter };
diff --git a/ui/src/Components/SilenceModal/SilenceMatch/MatchCounter.test.js b/ui/src/Components/SilenceModal/SilenceMatch/MatchCounter.test.js
index de403f55b..e1667e185 100644
--- a/ui/src/Components/SilenceModal/SilenceMatch/MatchCounter.test.js
+++ b/ui/src/Components/SilenceModal/SilenceMatch/MatchCounter.test.js
@@ -38,7 +38,7 @@ describe("", () => {
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
});
- it("logs a trace on failed fetch", async () => {
+ it("logs a trace on failed fetch", (done) => {
const consoleSpy = jest
.spyOn(console, "trace")
.mockImplementation(() => {});
@@ -48,12 +48,14 @@ describe("", () => {
matcher.name = "foo";
matcher.values = [MatcherValueToObject("bar")];
- const tree = MountedMatchCounter();
- await expect(tree.instance().matchedAlerts.fetch).resolves.toBeUndefined();
- expect(consoleSpy).toHaveBeenCalled();
+ MountedMatchCounter();
+ setTimeout(() => {
+ expect(consoleSpy).toHaveBeenCalled();
+ done();
+ }, 200);
});
- it("renders error icon on failed fetch", async () => {
+ it("renders error icon on failed fetch", (done) => {
jest.spyOn(console, "trace").mockImplementation(() => {});
fetch.mockReject(new Error("Fetch error"));
@@ -62,8 +64,10 @@ describe("", () => {
matcher.values = [MatcherValueToObject("bar")];
const tree = MountedMatchCounter();
- await expect(tree.instance().matchedAlerts.fetch).resolves.toBeUndefined();
- expect(toDiffableHtml(tree.html())).toMatch(/exclamation-circle/);
+ setTimeout(() => {
+ expect(toDiffableHtml(tree.html())).toMatch(/exclamation-circle/);
+ done();
+ }, 200);
});
it("totalAlerts is 0 after mount", async () => {
@@ -72,7 +76,7 @@ describe("", () => {
expect(tree.text()).toBe("0");
});
- it("updates totalAlerts after successful fetch", async () => {
+ it("updates totalAlerts after successful fetch", (done) => {
fetch.mockResponse(JSON.stringify({ totalAlerts: 123 }));
// we need to set name & value to trigger fetch
@@ -81,49 +85,57 @@ describe("", () => {
const tree = MountedMatchCounter();
expect(tree.text()).toBe("");
- await expect(tree.instance().matchedAlerts.fetch).resolves.toBeUndefined();
- expect(tree.text()).toBe("123");
+ setTimeout(() => {
+ expect(tree.text()).toBe("123");
+ done();
+ }, 200);
});
- it("sends correct query string for a 'foo=bar' matcher", async () => {
+ it("sends correct query string for a 'foo=bar' matcher", (done) => {
fetch.mockResponse(JSON.stringify({ totalAlerts: 0 }));
matcher.name = "foo";
matcher.values = [MatcherValueToObject("bar")];
matcher.isRegex = false;
- const tree = MountedMatchCounter();
- await expect(tree.instance().matchedAlerts.fetch).resolves.toBeUndefined();
- expect(fetch.mock.calls[0][0]).toBe("./alerts.json?q=foo%3Dbar");
+ MountedMatchCounter();
+ setTimeout(() => {
+ expect(fetch.mock.calls[0][0]).toBe("./alerts.json?q=foo%3Dbar");
+ done();
+ }, 200);
});
- it("sends correct query string for a 'foo=~bar' matcher", async () => {
+ it("sends correct query string for a 'foo=~bar' matcher", (done) => {
fetch.mockResponse(JSON.stringify({ totalAlerts: 0 }));
matcher.name = "foo";
matcher.values = [MatcherValueToObject("bar")];
matcher.isRegex = true;
- const tree = MountedMatchCounter();
- await expect(tree.instance().matchedAlerts.fetch).resolves.toBeUndefined();
- expect(fetch.mock.calls[0][0]).toBe("./alerts.json?q=foo%3D~%5Ebar%24");
+ MountedMatchCounter();
+ setTimeout(() => {
+ expect(fetch.mock.calls[0][0]).toBe("./alerts.json?q=foo%3D~%5Ebar%24");
+ done();
+ }, 200);
});
- it("sends correct query string for a 'foo=~(bar|baz)' matcher", async () => {
+ it("sends correct query string for a 'foo=~(bar|baz)' matcher", (done) => {
fetch.mockResponse(JSON.stringify({ totalAlerts: 0 }));
matcher.name = "foo";
matcher.values = [MatcherValueToObject("bar"), MatcherValueToObject("baz")];
matcher.isRegex = true;
- const tree = MountedMatchCounter();
- await expect(tree.instance().matchedAlerts.fetch).resolves.toBeUndefined();
- expect(fetch.mock.calls[0][0]).toBe(
- "./alerts.json?q=foo%3D~%5E%28bar%7Cbaz%29%24"
- );
+ MountedMatchCounter();
+ setTimeout(() => {
+ expect(fetch.mock.calls[0][0]).toBe(
+ "./alerts.json?q=foo%3D~%5E%28bar%7Cbaz%29%24"
+ );
+ done();
+ }, 200);
});
- it("selecting one Alertmanager instance appends it to the filters", async () => {
+ it("selecting one Alertmanager instance appends it to the filters", (done) => {
fetch.mockResponse(JSON.stringify({ totalAlerts: 0 }));
silenceFormStore.data.alertmanagers = [MatcherValueToObject("am1")];
@@ -131,14 +143,16 @@ describe("", () => {
matcher.values = [MatcherValueToObject("bar")];
matcher.isRegex = false;
- const tree = MountedMatchCounter();
- await expect(tree.instance().matchedAlerts.fetch).resolves.toBeUndefined();
- expect(fetch.mock.calls[0][0]).toBe(
- "./alerts.json?q=foo%3Dbar&q=%40alertmanager%3D~%5E%28am1%29%24"
- );
+ MountedMatchCounter();
+ setTimeout(() => {
+ expect(fetch.mock.calls[0][0]).toBe(
+ "./alerts.json?q=foo%3Dbar&q=%40alertmanager%3D~%5E%28am1%29%24"
+ );
+ done();
+ }, 200);
});
- it("selecting two Alertmanager instances appends it correctly to the filters", async () => {
+ it("selecting two Alertmanager instances appends it correctly to the filters", (done) => {
fetch.mockResponse(JSON.stringify({ totalAlerts: 0 }));
silenceFormStore.data.alertmanagers = [
@@ -149,10 +163,12 @@ describe("", () => {
matcher.values = [MatcherValueToObject("bar")];
matcher.isRegex = false;
- const tree = MountedMatchCounter();
- await expect(tree.instance().matchedAlerts.fetch).resolves.toBeUndefined();
- expect(fetch.mock.calls[0][0]).toBe(
- "./alerts.json?q=foo%3Dbar&q=%40alertmanager%3D~%5E%28am1%7Cam1%29%24"
- );
+ MountedMatchCounter();
+ setTimeout(() => {
+ expect(fetch.mock.calls[0][0]).toBe(
+ "./alerts.json?q=foo%3Dbar&q=%40alertmanager%3D~%5E%28am1%7Cam1%29%24"
+ );
+ done();
+ }, 200);
});
});