fix(ui): rewrite SilenceProgress component with hooks

This commit is contained in:
Łukasz Mierzwa
2020-04-30 17:56:18 +01:00
committed by Łukasz Mierzwa
parent 2440b7778e
commit cd41829a27
2 changed files with 67 additions and 96 deletions

View File

@@ -1,7 +1,6 @@
import React, { Component } from "react";
import React, { useEffect } from "react";
import { observable, action } from "mobx";
import { observer } from "mobx-react";
import { useObserver, useLocalStore } from "mobx-react";
import moment from "moment";
import Moment from "react-moment";
@@ -10,86 +9,58 @@ import { APISilence } from "Models/API";
import "./SilenceProgress.scss";
const SilenceProgress = observer(
class SilenceProgress extends Component {
static propTypes = {
silence: APISilence.isRequired,
};
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,
const SilenceProgress = ({ silence }) => {
const progress = useLocalStore(() => ({
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;
}
);
},
}));
constructor(props) {
super(props);
useEffect(() => {
progress.calculate(silence.startsAt, silence.endsAt);
this.recalculateProgress();
this.progressTimer = setInterval(this.recalculateProgress, 30 * 1000);
}
const timer = setInterval(() => {
progress.calculate(silence.startsAt, silence.endsAt);
}, 30 * 1000);
return () => clearInterval(timer);
}, [progress, silence.startsAt, silence.endsAt]);
componentWillUnmount() {
clearInterval(this.progressTimer);
this.progressTimer = null;
}
recalculateProgress = () => {
const { silence } = this.props;
this.progress.calculate(silence.startsAt, silence.endsAt);
};
render() {
const { silence } = this.props;
// if silence is expired we can skip progress value calculation
if (moment(silence.endsAt) < moment()) {
return (
<span className="badge badge-danger align-text-bottom p-1">
Expired <Moment fromNow>{silence.endsAt}</Moment>
</span>
);
}
let progressClass;
if (this.progress.value > 90) {
progressClass = "progress-bar bg-danger";
} else if (this.progress.value > 75) {
progressClass = "progress-bar bg-warning";
} else {
progressClass = "progress-bar bg-success";
}
return (
<span className="badge badge-light nmb-05 align-text-bottom p-1">
Expires <Moment fromNow>{silence.endsAt}</Moment>
<div className="progress silence-progress">
<div
className={progressClass}
role="progressbar"
style={{ width: this.progress.value + "%" }}
aria-valuenow={this.progress.value}
aria-valuemin="0"
aria-valuemax="100"
/>
</div>
</span>
);
}
}
);
return useObserver(() =>
moment(silence.endsAt) < moment() ? (
<span className="badge badge-danger align-text-bottom p-1">
Expired <Moment fromNow>{silence.endsAt}</Moment>
</span>
) : (
<span className="badge badge-light nmb-05 align-text-bottom p-1">
Expires <Moment fromNow>{silence.endsAt}</Moment>
<div className="progress silence-progress">
<div
className={
progress.value > 90
? "progress-bar bg-danger"
: progress.value > 75
? "progress-bar bg-warning"
: "progress-bar bg-success"
}
role="progressbar"
style={{ width: progress.value + "%" }}
aria-valuenow={progress.value}
aria-valuemin="0"
aria-valuemax="100"
/>
</div>
</span>
)
);
};
SilenceProgress.propTypes = {
silence: APISilence.isRequired,
};
export { SilenceProgress };

View File

@@ -2,8 +2,6 @@ import React from "react";
import { mount } from "enzyme";
import { toJS } from "mobx";
import toDiffableHtml from "diffable-html";
import moment from "moment";
@@ -14,6 +12,10 @@ import { SilenceProgress } from "./SilenceProgress";
let silence;
beforeAll(() => {
jest.useFakeTimers();
});
beforeEach(() => {
silence = MockSilence();
@@ -57,24 +59,22 @@ describe("<SilenceProgress />", () => {
expect(toDiffableHtml(tree.html())).toMatch(/progress-bar bg-success/);
});
it("calling calculate() on progress multiple times in a row doesn't change the value", () => {
const startsAt = moment.utc([2000, 0, 1, 0, 0, 0]);
const endsAt = moment.utc([2000, 0, 1, 1, 0, 0]);
it("progressbar is updated every 30 seconds", () => {
advanceTo(moment.utc([2000, 0, 1, 0, 30, 0]));
const tree = MountedSilenceProgress();
const instance = tree.instance();
expect(toDiffableHtml(tree.html())).toMatch(/progress-bar bg-success/);
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);
advanceTo(moment.utc([2000, 0, 1, 0, 50, 0]));
jest.runOnlyPendingTimers();
expect(toDiffableHtml(tree.html())).toMatch(/progress-bar bg-warning/);
advanceTo(moment.utc([2000, 0, 1, 0, 55, 0]));
jest.runOnlyPendingTimers();
expect(toDiffableHtml(tree.html())).toMatch(/progress-bar bg-danger/);
});
it("resets the timer on unmount", () => {
it("unmounts cleanly", () => {
const tree = MountedSilenceProgress();
expect(tree.instance().progressTimer).not.toBeNull();
tree.instance().componentWillUnmount();
expect(tree.instance().progressTimer).toBeNull();
tree.unmount();
});
});