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`] = ` +" +
+
+ +
+
+
+
+
+ Collapse on mobile +
+
+
+ +
+
+
+
+
+
+ + +
+ + + + +
+
+
+
+
+" +`; 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`] = ` +" +
+
+ +
+ + + 10s + + +
+
+
+ + + + 30s + + +
+
+
+
+ + + 120s + + +
+
+
+
+
+
+ +
+
+ + + + +
+
+
+
+
+ +
+ + + 1 + + +
+
+
+ + + + 5 + + +
+
+
+
+ + + 10 + + +
+
+
+
+
+
+ +
+
+
+
+
+ Collapse on mobile +
+
+
+ +
+
+
+
+
+
+ + +
+ + + + +
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ Use defaults from karma config file +
+
+
+ +
+
+
+
+
+
+ + +
+ + + + +
+
+
+
+
+
+
+
+" +`; 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 }) => (
@@ -15,6 +16,8 @@ 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`] = `
+
+
+ +
+
+
+
+
+ Collapse on mobile +
+
+
+ +
+
+
+
+
+
+ + +
+ + + + +
+
+
+
+
+
+