From 53a83a0b33a098ea820dfbc20c57726be72646eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?= Date: Sun, 9 Sep 2018 21:38:01 +0100 Subject: [PATCH] fix(ui): silence expiry progress bar should be updated every 30s --- .../AlertGrid/AlertGroup/Silence/index.js | 87 ++++++++++++++----- .../AlertGroup/Silence/index.test.js | 37 ++++++-- 2 files changed, 92 insertions(+), 32 deletions(-) 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); + }); });