diff --git a/ui/src/Components/Grid/AlertGrid/Grid.tsx b/ui/src/Components/Grid/AlertGrid/Grid.tsx index 3825f4143..8a7dd477e 100644 --- a/ui/src/Components/Grid/AlertGrid/Grid.tsx +++ b/ui/src/Components/Grid/AlertGrid/Grid.tsx @@ -109,8 +109,15 @@ const Grid: FC<{ repack(); }); + const [isMenuOpen, setIsMenuOpen] = useState(false); + return ( - <> +
setIsMenuOpen(true)} + onMenuClose={() => setIsMenuOpen(false)} />
)} - +
); }; diff --git a/ui/src/Components/Grid/AlertGrid/GridLabelSelect.test.tsx b/ui/src/Components/Grid/AlertGrid/GridLabelSelect.test.tsx index ee2f905ed..7b487c650 100644 --- a/ui/src/Components/Grid/AlertGrid/GridLabelSelect.test.tsx +++ b/ui/src/Components/Grid/AlertGrid/GridLabelSelect.test.tsx @@ -5,15 +5,25 @@ import { mount } from "enzyme"; import fetchMock from "fetch-mock"; import { MockGrid } from "__fixtures__/Stories"; +import { MockThemeContextWithoutAnimations } from "__fixtures__/Theme"; +import { mockMatchMedia } from "__fixtures__/matchMedia"; import { AlertStore } from "Stores/AlertStore"; import { Settings } from "Stores/Settings"; +import { SilenceFormStore } from "Stores/SilenceFormStore"; import type { APIGridT } from "Models/APITypes"; +import { ThemeContext } from "Components/Theme"; +import AlertGrid from "."; import { GridLabelSelect } from "./GridLabelSelect"; let alertStore: AlertStore; let settingsStore: Settings; +let silenceFormStore: SilenceFormStore; let grid: APIGridT; +declare let global: any; +declare let document: any; +declare let window: any; + beforeEach(() => { fetchMock.reset(); fetchMock.mock("*", { @@ -22,6 +32,7 @@ beforeEach(() => { alertStore = new AlertStore([]); settingsStore = new Settings(null); + silenceFormStore = new SilenceFormStore(); grid = { labelName: "foo", labelValue: "bar", @@ -35,6 +46,15 @@ beforeEach(() => { }; alertStore.data.setLabelNames(["alertname", "job", "cluster"]); + window.matchMedia = mockMatchMedia({}); + global.ResizeObserver = jest.fn((cb) => { + return { + observe: jest.fn(), + disconnect: jest.fn(), + }; + }); + global.ResizeObserverEntry = jest.fn(); + jest.useFakeTimers(); }); @@ -44,6 +64,8 @@ const MountedGridLabelSelect = () => { alertStore={alertStore} settingsStore={settingsStore} grid={grid} + onMenuOpen={jest.fn()} + onMenuClose={jest.fn()} /> ); }; @@ -126,4 +148,46 @@ describe("", () => { expect(tree.find("div.components-grid-label-select-menu")).toHaveLength(0); await act(() => promise); }); + + it("opening label select sets z-index", async () => { + const promise = Promise.resolve(); + alertStore.data.setGrids([ + { + labelName: "foo", + labelValue: "bar", + alertGroups: [], + totalGroups: 0, + stateCount: { + unprocessed: 1, + suppressed: 2, + active: 3, + }, + }, + ]); + const tree = mount( + , + { + wrappingComponent: ThemeContext.Provider, + wrappingComponentProps: { value: MockThemeContextWithoutAnimations }, + } + ); + + tree.find("span.components-grid-label-select-dropdown").simulate("click"); + expect(tree.find("div.components-grid-label-select-menu")).toHaveLength(1); + expect(tree.find("div").at(1).props().style?.zIndex).toBe(102); + + tree.find("span.components-grid-label-select-dropdown").simulate("click"); + act(() => { + jest.runOnlyPendingTimers(); + }); + tree.update(); + expect(tree.find("div.components-grid-label-select-menu")).toHaveLength(0); + expect(tree.find("div").at(1).props().style?.zIndex).toBeUndefined(); + + await act(() => promise); + }); }); diff --git a/ui/src/Components/Grid/AlertGrid/GridLabelSelect.tsx b/ui/src/Components/Grid/AlertGrid/GridLabelSelect.tsx index f93b0eb87..8d4f74ac0 100644 --- a/ui/src/Components/Grid/AlertGrid/GridLabelSelect.tsx +++ b/ui/src/Components/Grid/AlertGrid/GridLabelSelect.tsx @@ -131,47 +131,57 @@ const GridLabelSelect: FC<{ alertStore: AlertStore; settingsStore: Settings; grid: APIGridT; -}> = observer(({ alertStore, settingsStore, grid }) => { - const [isVisible, setIsVisible] = useState(false); - const hide = useCallback(() => setIsVisible(false), []); - const toggle = useCallback(() => setIsVisible(!isVisible), [isVisible]); + onMenuOpen: () => void; + onMenuClose: () => void; +}> = observer( + ({ alertStore, settingsStore, grid, onMenuOpen, onMenuClose }) => { + const [isVisible, setIsVisible] = useState(false); + const hide = useCallback(() => setIsVisible(false), []); + const toggle = useCallback(() => { + if (isVisible) { + onMenuClose(); + } else { + onMenuOpen(); + } + setIsVisible(!isVisible); + }, [isVisible, onMenuOpen, onMenuClose]); + const ref = useRef(null); + useOnClickOutside(ref, hide, isVisible); - const ref = useRef(null); - useOnClickOutside(ref, hide, isVisible); - - return ( -
- - - {({ ref }) => ( - - - - )} - - - - {({ placement, ref, style }) => ( - + return ( +
+ + + {({ ref }) => ( + + + )} - - - -
- ); -}); + + + + {({ placement, ref, style }) => ( + + )} + + +
+
+ ); + } +); export { GridLabelSelect }; diff --git a/ui/src/Components/Grid/AlertGrid/Swimlane.tsx b/ui/src/Components/Grid/AlertGrid/Swimlane.tsx index a53681cf1..48eb58667 100644 --- a/ui/src/Components/Grid/AlertGrid/Swimlane.tsx +++ b/ui/src/Components/Grid/AlertGrid/Swimlane.tsx @@ -19,6 +19,8 @@ const Swimlane: FC<{ isExpanded: boolean; onToggle: (event: MouseEvent) => void; paddingTop: number; + onMenuOpen: () => void; + onMenuClose: () => void; }> = ({ alertStore, settingsStore, @@ -26,6 +28,8 @@ const Swimlane: FC<{ isExpanded, onToggle, paddingTop, + onMenuOpen, + onMenuClose, }) => { return (
)} diff --git a/ui/src/Components/Grid/AlertGrid/index.tsx b/ui/src/Components/Grid/AlertGrid/index.tsx index 1cbe90332..da5e7967a 100644 --- a/ui/src/Components/Grid/AlertGrid/index.tsx +++ b/ui/src/Components/Grid/AlertGrid/index.tsx @@ -77,18 +77,17 @@ const AlertGrid: FC<{ }} /> {alertStore.data.grids.map((grid) => ( -
- -
+ ))} );