diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/Silence/index.js b/ui/src/Components/Grid/AlertGrid/AlertGroup/Silence/index.js
index a265583e9..40742cb7f 100644
--- a/ui/src/Components/Grid/AlertGrid/AlertGroup/Silence/index.js
+++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/Silence/index.js
@@ -34,7 +34,7 @@ SilenceComment.propTypes = {
silence: PropTypes.object.isRequired
};
-const SilenceExpiryBadgeWithProgress = ({ silence }) => {
+const SilenceExpiryBadgeWithProgress = ({ silence, progress }) => {
// if silence is expired we can skip progress value calculation
if (moment(silence.endsAt) < moment()) {
return (
@@ -44,15 +44,10 @@ const SilenceExpiryBadgeWithProgress = ({ silence }) => {
);
}
- const durationDone = moment().unix() - moment(silence.startsAt).unix();
- const durationTotal =
- moment(silence.endsAt).unix() - moment(silence.startsAt).unix();
- const durationPercent = (durationDone / durationTotal) * 100;
-
let progressClass;
- if (durationPercent > 90) {
+ if (progress > 90) {
progressClass = "progress-bar bg-danger";
- } else if (durationPercent > 75) {
+ } else if (progress > 75) {
progressClass = "progress-bar bg-warning";
} else {
progressClass = "progress-bar bg-success";
@@ -65,8 +60,8 @@ const SilenceExpiryBadgeWithProgress = ({ silence }) => {
@@ -75,7 +70,8 @@ const SilenceExpiryBadgeWithProgress = ({ silence }) => {
);
};
SilenceExpiryBadgeWithProgress.propTypes = {
- silence: PropTypes.object.isRequired
+ silence: PropTypes.object.isRequired,
+ progress: PropTypes.number.isRequired
};
const SilenceDetails = ({ alertmanager, silence }) => {
@@ -164,12 +160,34 @@ const Silence = inject("alertStore")(
{ name: "Silence collpase toggle" }
);
- componentDidUpdate() {
- const { afterUpdate } = this.props;
- afterUpdate();
+ progress = observable(
+ {
+ value: 0,
+ calculate(startsAt, endsAt) {
+ const durationDone = moment().unix() - moment(startsAt).unix();
+ const durationTotal =
+ moment(endsAt).unix() - moment(startsAt).unix();
+ const durationPercent = Math.floor(
+ (durationDone / durationTotal) * 100
+ );
+ if (this.value !== durationPercent) {
+ this.value = durationPercent;
+ }
+ }
+ },
+ {
+ calculate: action.bound
+ }
+ );
+
+ constructor(props) {
+ super(props);
+
+ this.recalculateProgress();
+ this.progressTimer = setInterval(this.recalculateProgress, 30 * 1000);
}
- render() {
+ getSilence = () => {
const { alertStore, alertmanager, silenceID } = this.props;
// We pass alertmanager name and silence ID to Silence component
@@ -177,16 +195,36 @@ const Silence = inject("alertStore")(
// Data might be missing from the store so first check if we have
// anything for this alertmanager instance
const amSilences = alertStore.data.silences[alertmanager.name];
- if (!amSilences)
- return (
-
- );
+ if (!amSilences) return null;
// next check if alertmanager has our silence ID
const silence = amSilences[silenceID];
+ if (!silence) return null;
+
+ return silence;
+ };
+
+ recalculateProgress = () => {
+ const silence = this.getSilence();
+ if (silence !== null) {
+ this.progress.calculate(silence.startsAt, silence.endsAt);
+ }
+ };
+
+ componentDidUpdate() {
+ const { afterUpdate } = this.props;
+ afterUpdate();
+ }
+
+ componentWillUnmount() {
+ clearInterval(this.progressTimer);
+ this.progressTimer = null;
+ }
+
+ render() {
+ const { alertmanager, silenceID } = this.props;
+
+ const silence = this.getSilence();
if (!silence)
return (
{this.collapse.value ? (
-
+
) : null}
diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/Silence/index.test.js b/ui/src/Components/Grid/AlertGrid/AlertGroup/Silence/index.test.js
index 4eb9b29de..dd23a3930 100644
--- a/ui/src/Components/Grid/AlertGrid/AlertGroup/Silence/index.test.js
+++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/Silence/index.test.js
@@ -1,5 +1,6 @@
import React from "react";
+import { toJS } from "mobx";
import { Provider } from "mobx-react";
import { mount, shallow } from "enzyme";
@@ -9,7 +10,7 @@ import toDiffableHtml from "diffable-html";
import { advanceTo, clear } from "jest-date-mock";
import { AlertStore } from "Stores/AlertStore";
-import { Silence, SilenceDetails, SilenceExpiryBadgeWithProgress } from ".";
+import { Silence, SilenceDetails } from ".";
const mockAfterUpdate = jest.fn();
@@ -150,6 +151,14 @@ describe("", () => {
expect(link).toHaveLength(1);
expect(link.text()).toBe("Fake silence");
});
+
+ it("clears progress timer on unmount", () => {
+ const tree = MountedSilence().find("Silence");
+ const instance = tree.instance();
+ expect(instance.progressTimer).toBeTruthy();
+ instance.componentWillUnmount();
+ expect(instance.progressTimer).toBeNull();
+ });
});
const ShallowSilenceDetails = () => {
@@ -173,27 +182,37 @@ describe("", () => {
});
});
-const ShallowSilenceExpiryBadgeWithProgress = () => {
- return shallow();
-};
-
describe("", () => {
it("renders with class 'danger' and no progressbar when expired", () => {
advanceTo(new Date(2001, 0, 1, 23, 0, 0));
- const tree = ShallowSilenceExpiryBadgeWithProgress();
+ const tree = MountedSilence();
expect(tree.html()).toMatch(/badge-danger/);
- expect(tree.text()).toBe("Expired ");
+ expect(tree.text()).toMatch(/Expired a year ago/);
});
it("progressbar uses class 'danger' when > 90%", () => {
advanceTo(new Date(2000, 0, 1, 19, 30, 0));
- const tree = ShallowSilenceExpiryBadgeWithProgress();
+ const tree = MountedSilence();
expect(tree.html()).toMatch(/progress-bar bg-danger/);
});
it("progressbar uses class 'danger' when > 75%", () => {
advanceTo(new Date(2000, 0, 1, 17, 45, 0));
- const tree = ShallowSilenceExpiryBadgeWithProgress();
+ const tree = MountedSilence();
expect(tree.html()).toMatch(/progress-bar bg-warning/);
});
+
+ it("calling calculate() on progress multiple times in a row doesn't change the value", () => {
+ const startsAt = new Date(2000, 0, 1, 10, 0, 0);
+ const endsAt = new Date(2000, 0, 1, 20, 0, 0);
+
+ const tree = MountedSilence().find("Silence");
+ const instance = tree.instance();
+
+ const value = toJS(instance.progress.value);
+ instance.progress.calculate(startsAt, endsAt);
+ instance.progress.calculate(startsAt, endsAt);
+ instance.progress.calculate(startsAt, endsAt);
+ expect(toJS(instance.progress.value)).toBe(value);
+ });
});