diff --git a/ui/src/Components/MainModal/Configuration/GridLabelName.js b/ui/src/Components/MainModal/Configuration/GridLabelName.js new file mode 100644 index 000000000..e00c81428 --- /dev/null +++ b/ui/src/Components/MainModal/Configuration/GridLabelName.js @@ -0,0 +1,84 @@ +import React from "react"; +import PropTypes from "prop-types"; + +import { action, observable } from "mobx"; +import { observer } from "mobx-react"; + +import Creatable from "react-select/creatable"; + +import { FetchGet } from "Common/Fetch"; +import { FormatBackendURI } from "Stores/AlertStore"; +import { Settings } from "Stores/Settings"; +import { ThemeContext } from "Components/Theme"; + +const disabledLabel = "Disable multi-grid"; +const emptyValue = { label: disabledLabel, value: "" }; +const valueToOption = (v) => ({ label: v ? v : disabledLabel, value: v }); + +const GridLabelName = observer( + class GridLabelName extends Creatable { + static propTypes = { + settingsStore: PropTypes.instanceOf(Settings).isRequired, + }; + static contextType = ThemeContext; + + suggestions = observable({ + names: [], + }); + + populateNameSuggestions = action(() => { + this.nameSuggestionsFetch = FetchGet( + FormatBackendURI(`labelNames.json`), + {} + ) + .then( + (result) => result.json(), + (err) => { + return []; + } + ) + .then((result) => { + this.suggestions.names = [ + ...[emptyValue], + ...result.map((value) => ({ + label: value, + value: value, + })), + ]; + }) + .catch((err) => { + console.error(err.message); + this.suggestions.names = [emptyValue]; + }); + }); + + onChange = action((newValue, actionMeta) => { + const { settingsStore } = this.props; + + settingsStore.multiGridConfig.config.gridLabel = newValue.value; + }); + + componentDidMount() { + this.populateNameSuggestions(); + } + + render() { + const { settingsStore } = this.props; + + return ( + + ); + } + } +); + +export { GridLabelName }; diff --git a/ui/src/Components/MainModal/Configuration/MultiGridConfiguration.js b/ui/src/Components/MainModal/Configuration/MultiGridConfiguration.js new file mode 100644 index 000000000..fe00ab019 --- /dev/null +++ b/ui/src/Components/MainModal/Configuration/MultiGridConfiguration.js @@ -0,0 +1,62 @@ +import React, { Component } from "react"; +import PropTypes from "prop-types"; + +import { action } from "mobx"; +import { observer } from "mobx-react"; + +import { Settings } from "Stores/Settings"; +import { ThemeContext } from "Components/Theme"; +import { GridLabelName } from "./GridLabelName"; + +const MultiGridConfiguration = observer( + class MultiGridConfiguration extends Component { + static propTypes = { + settingsStore: PropTypes.instanceOf(Settings).isRequired, + }; + static contextType = ThemeContext; + + onSortReverseChange = action((event) => { + const { settingsStore } = this.props; + + settingsStore.multiGridConfig.config.gridSortReverse = + event.target.checked; + }); + + render() { + const { settingsStore } = this.props; + + return ( +
+
+
+ +
+
+ + + + +
+
+
+ ); + } + } +); + +export { MultiGridConfiguration }; diff --git a/ui/src/Components/MainModal/Configuration/MultiGridConfiguration.test.js b/ui/src/Components/MainModal/Configuration/MultiGridConfiguration.test.js new file mode 100644 index 000000000..71370cbb8 --- /dev/null +++ b/ui/src/Components/MainModal/Configuration/MultiGridConfiguration.test.js @@ -0,0 +1,100 @@ +import React from "react"; + +import { mount } from "enzyme"; + +import toDiffableHtml from "diffable-html"; + +import { Settings } from "Stores/Settings"; +import { ThemeContext } from "Components/Theme"; +import { + ReactSelectColors, + ReactSelectStyles, +} from "Components/Theme/ReactSelect"; +import { MultiGridConfiguration } from "./MultiGridConfiguration"; + +let settingsStore; +beforeEach(() => { + fetch.mockResponse(JSON.stringify([])); + settingsStore = new Settings(); +}); + +afterEach(() => { + jest.restoreAllMocks(); +}); + +const FakeConfiguration = () => { + return mount( + + + + ); +}; + +const ExpandSortLabelSuggestions = async () => { + settingsStore.gridConfig.config.sortOrder = + settingsStore.gridConfig.options.label.value; + const tree = FakeConfiguration(); + const labelSelect = tree.find("GridLabelName"); + await expect( + labelSelect.instance().nameSuggestionsFetch + ).resolves.toBeUndefined(); + + tree + .find("input#react-select-configuration-grid-label-input") + .simulate("change", { target: { value: "a" } }); + + fetch.resetMocks(); + return tree; +}; + +describe("", () => { + it("matches snapshot with default values", () => { + const tree = FakeConfiguration(); + expect(toDiffableHtml(tree.html())).toMatchSnapshot(); + }); + + it("label select handles fetch errors", async () => { + fetch.mockReject(new Error("Fetch error")); + const tree = await ExpandSortLabelSuggestions(); + const options = tree.find("div.react-select__option"); + expect(options).toHaveLength(1); + expect(options.text()).toBe("Disable multi-grid"); + }); + + it("label select handles invalid JSON", async () => { + jest.spyOn(console, "error").mockImplementation(() => {}); + fetch.mockResponse("invalid JSON"); + const tree = await ExpandSortLabelSuggestions(); + const options = tree.find("div.react-select__option"); + expect(options).toHaveLength(1); + expect(options.text()).toBe("Disable multi-grid"); + }); + + it("clicking on a label option updates settingsStore", async (done) => { + fetch.mockResponse(JSON.stringify(["alertname", "cluster", "fakeLabel"])); + const tree = await ExpandSortLabelSuggestions(); + const options = tree.find("div.react-select__option"); + options.at(2).simulate("click"); + setTimeout(() => { + expect(settingsStore.multiGridConfig.config.gridLabel).toBe("cluster"); + done(); + }, 200); + }); + + it("clicking on the 'reverse' checkbox updates settingsStore", (done) => { + settingsStore.gridConfig.config.reverseSort = false; + const tree = FakeConfiguration(); + const checkbox = tree.find("#configuration-multigrid-sort-reverse"); + + expect(settingsStore.gridConfig.config.reverseSort).toBe(false); + checkbox.simulate("change", { target: { checked: true } }); + setTimeout(() => { + expect(settingsStore.multiGridConfig.config.gridSortReverse).toBe(true); + done(); + }, 200); + }); +}); diff --git a/ui/src/Components/MainModal/Configuration/__snapshots__/MultiGridConfiguration.test.js.snap b/ui/src/Components/MainModal/Configuration/__snapshots__/MultiGridConfiguration.test.js.snap new file mode 100644 index 000000000..038600ff6 --- /dev/null +++ b/ui/src/Components/MainModal/Configuration/__snapshots__/MultiGridConfiguration.test.js.snap @@ -0,0 +1,72 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` matches snapshot with default values 1`] = ` +" +
+
+
+
+
+
+
+ Disable multi-grid +
+
+
+ +
+
+
+
+
+
+ + +
+ + + + +
+
+
+
+
+
+ + + + +
+
+
+" +`; diff --git a/ui/src/Components/MainModal/Configuration/__snapshots__/index.test.js.snap b/ui/src/Components/MainModal/Configuration/__snapshots__/index.test.js.snap index 795c6b915..d0d7ad8ca 100644 --- a/ui/src/Components/MainModal/Configuration/__snapshots__/index.test.js.snap +++ b/ui/src/Components/MainModal/Configuration/__snapshots__/index.test.js.snap @@ -521,6 +521,103 @@ exports[` matches snapshot 1`] = ` +
+
+
+
+ Multi-grid +
+
+ + + + +
+
+
+
+
+
+
+
+
+
+
+
+ Disable multi-grid +
+
+
+ +
+
+
+
+
+
+ + +
+ + + + +
+
+
+
+
+
+ + + + +
+
+
+
+
+
" `; diff --git a/ui/src/Components/MainModal/Configuration/index.js b/ui/src/Components/MainModal/Configuration/index.js index 79b1df0bc..ee7233933 100644 --- a/ui/src/Components/MainModal/Configuration/index.js +++ b/ui/src/Components/MainModal/Configuration/index.js @@ -11,6 +11,7 @@ import { AlertGroupSortConfiguration } from "./AlertGroupSortConfiguration"; import { AlertGroupCollapseConfiguration } from "./AlertGroupCollapseConfiguration"; import { AlertGroupTitleBarColor } from "./AlertGroupTitleBarColor"; import { ThemeConfiguration } from "./ThemeConfiguration"; +import { MultiGridConfiguration } from "./MultiGridConfiguration"; const Configuration = ({ settingsStore, defaultIsOpen }) => (
@@ -56,6 +57,11 @@ const Configuration = ({ settingsStore, defaultIsOpen }) => ( content={} extraProps={{ open: defaultIsOpen }} /> + } + extraProps={{ open: defaultIsOpen }} + /> ); Configuration.propTypes = { diff --git a/ui/src/Components/MainModal/Configuration/index.test.js b/ui/src/Components/MainModal/Configuration/index.test.js index 3c3a4b5d1..16a385b94 100644 --- a/ui/src/Components/MainModal/Configuration/index.test.js +++ b/ui/src/Components/MainModal/Configuration/index.test.js @@ -12,6 +12,14 @@ import { } from "Components/Theme/ReactSelect"; import { Configuration } from "."; +beforeEach(() => { + fetch.mockResponse(JSON.stringify([])); +}); + +afterEach(() => { + jest.restoreAllMocks(); +}); + describe("", () => { it("matches snapshot", () => { const settingsStore = new Settings(); diff --git a/ui/src/Components/MainModal/MainModalContent.test.js b/ui/src/Components/MainModal/MainModalContent.test.js index 535e8c9ff..4588f3fbf 100644 --- a/ui/src/Components/MainModal/MainModalContent.test.js +++ b/ui/src/Components/MainModal/MainModalContent.test.js @@ -21,6 +21,7 @@ beforeEach(() => { alertStore = new AlertStore([]); settingsStore = new Settings(); onHide.mockClear(); + fetch.mockResponse(JSON.stringify([])); }); afterEach(() => { diff --git a/ui/src/Components/MainModal/__snapshots__/MainModalContent.test.js.snap b/ui/src/Components/MainModal/__snapshots__/MainModalContent.test.js.snap index 6ed928c0e..32624a7e2 100644 --- a/ui/src/Components/MainModal/__snapshots__/MainModalContent.test.js.snap +++ b/ui/src/Components/MainModal/__snapshots__/MainModalContent.test.js.snap @@ -540,6 +540,103 @@ exports[` matches snapshot 1`] = ` +
+
+
+
+ Multi-grid +
+
+ + + + +
+
+
+
+
+
+
+
+
+
+
+
+ Disable multi-grid +
+
+
+ +
+
+
+
+
+
+ + +
+ + + + +
+
+
+
+
+
+ + + + +
+
+
+
+
+
diff --git a/ui/src/Components/MainModal/index.test.js b/ui/src/Components/MainModal/index.test.js index f2fd39f43..fa8e5fa6a 100644 --- a/ui/src/Components/MainModal/index.test.js +++ b/ui/src/Components/MainModal/index.test.js @@ -21,6 +21,12 @@ beforeAll(() => { beforeEach(() => { alertStore = new AlertStore([]); settingsStore = new Settings(); + + fetch.mockResponse(JSON.stringify([])); +}); + +afterEach(() => { + jest.restoreAllMocks(); }); const MountedMainModal = () => {