diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/index.js b/ui/src/Components/Grid/AlertGrid/AlertGroup/index.js
index ed304967e..9c32e770a 100644
--- a/ui/src/Components/Grid/AlertGrid/AlertGroup/index.js
+++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/index.js
@@ -61,30 +61,45 @@ const AlertGroup = observer(
constructor(props) {
super(props);
+ const { alertGroupConfig } = props.settingsStore;
+
this.defaultRenderCount = toJS(
- props.settingsStore.alertGroupConfig.config.defaultRenderCount
+ alertGroupConfig.config.defaultRenderCount
);
this.renderConfig = observable({
alertsToRender: this.defaultRenderCount
});
- }
- // store collapse state, alert groups can be collapsed to only show
- // the header, this is controlled by UI element on the header itself, so
- // this observable needs to be passed down to it
- collapse = observable(
- {
- value: IsMobile(),
- toggle() {
- this.value = !this.value;
- }
- },
- {
- toggle: action.bound
- },
- { name: "Collpase toggle" }
- );
+ let defaultCollapseState;
+ switch (alertGroupConfig.config.defaultCollapseState) {
+ case alertGroupConfig.options.collapsed.value:
+ defaultCollapseState = true;
+ break;
+ case alertGroupConfig.options.collapsedOnMobile.value:
+ defaultCollapseState = IsMobile();
+ break;
+ default:
+ defaultCollapseState = false;
+ break;
+ }
+
+ // store collapse state, alert groups can be collapsed to only show
+ // the header, this is controlled by UI element on the header itself, so
+ // this observable needs to be passed down to it
+ this.collapse = observable(
+ {
+ value: defaultCollapseState,
+ toggle() {
+ this.value = !this.value;
+ }
+ },
+ {
+ toggle: action.bound
+ },
+ { name: "Collpase toggle" }
+ );
+ }
loadMore = action(() => {
const { group } = this.props;
diff --git a/ui/src/Components/Grid/AlertGrid/AlertGroup/index.test.js b/ui/src/Components/Grid/AlertGrid/AlertGroup/index.test.js
index dee848ab6..4bebce2c3 100644
--- a/ui/src/Components/Grid/AlertGrid/AlertGroup/index.test.js
+++ b/ui/src/Components/Grid/AlertGrid/AlertGroup/index.test.js
@@ -70,6 +70,22 @@ const MountedAlertGroup = (afterUpdate, showAlertmanagers) => {
);
};
+const ValidateCollapse = (
+ innerWidth,
+ defaultCollapseState,
+ shouldBeCollapsed
+) => {
+ global.innerWidth = innerWidth;
+
+ settingsStore.alertGroupConfig.config.defaultCollapseState = defaultCollapseState;
+
+ MockAlerts(3);
+ const tree = MountedAlertGroup(jest.fn(), false);
+ const alertGroup = tree.find("AlertGroup");
+ expect(alertGroup.instance().collapse.value).toBe(shouldBeCollapsed);
+ expect(tree.find("Alert")).toHaveLength(shouldBeCollapsed ? 0 : 3);
+};
+
describe("", () => {
it("renders Alertmanager labels in footer if showAlertmanagersInFooter=true", () => {
MockAlerts(2);
@@ -109,26 +125,58 @@ describe("", () => {
expect(tree.find("ul.list-group")).toHaveLength(0);
});
- it("is expanded by default on desktop", () => {
+ it("is collapsed by default on desktop when defaultCollapseState=collapsed", () => {
// set window.innerWidth to 2k to mock a desktop window
- global.innerWidth = 2048;
-
- MockAlerts(3);
- const tree = MountedAlertGroup(jest.fn(), false);
- const alertGroup = tree.find("AlertGroup");
- expect(alertGroup.instance().collapse.value).toBe(false);
- expect(tree.find("Alert")).toHaveLength(3);
+ ValidateCollapse(
+ 2048,
+ settingsStore.alertGroupConfig.options.collapsed.value,
+ true
+ );
});
- it("is collapsed by default on mobile", () => {
+ it("is collapsed by default on mobile when defaultCollapseState=collapsed", () => {
// set window.innerWidth to <768 to mock a mobile window
- global.innerWidth = 700;
+ ValidateCollapse(
+ 767,
+ settingsStore.alertGroupConfig.options.collapsed.value,
+ true
+ );
+ });
- MockAlerts(3);
- const tree = MountedAlertGroup(jest.fn(), false);
- const alertGroup = tree.find("AlertGroup");
- expect(alertGroup.instance().collapse.value).toBe(true);
- expect(tree.find("Alert")).toHaveLength(0);
+ it("is expanded by default on desktop when defaultCollapseState=collapsedOnMobile", () => {
+ // set window.innerWidth to 2k to mock a desktop window
+ ValidateCollapse(
+ 2048,
+ settingsStore.alertGroupConfig.options.collapsedOnMobile.value,
+ false
+ );
+ });
+
+ it("is collapsed by default on mobile when defaultCollapseState=collapsedOnMobile", () => {
+ // set window.innerWidth to <768 to mock a mobile window
+ ValidateCollapse(
+ 767,
+ settingsStore.alertGroupConfig.options.collapsedOnMobile.value,
+ true
+ );
+ });
+
+ it("is expanded by default on desktop when defaultCollapseState=expanded", () => {
+ // set window.innerWidth to 2k to mock a desktop window
+ ValidateCollapse(
+ 2048,
+ settingsStore.alertGroupConfig.options.expanded.value,
+ false
+ );
+ });
+
+ it("is expanded by default on mobile when defaultCollapseState=expanded", () => {
+ // set window.innerWidth to <768 to mock a mobile window
+ ValidateCollapse(
+ 767,
+ settingsStore.alertGroupConfig.options.expanded.value,
+ false
+ );
});
});
diff --git a/ui/src/Components/MainModal/Configuration/AlertGroupCollapseConfiguration.js b/ui/src/Components/MainModal/Configuration/AlertGroupCollapseConfiguration.js
new file mode 100644
index 000000000..f5150225a
--- /dev/null
+++ b/ui/src/Components/MainModal/Configuration/AlertGroupCollapseConfiguration.js
@@ -0,0 +1,80 @@
+import React, { Component } from "react";
+import PropTypes from "prop-types";
+
+import { action } from "mobx";
+import { observer } from "mobx-react";
+
+import ReactSelect from "react-select";
+
+import { Settings } from "Stores/Settings";
+import { ReactSelectStyles } from "Components/MultiSelect";
+
+const AlertGroupCollapseConfiguration = observer(
+ class AlertGroupCollapseConfiguration extends Component {
+ static propTypes = {
+ settingsStore: PropTypes.instanceOf(Settings).isRequired
+ };
+
+ constructor(props) {
+ super(props);
+
+ this.validateConfig();
+ }
+
+ valueToOption = val => {
+ const { settingsStore } = this.props;
+
+ return {
+ label: settingsStore.alertGroupConfig.options[val].label,
+ value: val
+ };
+ };
+
+ validateConfig = action(() => {
+ const { settingsStore } = this.props;
+
+ if (
+ !Object.values(settingsStore.alertGroupConfig.options)
+ .map(o => o.value)
+ .includes(settingsStore.alertGroupConfig.config.defaultCollapseState)
+ ) {
+ settingsStore.alertGroupConfig.config.defaultCollapseState =
+ settingsStore.alertGroupConfig.options.collapsedOnMobile.value;
+ }
+ });
+
+ onCollapseChange = action((newValue, actionMeta) => {
+ const { settingsStore } = this.props;
+
+ settingsStore.alertGroupConfig.config.defaultCollapseState =
+ newValue.value;
+ });
+
+ render() {
+ const { settingsStore } = this.props;
+
+ return (
+
+
+
+
+
+
+ );
+ }
+ }
+);
+
+export { AlertGroupCollapseConfiguration };
diff --git a/ui/src/Components/MainModal/Configuration/AlertGroupCollapseConfiguration.test.js b/ui/src/Components/MainModal/Configuration/AlertGroupCollapseConfiguration.test.js
new file mode 100644
index 000000000..7145c7af2
--- /dev/null
+++ b/ui/src/Components/MainModal/Configuration/AlertGroupCollapseConfiguration.test.js
@@ -0,0 +1,69 @@
+import React from "react";
+
+import { mount } from "enzyme";
+
+import toDiffableHtml from "diffable-html";
+
+import { Settings } from "Stores/Settings";
+import { AlertGroupCollapseConfiguration } from "./AlertGroupCollapseConfiguration";
+
+let settingsStore;
+beforeEach(() => {
+ settingsStore = new Settings();
+});
+
+const FakeConfiguration = () => {
+ return mount(
+
+ );
+};
+
+describe("", () => {
+ it("matches snapshot with default values", () => {
+ const tree = FakeConfiguration();
+ expect(toDiffableHtml(tree.html())).toMatchSnapshot();
+ });
+
+ it("resets stored config to defaults if it is invalid", done => {
+ settingsStore.alertGroupConfig.config.defaultCollapseState = "foo";
+ const tree = FakeConfiguration();
+ const select = tree.find(".react-select__value-container");
+ expect(select.text()).toBe(
+ settingsStore.alertGroupConfig.options.collapsedOnMobile.label
+ );
+ setTimeout(() => {
+ expect(settingsStore.alertGroupConfig.config.defaultCollapseState).toBe(
+ settingsStore.alertGroupConfig.options.collapsedOnMobile.value
+ );
+ done();
+ }, 200);
+ });
+
+ it("rendered correct default value", done => {
+ settingsStore.alertGroupConfig.config.defaultCollapseState =
+ settingsStore.alertGroupConfig.options.expanded.value;
+ const tree = FakeConfiguration();
+ const select = tree.find(".react-select__value-container");
+ setTimeout(() => {
+ expect(select.text()).toBe(
+ settingsStore.alertGroupConfig.options.expanded.label
+ );
+ done();
+ }, 200);
+ });
+
+ it("clicking on a label option updates settingsStore", done => {
+ const tree = FakeConfiguration();
+ tree
+ .find("input#react-select-configuration-collapse-input")
+ .simulate("change", { target: { value: " " } });
+ const options = tree.find(".react-select__option");
+ options.at(1).simulate("click");
+ setTimeout(() => {
+ expect(settingsStore.alertGroupConfig.config.defaultCollapseState).toBe(
+ settingsStore.alertGroupConfig.options.collapsed.value
+ );
+ done();
+ }, 200);
+ });
+});
diff --git a/ui/src/Components/MainModal/Configuration/__snapshots__/AlertGroupCollapseConfiguration.test.js.snap b/ui/src/Components/MainModal/Configuration/__snapshots__/AlertGroupCollapseConfiguration.test.js.snap
new file mode 100644
index 000000000..7f4f118de
--- /dev/null
+++ b/ui/src/Components/MainModal/Configuration/__snapshots__/AlertGroupCollapseConfiguration.test.js.snap
@@ -0,0 +1,59 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` matches snapshot with default values 1`] = `
+"
+
+"
+`;
diff --git a/ui/src/Components/MainModal/Configuration/__snapshots__/index.test.js.snap b/ui/src/Components/MainModal/Configuration/__snapshots__/index.test.js.snap
new file mode 100644
index 000000000..12da2f170
--- /dev/null
+++ b/ui/src/Components/MainModal/Configuration/__snapshots__/index.test.js.snap
@@ -0,0 +1,234 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` matches snapshot 1`] = `
+"
+
+"
+`;
diff --git a/ui/src/Components/MainModal/Configuration/index.js b/ui/src/Components/MainModal/Configuration/index.js
index 5c9c09a53..7b9bc8a4d 100644
--- a/ui/src/Components/MainModal/Configuration/index.js
+++ b/ui/src/Components/MainModal/Configuration/index.js
@@ -6,6 +6,7 @@ import { FetchConfiguration } from "./FetchConfiguration";
import { FilterBarConfiguration } from "./FilterBarConfiguration";
import { AlertGroupConfiguration } from "./AlertGroupConfiguration";
import { AlertGroupSortConfiguration } from "./AlertGroupSortConfiguration";
+import { AlertGroupCollapseConfiguration } from "./AlertGroupCollapseConfiguration";
const Configuration = ({ settingsStore }) => (
);
diff --git a/ui/src/Components/MainModal/Configuration/index.test.js b/ui/src/Components/MainModal/Configuration/index.test.js
index 43ac7141b..3fbab613b 100644
--- a/ui/src/Components/MainModal/Configuration/index.test.js
+++ b/ui/src/Components/MainModal/Configuration/index.test.js
@@ -2,15 +2,15 @@ import React from "react";
import { shallow } from "enzyme";
+import toDiffableHtml from "diffable-html";
+
import { Settings } from "Stores/Settings";
import { Configuration } from ".";
describe("", () => {
- it("renders correctly", () => {
+ it("matches snapshot", () => {
const settingsStore = new Settings();
const tree = shallow();
- expect(tree.text()).toBe(
- ""
- );
+ expect(toDiffableHtml(tree.html())).toMatchSnapshot();
});
});
diff --git a/ui/src/Components/MainModal/__snapshots__/MainModalContent.test.js.snap b/ui/src/Components/MainModal/__snapshots__/MainModalContent.test.js.snap
index e548b3a1d..769efbafa 100644
--- a/ui/src/Components/MainModal/__snapshots__/MainModalContent.test.js.snap
+++ b/ui/src/Components/MainModal/__snapshots__/MainModalContent.test.js.snap
@@ -136,6 +136,61 @@ exports[` matches snapshot 1`] = `
+
+
+