diff --git a/ui/src/Components/Grid/ReloadNeeded/__snapshots__/index.test.js.snap b/ui/src/Components/Grid/ReloadNeeded/__snapshots__/index.test.js.snap
new file mode 100644
index 000000000..74e31e607
--- /dev/null
+++ b/ui/src/Components/Grid/ReloadNeeded/__snapshots__/index.test.js.snap
@@ -0,0 +1,41 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` matches snapshot 1`] = `
+"
+
+
+
+
+
+ All API connection attempts failed. This migth be caused by authentication middleware, will try to reload.
+
+
+
+"
+`;
diff --git a/ui/src/Components/Grid/ReloadNeeded/index.js b/ui/src/Components/Grid/ReloadNeeded/index.js
new file mode 100644
index 000000000..1b1539792
--- /dev/null
+++ b/ui/src/Components/Grid/ReloadNeeded/index.js
@@ -0,0 +1,48 @@
+import React, { Component } from "react";
+import PropTypes from "prop-types";
+
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons/faExclamationCircle";
+import { faSpinner } from "@fortawesome/free-solid-svg-icons/faSpinner";
+
+import { CenteredMessage } from "Components/CenteredMessage";
+
+class ReloadNeeded extends Component {
+ static propTypes = {
+ reloadAfter: PropTypes.number.isRequired
+ };
+
+ reloadApp = () => {
+ window.location.reload();
+ };
+
+ componentDidMount() {
+ const { reloadAfter } = this.props;
+ this.timer = setTimeout(this.reloadApp, reloadAfter);
+ }
+
+ componentWillUnmount() {
+ clearTimeout(this.timer);
+ this.timer = null;
+ }
+
+ render() {
+ return (
+
+
+
+
+
+ All API connection attempts failed. This migth be caused by
+ authentication middleware, will try to reload.
+
+
+
+ );
+ }
+}
+
+export { ReloadNeeded };
diff --git a/ui/src/Components/Grid/ReloadNeeded/index.test.js b/ui/src/Components/Grid/ReloadNeeded/index.test.js
new file mode 100644
index 000000000..91fdc7c5a
--- /dev/null
+++ b/ui/src/Components/Grid/ReloadNeeded/index.test.js
@@ -0,0 +1,43 @@
+import React from "react";
+
+import { mount, shallow } from "enzyme";
+
+import toDiffableHtml from "diffable-html";
+
+import { ReloadNeeded } from ".";
+
+beforeEach(() => {
+ jest.useFakeTimers();
+ jest.clearAllTimers();
+});
+
+afterEach(() => {
+ jest.clearAllTimers();
+ jest.restoreAllMocks();
+});
+
+describe("", () => {
+ it("matches snapshot", () => {
+ const tree = shallow(
+
+ );
+ expect(toDiffableHtml(tree.html())).toMatchSnapshot();
+ });
+
+ it("calls window.location.reload after timer is done", () => {
+ const reloadSpy = jest
+ .spyOn(global.window.location, "reload")
+ .mockImplementation(() => {});
+ mount();
+ jest.runOnlyPendingTimers();
+ expect(reloadSpy).toBeCalled();
+ });
+
+ it("timer is cleared on unmount", () => {
+ const tree = mount();
+ const instance = tree.instance();
+
+ instance.componentWillUnmount();
+ expect(instance.timer).toBeNull();
+ });
+});
diff --git a/ui/src/Components/Grid/UpgradeNeeded/__snapshots__/index.test.js.snap b/ui/src/Components/Grid/UpgradeNeeded/__snapshots__/index.test.js.snap
index 6a55d3cea..1948febd8 100644
--- a/ui/src/Components/Grid/UpgradeNeeded/__snapshots__/index.test.js.snap
+++ b/ui/src/Components/Grid/UpgradeNeeded/__snapshots__/index.test.js.snap
@@ -25,7 +25,7 @@ exports[` matches snapshot 1`] = `
focusable=\\"false\\"
data-prefix=\\"fas\\"
data-icon=\\"spinner\\"
- class=\\"svg-inline--fa fa-spinner fa-w-16 fa-spin mr-1\\"
+ class=\\"svg-inline--fa fa-spinner fa-w-16 fa-spin mr-2\\"
role=\\"img\\"
xmlns=\\"http://www.w3.org/2000/svg\\"
viewbox=\\"0 0 512 512\\"
diff --git a/ui/src/Components/Grid/UpgradeNeeded/index.js b/ui/src/Components/Grid/UpgradeNeeded/index.js
index f2ebe92a1..768b812e3 100644
--- a/ui/src/Components/Grid/UpgradeNeeded/index.js
+++ b/ui/src/Components/Grid/UpgradeNeeded/index.js
@@ -41,7 +41,7 @@ class UpgradeNeeded extends Component {
/>
-
+
Upgrading to a new version: {newVersion}
diff --git a/ui/src/Components/Grid/index.js b/ui/src/Components/Grid/index.js
index 83818de40..77cf3c233 100644
--- a/ui/src/Components/Grid/index.js
+++ b/ui/src/Components/Grid/index.js
@@ -10,6 +10,7 @@ import { AlertGrid } from "./AlertGrid";
import { FatalError } from "./FatalError";
import { UpstreamError } from "./UpstreamError";
import { UpgradeNeeded } from "./UpgradeNeeded";
+import { ReloadNeeded } from "./ReloadNeeded";
import { EmptyGrid } from "./EmptyGrid";
const Grid = observer(
@@ -23,10 +24,6 @@ const Grid = observer(
render() {
const { alertStore, settingsStore, silenceFormStore } = this.props;
- if (alertStore.status.error) {
- return ;
- }
-
if (alertStore.info.upgradeNeeded) {
return (
;
+ }
+
+ if (alertStore.status.error) {
+ return ;
+ }
+
if (
alertStore.data.upstreams.counters &&
alertStore.data.upstreams.counters.total === 1 &&
diff --git a/ui/src/Components/Grid/index.stories.js b/ui/src/Components/Grid/index.stories.js
index d5ec644e1..e4f977ad7 100644
--- a/ui/src/Components/Grid/index.stories.js
+++ b/ui/src/Components/Grid/index.stories.js
@@ -8,6 +8,7 @@ import { Settings } from "Stores/Settings";
import { SilenceFormStore } from "Stores/SilenceFormStore";
import { FatalError } from "./FatalError";
import { UpgradeNeeded } from "./UpgradeNeeded";
+import { ReloadNeeded } from "./ReloadNeeded";
import { EmptyGrid } from "./EmptyGrid";
import { Grid } from ".";
import { InternalError } from "../../ErrorBoundary";
@@ -32,6 +33,9 @@ storiesOf("Grid", module)
.add("UpgradeNeeded", () => {
return ;
})
+ .add("ReloadNeeded", () => {
+ return ;
+ })
.add("EmptyGrid", () => {
return (
diff --git a/ui/src/Components/Grid/index.test.js b/ui/src/Components/Grid/index.test.js
index 49503e643..722eb8545 100644
--- a/ui/src/Components/Grid/index.test.js
+++ b/ui/src/Components/Grid/index.test.js
@@ -94,6 +94,12 @@ describe("", () => {
expect(tree.text()).toBe("");
});
+ it("renders ReloadNeeded when alertStore.info.reloadNeeded=true", () => {
+ alertStore.info.reloadNeeded = true;
+ const tree = ShallowGrid();
+ expect(tree.text()).toBe("");
+ });
+
it("renders AlertGrid before any fetch finished when totalAlerts is 0", () => {
alertStore.info.version = "unknown";
alertStore.info.totalAlerts = 0;
diff --git a/ui/src/Components/NavBar/FilterInput/index.test.js b/ui/src/Components/NavBar/FilterInput/index.test.js
index d5a15c2ad..b8f831c9e 100644
--- a/ui/src/Components/NavBar/FilterInput/index.test.js
+++ b/ui/src/Components/NavBar/FilterInput/index.test.js
@@ -203,7 +203,7 @@ describe("", () => {
tree.find("input").simulate("change", { target: { value: "bar" } });
await WaitForFetch(tree);
- expect(fetch.mock.calls).toHaveLength(6);
+ expect(fetch.mock.calls).toHaveLength(11);
expect(fetch.mock.calls[0]).toContain("./autocomplete.json?term=bar");
expect(instance.inputStore.suggestions).toHaveLength(0);
});
diff --git a/ui/src/Components/SilenceModal/SilencePreview/index.test.js b/ui/src/Components/SilenceModal/SilencePreview/index.test.js
index 18c123d9b..ef907878e 100644
--- a/ui/src/Components/SilenceModal/SilencePreview/index.test.js
+++ b/ui/src/Components/SilenceModal/SilencePreview/index.test.js
@@ -90,6 +90,7 @@ describe("", () => {
{
method: "GET",
credentials: "include",
+ mode: "cors",
redirect: "follow"
}
);
@@ -108,6 +109,7 @@ describe("", () => {
{
method: "GET",
credentials: "include",
+ mode: "cors",
redirect: "follow"
}
);