From 98f70108cd52b76fe90ac89a41f931febaabc55d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?= Date: Mon, 1 Mar 2021 20:12:31 +0000 Subject: [PATCH] fix(ui): use active alerts to populate grid dropdown --- CHANGELOG.md | 6 ++ .../Grid/AlertGrid/GridLabelSelect.test.tsx | 25 ++++- .../Grid/AlertGrid/GridLabelSelect.tsx | 92 ++++++++++++++++--- ui/src/Components/Grid/AlertGrid/Swimlane.tsx | 5 +- .../MainModal/Configuration/GridLabelName.tsx | 9 +- 5 files changed, 115 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c47f8e7ae..a51566808 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [unreleased] + +### Changed + +- Multi-grid label dropdown will only show label names from visible alerts. + ## v0.80 ### Added diff --git a/ui/src/Components/Grid/AlertGrid/GridLabelSelect.test.tsx b/ui/src/Components/Grid/AlertGrid/GridLabelSelect.test.tsx index 301c3a54b..466c5222f 100644 --- a/ui/src/Components/Grid/AlertGrid/GridLabelSelect.test.tsx +++ b/ui/src/Components/Grid/AlertGrid/GridLabelSelect.test.tsx @@ -5,9 +5,12 @@ import { mount } from "enzyme"; import fetchMock from "fetch-mock"; +import { MockGrid } from "__fixtures__/Stories"; +import { AlertStore } from "Stores/AlertStore"; import { Settings } from "Stores/Settings"; import { GridLabelSelect } from "./GridLabelSelect"; +let alertStore: AlertStore; let settingsStore: Settings; beforeEach(() => { @@ -16,13 +19,16 @@ beforeEach(() => { body: JSON.stringify([]), }); + alertStore = new AlertStore([]); settingsStore = new Settings(null); jest.useFakeTimers(); }); const MountedGridLabelSelect = () => { - return mount(); + return mount( + + ); }; describe("", () => { @@ -35,6 +41,7 @@ describe("", () => { it("clicking toggle renders select dropdown", async () => { const promise = Promise.resolve(); + MockGrid(alertStore); const tree = MountedGridLabelSelect(); const toggle = tree.find("span.components-grid-label-select-dropdown"); toggle.simulate("click"); @@ -42,6 +49,22 @@ describe("", () => { await act(() => promise); }); + it("clicking an option updates grid settings", async () => { + const promise = Promise.resolve(); + MockGrid(alertStore); + const tree = MountedGridLabelSelect(); + + const toggle = tree.find("span.components-grid-label-select-dropdown"); + toggle.simulate("click"); + expect(tree.find("div.components-grid-label-select-menu")).toHaveLength(1); + + settingsStore.multiGridConfig.config.gridLabel = "foo"; + const options = tree.find("div.react-select__option"); + options.at(4).simulate("click"); + expect(settingsStore.multiGridConfig.config.gridLabel).toBe("cluster"); + await act(() => promise); + }); + it("clicking toggle twice hides select dropdown", async () => { const promise = Promise.resolve(); const tree = MountedGridLabelSelect(); diff --git a/ui/src/Components/Grid/AlertGrid/GridLabelSelect.tsx b/ui/src/Components/Grid/AlertGrid/GridLabelSelect.tsx index 6be20491e..0e5425e2e 100644 --- a/ui/src/Components/Grid/AlertGrid/GridLabelSelect.tsx +++ b/ui/src/Components/Grid/AlertGrid/GridLabelSelect.tsx @@ -11,23 +11,96 @@ import { observer } from "mobx-react-lite"; import { Manager, Reference, Popper } from "react-popper"; +import AsyncSelect from "react-select/async"; + import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCaretDown } from "@fortawesome/free-solid-svg-icons/faCaretDown"; +import { AlertStore } from "Stores/AlertStore"; import { Settings } from "Stores/Settings"; import { CommonPopperModifiers } from "Common/Popper"; +import { NewLabelName, StringToOption, OptionT } from "Common/Select"; import { DropdownSlide } from "Components/Animations/DropdownSlide"; +import { ThemeContext } from "Components/Theme"; import { useOnClickOutside } from "Hooks/useOnClickOutside"; -import { GridLabelName } from "Components/MainModal/Configuration/GridLabelName"; const NullContainer: FC = () => null; +const GridLabelNameSelect: FC<{ + alertStore: AlertStore; + settingsStore: Settings; +}> = ({ alertStore, settingsStore }) => { + const loadOptions = ( + inputValue: string, + callback: (options: OptionT[]) => void + ) => { + const labelNames: { [key: string]: boolean } = { + "@alertmanager": true, + "@cluster": true, + "@receiver": true, + }; + + alertStore.data.grids.forEach((grid) => { + labelNames[grid.labelName] = true; + grid.alertGroups.forEach((group) => { + Object.keys(group.labels).forEach((name) => { + labelNames[name] = true; + }); + Object.keys(group.shared.labels).forEach((name) => { + labelNames[name] = true; + }); + group.alerts.forEach((alert) => { + Object.keys(alert.labels).forEach((name) => { + labelNames[name] = true; + }); + }); + }); + }); + + callback( + Object.keys(labelNames) + .sort() + .map((key) => StringToOption(key)) + ); + }; + + const context = React.useContext(ThemeContext); + + return ( + { + settingsStore.multiGridConfig.config.gridLabel = (option as OptionT).value; + }} + menuIsOpen={true} + components={{ + ClearIndicator: null, + IndicatorSeparator: null, + DropdownIndicator: null, + ValueContainer: NullContainer, + Control: NullContainer, + }} + /> + ); +}; + const Dropdown: FC<{ popperPlacement?: string; popperRef?: Ref; popperStyle?: CSSProperties; + alertStore: AlertStore; settingsStore: Settings; -}> = ({ popperPlacement, popperRef, popperStyle, settingsStore }) => { +}> = ({ + popperPlacement, + popperRef, + popperStyle, + alertStore, + settingsStore, +}) => { return (
-
); }; const GridLabelSelect: FC<{ + alertStore: AlertStore; settingsStore: Settings; -}> = observer(({ settingsStore }) => { +}> = observer(({ alertStore, settingsStore }) => { const [isVisible, setIsVisible] = useState(false); const hide = useCallback(() => setIsVisible(false), []); const toggle = useCallback(() => setIsVisible(!isVisible), [isVisible]); @@ -86,6 +153,7 @@ const GridLabelSelect: FC<{ popperPlacement={placement} popperRef={ref} popperStyle={style} + alertStore={alertStore} settingsStore={settingsStore} /> )} diff --git a/ui/src/Components/Grid/AlertGrid/Swimlane.tsx b/ui/src/Components/Grid/AlertGrid/Swimlane.tsx index 22a0d686b..63d0e0cee 100644 --- a/ui/src/Components/Grid/AlertGrid/Swimlane.tsx +++ b/ui/src/Components/Grid/AlertGrid/Swimlane.tsx @@ -41,7 +41,10 @@ const Swimlane: FC<{ className="flex-shrink-0 flex-grow-1 px-0" style={{ minWidth: "0px" }} > - + )} diff --git a/ui/src/Components/MainModal/Configuration/GridLabelName.tsx b/ui/src/Components/MainModal/Configuration/GridLabelName.tsx index 9939b09dc..13068d55d 100644 --- a/ui/src/Components/MainModal/Configuration/GridLabelName.tsx +++ b/ui/src/Components/MainModal/Configuration/GridLabelName.tsx @@ -1,7 +1,6 @@ import React, { FC } from "react"; import Creatable from "react-select/creatable"; -import { GroupTypeBase, SelectComponentsConfig } from "react-select"; import { useFetchGet } from "Hooks/useFetchGet"; import { FormatBackendURI } from "Stores/AlertStore"; @@ -25,11 +24,7 @@ const staticValues = [ const GridLabelName: FC<{ settingsStore: Settings; - isOpen?: boolean | undefined; - selectComponents?: - | Partial>> - | undefined; -}> = ({ settingsStore, isOpen = undefined, selectComponents = undefined }) => { +}> = ({ settingsStore }) => { const { response } = useFetchGet( FormatBackendURI(`labelNames.json`) ); @@ -56,8 +51,6 @@ const GridLabelName: FC<{ onChange={(option) => { settingsStore.multiGridConfig.config.gridLabel = (option as OptionT).value; }} - menuIsOpen={isOpen} - components={selectComponents} /> ); };