fix(ui): make sure all dropdowns are rendered above grid elements

This commit is contained in:
Łukasz Mierzwa
2021-07-22 17:44:43 +01:00
committed by Łukasz Mierzwa
parent cd52400b03
commit d22c9271c8
13 changed files with 257 additions and 104 deletions

View File

@@ -7,6 +7,7 @@ import type {
APIAlertGroupT,
APIAlertT,
APIAlertsResponseUpstreamsT,
APIGridT,
} from "Models/APITypes";
import { AlertStore } from "Stores/AlertStore";
import { SilenceFormStore } from "Stores/SilenceFormStore";
@@ -16,6 +17,7 @@ let alertStore: AlertStore;
let silenceFormStore: SilenceFormStore;
let alert: APIAlertT;
let group: APIAlertGroupT;
let grid: APIGridT;
let MockAfterClick: () => void;
let MockSetIsMenuOpen: () => void;
@@ -74,6 +76,17 @@ beforeEach(() => {
alert = MockAlert([], { foo: "bar" }, "active");
group = MockAlertGroup({ alertname: "Fake Alert" }, [alert], [], {}, {});
grid = {
labelName: "foo",
labelValue: "bar",
alertGroups: [],
totalGroups: 0,
stateCount: {
active: 0,
suppressed: 0,
unprocessed: 0,
},
};
alertStore.data.setUpstreams(generateUpstreams());
});
@@ -81,6 +94,7 @@ beforeEach(() => {
const MountedAlertMenu = (group: APIAlertGroupT) => {
return mount(
<AlertMenu
grid={grid}
group={group}
alert={alert}
alertStore={alertStore}

View File

@@ -11,6 +11,7 @@ import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons/faExternalL
import { faWrench } from "@fortawesome/free-solid-svg-icons/faWrench";
import type {
APIGridT,
APIAlertT,
APIAlertGroupT,
APIAnnotationT,
@@ -142,19 +143,25 @@ const MenuContent: FC<{
};
const AlertMenu: FC<{
grid: APIGridT;
group: APIAlertGroupT;
alert: APIAlertT;
alertStore: AlertStore;
silenceFormStore: SilenceFormStore;
setIsMenuOpen: (isOpen: boolean) => void;
}> = observer(
({ group, alert, alertStore, silenceFormStore, setIsMenuOpen }) => {
({ grid, group, alert, alertStore, silenceFormStore, setIsMenuOpen }) => {
const [isHidden, setIsHidden] = useState<boolean>(true);
const toggle = useCallback(() => {
window.dispatchEvent(
new CustomEvent("gridMenuOpen", {
detail: { isOpen: isHidden, labelValue: grid.labelValue },
})
);
setIsMenuOpen(isHidden);
setIsHidden(!isHidden);
}, [isHidden, setIsMenuOpen]);
}, [grid.labelValue, isHidden, setIsMenuOpen]);
const hide = useCallback(() => {
setIsHidden(true);

View File

@@ -13,7 +13,7 @@ import {
MockSilence,
} from "__fixtures__/Alerts";
import { MockThemeContext } from "__fixtures__/Theme";
import type { APIAlertGroupT, APIAlertT } from "Models/APITypes";
import type { APIAlertGroupT, APIAlertT, APIGridT } from "Models/APITypes";
import { AlertStore } from "Stores/AlertStore";
import { SilenceFormStore } from "Stores/SilenceFormStore";
import { BorderClassMap } from "Common/Colors";
@@ -22,11 +22,24 @@ import Alert from ".";
let alertStore: AlertStore;
let silenceFormStore: SilenceFormStore;
let grid: APIGridT;
beforeEach(() => {
advanceTo(new Date(Date.UTC(2018, 7, 15, 20, 40, 0)));
alertStore = new AlertStore([]);
silenceFormStore = new SilenceFormStore();
grid = {
labelName: "foo",
labelValue: "bar",
alertGroups: [],
totalGroups: 0,
stateCount: {
active: 0,
suppressed: 0,
unprocessed: 0,
},
};
});
afterEach(() => {
@@ -58,6 +71,7 @@ const MountedAlert = (
) => {
return mount(
<Alert
grid={grid}
alert={alert}
group={group}
showReceiver={showReceiver}

View File

@@ -3,6 +3,7 @@ import type { FC } from "react";
import { observer } from "mobx-react-lite";
import type {
APIGridT,
APIAlertT,
APIAlertGroupT,
APIAlertmanagerStateT,
@@ -18,6 +19,7 @@ import { AlertMenu } from "./AlertMenu";
import { RenderSilence } from "../Silences";
const Alert: FC<{
grid: APIGridT;
group: APIAlertGroupT;
alert: APIAlertT;
showReceiver: boolean;
@@ -27,6 +29,7 @@ const Alert: FC<{
silenceFormStore: SilenceFormStore;
setIsMenuOpen: (isOpen: boolean) => void;
}> = ({
grid,
group,
alert,
showReceiver,
@@ -99,6 +102,7 @@ const Alert: FC<{
))}
</div>
<AlertMenu
grid={grid}
group={group}
alert={alert}
alertStore={alertStore}

View File

@@ -8,6 +8,7 @@ import { MockAlertGroup } from "__fixtures__/Alerts";
import type {
APIAlertGroupT,
APIAlertsResponseUpstreamsT,
APIGridT,
} from "Models/APITypes";
import { AlertStore } from "Stores/AlertStore";
import { SilenceFormStore } from "Stores/SilenceFormStore";
@@ -15,6 +16,7 @@ import { GroupMenu, MenuContent } from "./GroupMenu";
let alertStore: AlertStore;
let silenceFormStore: SilenceFormStore;
let grid: APIGridT;
let MockAfterClick: () => void;
let MockSetIsMenuOpen: () => void;
@@ -72,11 +74,24 @@ beforeEach(() => {
MockSetIsMenuOpen = jest.fn();
alertStore.data.setUpstreams(generateUpstreams());
grid = {
labelName: "foo",
labelValue: "bar",
alertGroups: [],
totalGroups: 0,
stateCount: {
active: 0,
suppressed: 0,
unprocessed: 0,
},
};
});
const MountedGroupMenu = (group: APIAlertGroupT, themed: boolean) => {
return mount(
<GroupMenu
grid={grid}
group={group}
alertStore={alertStore}
silenceFormStore={silenceFormStore}

View File

@@ -10,7 +10,7 @@ import { faShareSquare } from "@fortawesome/free-solid-svg-icons/faShareSquare";
import { faBellSlash } from "@fortawesome/free-solid-svg-icons/faBellSlash";
import { faWrench } from "@fortawesome/free-solid-svg-icons/faWrench";
import type { APIAlertGroupT } from "Models/APITypes";
import type { APIAlertGroupT, APIGridT } from "Models/APITypes";
import { FormatAlertsQ } from "Stores/AlertStore";
import type { AlertStore } from "Stores/AlertStore";
import {
@@ -143,18 +143,24 @@ const MenuContent: FC<{
};
const GroupMenu: FC<{
grid: APIGridT;
group: APIAlertGroupT;
alertStore: AlertStore;
silenceFormStore: SilenceFormStore;
themed: boolean;
setIsMenuOpen: (isOpen: boolean) => void;
}> = ({ group, alertStore, silenceFormStore, themed, setIsMenuOpen }) => {
}> = ({ grid, group, alertStore, silenceFormStore, themed, setIsMenuOpen }) => {
const [isHidden, setIsHidden] = useState<boolean>(true);
const toggle = useCallback(() => {
window.dispatchEvent(
new CustomEvent("gridMenuOpen", {
detail: { isOpen: isHidden, labelValue: grid.labelValue },
})
);
setIsMenuOpen(isHidden);
setIsHidden(!isHidden);
}, [isHidden, setIsMenuOpen]);
}, [setIsMenuOpen, isHidden, grid.labelValue]);
const hide = useCallback(() => {
setIsHidden(true);

View File

@@ -2,7 +2,7 @@ import type { FC, MouseEvent } from "react";
import { observer } from "mobx-react-lite";
import type { APIAlertGroupT } from "Models/APITypes";
import type { APIAlertGroupT, APIGridT } from "Models/APITypes";
import type { AlertStore } from "Stores/AlertStore";
import type { SilenceFormStore } from "Stores/SilenceFormStore";
import FilteringLabel from "Components/Labels/FilteringLabel";
@@ -15,6 +15,7 @@ import { GroupMenu } from "./GroupMenu";
const GroupHeader: FC<{
isCollapsed: boolean;
setIsCollapsed: (isCollapsed: boolean) => void;
grid: APIGridT;
group: APIAlertGroupT;
alertStore: AlertStore;
silenceFormStore: SilenceFormStore;
@@ -24,6 +25,7 @@ const GroupHeader: FC<{
}> = ({
isCollapsed,
setIsCollapsed,
grid,
group,
alertStore,
silenceFormStore,
@@ -55,6 +57,7 @@ const GroupHeader: FC<{
>
<span className="flex-shrink-0 flex-grow-0">
<GroupMenu
grid={grid}
group={group}
alertStore={alertStore}
silenceFormStore={silenceFormStore}

View File

@@ -10,7 +10,7 @@ import {
MockThemeContextWithoutAnimations,
} from "__fixtures__/Theme";
import { RainbowHistoryResponse } from "__fixtures__/AlertHistory";
import type { APIAlertGroupT } from "Models/APITypes";
import type { APIAlertGroupT, APIGridT } from "Models/APITypes";
import { AlertStore } from "Stores/AlertStore";
import { Settings, CollapseStateT } from "Stores/Settings";
import { SilenceFormStore } from "Stores/SilenceFormStore";
@@ -21,6 +21,7 @@ let alertStore: AlertStore;
let settingsStore: Settings;
let silenceFormStore: SilenceFormStore;
let group: APIAlertGroupT;
let grid: APIGridT;
let originalInnerWidth: number;
const MockGroup = (groupName: string) => {
@@ -50,6 +51,18 @@ beforeEach(() => {
...alertStore.settings.values,
...{ historyEnabled: false },
});
grid = {
labelName: "foo",
labelValue: "bar",
alertGroups: [],
totalGroups: 0,
stateCount: {
active: 0,
suppressed: 0,
unprocessed: 0,
},
};
});
afterEach(() => {
@@ -67,16 +80,12 @@ const MockAlerts = (alertCount: number, totalAlerts: number) => {
group.totalAlerts = totalAlerts;
};
const MountedAlertGroup = (
afterUpdate: () => void,
showAlertmanagers: boolean,
theme?: ThemeCtx
) => {
const MountedAlertGroup = (afterUpdate: () => void, theme?: ThemeCtx) => {
return mount(
<AlertGroup
afterUpdate={afterUpdate}
grid={grid}
group={group}
showAlertmanagers={showAlertmanagers}
settingsStore={settingsStore}
alertStore={alertStore}
silenceFormStore={silenceFormStore}
@@ -100,20 +109,20 @@ const ValidateCollapse = (
settingsStore.alertGroupConfig.setDefaultCollapseState(defaultCollapseState);
MockAlerts(3, 3);
const tree = MountedAlertGroup(jest.fn(), false);
const tree = MountedAlertGroup(jest.fn());
expect(tree.find("Alert")).toHaveLength(shouldBeCollapsed ? 0 : 3);
};
describe("<AlertGroup />", () => {
it("doesn't crash on unmount", () => {
MockAlerts(5, 5);
const tree = MountedAlertGroup(jest.fn(), true);
const tree = MountedAlertGroup(jest.fn());
tree.unmount();
});
it("uses 'animate' class when settingsStore.themeConfig.config.animations is true", () => {
MockAlerts(5, 5);
const tree = MountedAlertGroup(jest.fn(), true, MockThemeContext);
const tree = MountedAlertGroup(jest.fn(), MockThemeContext);
expect(
tree.find("div.components-grid-alertgrid-alertgroup").hasClass("animate")
).toBe(true);
@@ -123,7 +132,6 @@ describe("<AlertGroup />", () => {
MockAlerts(5, 5);
const tree = MountedAlertGroup(
jest.fn(),
true,
MockThemeContextWithoutAnimations
);
expect(
@@ -134,7 +142,7 @@ describe("<AlertGroup />", () => {
it("renders Alertmanager cluster labels in footer if shared", () => {
MockAlerts(2, 2);
group.shared.clusters = ["default"];
const tree = MountedAlertGroup(jest.fn(), true).find("AlertGroup");
const tree = MountedAlertGroup(jest.fn()).find("AlertGroup");
expect(tree.find("GroupFooter").html()).toMatch(/@cluster/);
});
@@ -163,7 +171,7 @@ describe("<AlertGroup />", () => {
});
}
group.shared.clusters = ["default", "HA"];
const tree = MountedAlertGroup(jest.fn(), true).find("AlertGroup");
const tree = MountedAlertGroup(jest.fn()).find("AlertGroup");
const labels = tree.find("GroupFooter").find("FilteringLabel");
expect(labels).toHaveLength(3);
expect(labels.at(0).text()).toBe("@cluster: default");
@@ -176,7 +184,7 @@ describe("<AlertGroup />", () => {
for (let i = 0; i < group.alerts.length; i++) {
group.alerts[i].alertmanager = [];
}
const tree = MountedAlertGroup(jest.fn(), true).find("AlertGroup");
const tree = MountedAlertGroup(jest.fn()).find("AlertGroup");
const labels = tree.find("GroupFooter").find("FilteringLabel");
expect(labels).toHaveLength(1);
expect(labels.at(0).text()).toBe("@receiver: by-name");
@@ -194,7 +202,7 @@ describe("<AlertGroup />", () => {
fakeAlertmanager3: 1,
fakeAlertmanager4: 1,
};
const tree = MountedAlertGroup(jest.fn(), true);
const tree = MountedAlertGroup(jest.fn());
const alerts = tree.find("ul.list-group");
expect(alerts.html()).toMatch(/@cluster/);
@@ -205,7 +213,7 @@ describe("<AlertGroup />", () => {
it("only renders titlebar when collapsed", () => {
MockAlerts(5, 10);
const tree = MountedAlertGroup(jest.fn(), false);
const tree = MountedAlertGroup(jest.fn());
tree.find("span.badge.cursor-pointer").at(1).simulate("click");
expect(tree.find("Alert")).toHaveLength(0);
expect(tree.find("ul.list-group")).toHaveLength(0);
@@ -214,7 +222,7 @@ describe("<AlertGroup />", () => {
it("renders reduced details when idle", () => {
MockAlerts(5, 10);
alertStore.ui.setIsIdle(true);
const tree = MountedAlertGroup(jest.fn(), true, MockThemeContext);
const tree = MountedAlertGroup(jest.fn(), MockThemeContext);
expect(tree.find("Alert")).toHaveLength(1);
});
@@ -251,14 +259,14 @@ describe("<AlertGroup />", () => {
it("renders @receiver label when alertStore.data.receivers.length > 1", () => {
alertStore.data.setReceivers(["foo", "bar"]);
MockAlerts(5, 10);
const tree = MountedAlertGroup(jest.fn(), false);
const tree = MountedAlertGroup(jest.fn());
expect(tree.html()).toMatch(/@receiver:/);
});
it("doesn't render @receiver label when alertStore.data.receivers.length == 0", () => {
alertStore.data.setReceivers([]);
MockAlerts(5, 10);
const tree = MountedAlertGroup(jest.fn(), false);
const tree = MountedAlertGroup(jest.fn());
expect(tree.html()).not.toMatch(/@receiver:/);
});
});
@@ -269,7 +277,7 @@ const ValidateLoadButtonPresent = (
isPresent: boolean
) => {
MockAlerts(alertCount, totalAlerts);
const tree = MountedAlertGroup(jest.fn(), false);
const tree = MountedAlertGroup(jest.fn());
const buttons = tree.find("button");
expect(buttons).toHaveLength(isPresent ? 2 : 0);
};
@@ -282,7 +290,7 @@ const ValidateLoadButtonAction = (
loadedAlerts: number
) => {
MockAlerts(alertCount, totalAlerts);
const tree = MountedAlertGroup(jest.fn(), false);
const tree = MountedAlertGroup(jest.fn());
const loadMore = tree.find("button").at(buttonIndex);
expect(loadMore.html()).toMatch(iconMatch);
loadMore.simulate("click");
@@ -348,7 +356,7 @@ describe("<AlertGroup /> renderConfig", () => {
const promise = Promise.resolve();
MockAlerts(5, 5);
const tree = MountedAlertGroup(jest.fn(), false);
const tree = MountedAlertGroup(jest.fn());
tree
.find("Alert")
@@ -366,7 +374,7 @@ describe("<AlertGroup /> renderConfig", () => {
it("uses 'z-index: 100' style after setIsMenuOpen() is called on AlertGroup header menu", async () => {
const promise = Promise.resolve();
MockAlerts(5, 5);
const tree = MountedAlertGroup(jest.fn(), false);
const tree = MountedAlertGroup(jest.fn());
tree.find("span.cursor-pointer").at(0).simulate("click");
await act(() => promise);
@@ -381,7 +389,7 @@ describe("<AlertGroup /> card theme", () => {
it("renders bg-light background when colorTitleBar=false", () => {
settingsStore.alertGroupConfig.setColorTitleBar(false);
group.stateCount = { active: 5, suppressed: 0, unprocessed: 0 };
const tree = MountedAlertGroup(jest.fn(), false);
const tree = MountedAlertGroup(jest.fn());
expect(tree.find(".card").hasClass("bg-light")).toBe(true);
expect(tree.find(".card").hasClass("bg-danger")).toBe(false);
expect(tree.find(".card").hasClass("bg-success")).toBe(false);
@@ -391,14 +399,14 @@ describe("<AlertGroup /> card theme", () => {
it("renders themed titlebar when colorTitleBar=false", () => {
settingsStore.alertGroupConfig.setColorTitleBar(false);
group.stateCount = { active: 5, suppressed: 0, unprocessed: 0 };
const tree = MountedAlertGroup(jest.fn(), false);
const tree = MountedAlertGroup(jest.fn());
expect(tree.find("GroupHeader").prop("themedCounters")).toBe(true);
});
it("renders bg-light border when colorTitleBar=true and there are multiple alert states", () => {
settingsStore.alertGroupConfig.setColorTitleBar(false);
group.stateCount = { active: 5, suppressed: 0, unprocessed: 0 };
const tree = MountedAlertGroup(jest.fn(), false);
const tree = MountedAlertGroup(jest.fn());
expect(tree.find(".card").hasClass("bg-light")).toBe(true);
expect(tree.find(".card").hasClass("bg-danger")).toBe(false);
expect(tree.find(".card").hasClass("bg-success")).toBe(false);
@@ -408,14 +416,14 @@ describe("<AlertGroup /> card theme", () => {
it("renders themed titlebar when colorTitleBar=true and there are multiple alert states", () => {
settingsStore.alertGroupConfig.setColorTitleBar(true);
group.stateCount = { active: 5, suppressed: 6, unprocessed: 7 };
const tree = MountedAlertGroup(jest.fn(), false);
const tree = MountedAlertGroup(jest.fn());
expect(tree.find("GroupHeader").prop("themedCounters")).toBe(true);
});
it("renders state based background when colorTitleBar=true and there's only one alert state", () => {
settingsStore.alertGroupConfig.setColorTitleBar(true);
group.stateCount = { active: 0, suppressed: 5, unprocessed: 0 };
const tree = MountedAlertGroup(jest.fn(), false);
const tree = MountedAlertGroup(jest.fn());
expect(tree.find(".card").hasClass("bg-light")).toBe(false);
expect(tree.find(".card").hasClass("bg-danger")).toBe(false);
expect(tree.find(".card").hasClass("bg-success")).toBe(true);
@@ -425,7 +433,7 @@ describe("<AlertGroup /> card theme", () => {
it("renders unthemed titlebar when colorTitleBar=true and there's only one alert state", () => {
settingsStore.alertGroupConfig.setColorTitleBar(true);
group.stateCount = { active: 5, suppressed: 0, unprocessed: 0 };
const tree = MountedAlertGroup(jest.fn(), false);
const tree = MountedAlertGroup(jest.fn());
expect(tree.find("GroupHeader").prop("themedCounters")).toBe(false);
});
@@ -447,7 +455,7 @@ describe("<AlertGroup /> card theme", () => {
...{ historyEnabled: true },
});
group.stateCount = { active: 5, suppressed: 0, unprocessed: 0 };
const tree = MountedAlertGroup(jest.fn(), false);
const tree = MountedAlertGroup(jest.fn());
await act(async () => {
await fetchMock.flush(true);
});
@@ -462,7 +470,7 @@ describe("<AlertGroup /> card theme", () => {
...{ historyEnabled: false },
});
group.stateCount = { active: 5, suppressed: 0, unprocessed: 0 };
const tree = MountedAlertGroup(jest.fn(), false);
const tree = MountedAlertGroup(jest.fn());
expect(tree.find("AlertHistory")).toHaveLength(0);
});
});

View File

@@ -8,7 +8,7 @@ import { faPlus } from "@fortawesome/free-solid-svg-icons/faPlus";
import { faMinus } from "@fortawesome/free-solid-svg-icons/faMinus";
import { faEllipsisH } from "@fortawesome/free-solid-svg-icons/faEllipsisH";
import type { APIAlertGroupT, AlertStateT } from "Models/APITypes";
import type { APIGridT, APIAlertGroupT, AlertStateT } from "Models/APITypes";
import type { Settings } from "Stores/Settings";
import type { AlertStore } from "Stores/AlertStore";
import type { SilenceFormStore } from "Stores/SilenceFormStore";
@@ -40,8 +40,8 @@ const LoadButton: FC<{
};
const AlertGroup: FC<{
grid: APIGridT;
group: APIAlertGroupT;
showAlertmanagers: boolean;
afterUpdate: () => void;
alertStore: AlertStore;
settingsStore: Settings;
@@ -49,8 +49,8 @@ const AlertGroup: FC<{
groupWidth: number;
gridLabelValue: string;
}> = ({
grid,
group,
showAlertmanagers,
afterUpdate,
silenceFormStore,
alertStore,
@@ -159,6 +159,7 @@ const AlertGroup: FC<{
<GroupHeader
isCollapsed={isCollapsed}
setIsCollapsed={setIsCollapsed}
grid={grid}
group={group}
alertStore={alertStore}
silenceFormStore={silenceFormStore}
@@ -177,6 +178,7 @@ const AlertGroup: FC<{
.map((alert) => (
<Alert
key={alert.id}
grid={grid}
group={group}
alert={alert}
showReceiver={

View File

@@ -110,6 +110,20 @@ const Grid: FC<{
});
const [isMenuOpen, setIsMenuOpen] = useState<boolean>(false);
const onMenuOpen = useCallback(
(event) => {
if (event.detail.labelValue === grid.labelValue) {
setIsMenuOpen(event.detail.isOpen);
}
},
[grid.labelValue]
);
useEffect(() => {
window.addEventListener("gridMenuOpen", onMenuOpen);
return () => {
window.removeEventListener("gridMenuOpen", onMenuOpen);
};
}, [onMenuOpen]);
return (
<div
@@ -133,8 +147,6 @@ const Grid: FC<{
isExpanded={isExpanded}
onToggle={onCollapseClick}
paddingTop={paddingTop}
onMenuOpen={() => setIsMenuOpen(true)}
onMenuClose={() => setIsMenuOpen(false)}
/>
</CSSTransition>
<div
@@ -162,10 +174,8 @@ const Grid: FC<{
unmountOnExit
>
<AlertGroup
grid={grid}
group={group}
showAlertmanagers={
Object.keys(alertStore.data.upstreams.clusters).length > 1
}
afterUpdate={debouncedRepack}
alertStore={alertStore}
settingsStore={settingsStore}

View File

@@ -64,8 +64,6 @@ const MountedGridLabelSelect = () => {
alertStore={alertStore}
settingsStore={settingsStore}
grid={grid}
onMenuOpen={jest.fn()}
onMenuClose={jest.fn()}
/>
);
};
@@ -190,4 +188,78 @@ describe("<GridLabelSelect />", () => {
await act(() => promise);
});
it("sending event from current grid sets z-index", () => {
alertStore.data.setGrids([
{
labelName: "foo",
labelValue: "baz",
alertGroups: [],
totalGroups: 0,
stateCount: {
unprocessed: 1,
suppressed: 2,
active: 3,
},
},
]);
const tree = mount(
<AlertGrid
alertStore={alertStore}
settingsStore={settingsStore}
silenceFormStore={silenceFormStore}
/>,
{
wrappingComponent: ThemeContext.Provider,
wrappingComponentProps: { value: MockThemeContextWithoutAnimations },
}
);
act(() => {
window.dispatchEvent(
new CustomEvent("gridMenuOpen", {
detail: { isOpen: true, labelValue: "baz" },
})
);
});
tree.update();
expect(tree.find("div").at(1).props().style?.zIndex).toBe(102);
});
it("sending event from a different grid is ignored", () => {
alertStore.data.setGrids([
{
labelName: "foo",
labelValue: "baz",
alertGroups: [],
totalGroups: 0,
stateCount: {
unprocessed: 1,
suppressed: 2,
active: 3,
},
},
]);
const tree = mount(
<AlertGrid
alertStore={alertStore}
settingsStore={settingsStore}
silenceFormStore={silenceFormStore}
/>,
{
wrappingComponent: ThemeContext.Provider,
wrappingComponentProps: { value: MockThemeContextWithoutAnimations },
}
);
act(() => {
window.dispatchEvent(
new CustomEvent("gridMenuOpen", {
detail: { isOpen: true, labelValue: "fake" },
})
);
});
tree.update();
expect(tree.find("div").at(1).props().style?.zIndex).toBeUndefined();
});
});

View File

@@ -131,57 +131,61 @@ const GridLabelSelect: FC<{
alertStore: AlertStore;
settingsStore: Settings;
grid: APIGridT;
onMenuOpen: () => void;
onMenuClose: () => void;
}> = observer(
({ alertStore, settingsStore, grid, onMenuOpen, onMenuClose }) => {
const [isVisible, setIsVisible] = useState<boolean>(false);
const hide = useCallback(() => setIsVisible(false), []);
const toggle = useCallback(() => {
if (isVisible) {
onMenuClose();
} else {
onMenuOpen();
}
setIsVisible(!isVisible);
}, [isVisible, onMenuOpen, onMenuClose]);
const ref = useRef<HTMLDivElement | null>(null);
useOnClickOutside(ref, hide, isVisible);
}> = observer(({ alertStore, settingsStore, grid }) => {
const [isVisible, setIsVisible] = useState<boolean>(false);
const hide = useCallback(() => setIsVisible(false), []);
const toggle = useCallback(() => {
if (isVisible) {
window.dispatchEvent(
new CustomEvent("gridMenuOpen", {
detail: { isOpen: false, labelValue: grid.labelValue },
})
);
} else {
window.dispatchEvent(
new CustomEvent("gridMenuOpen", {
detail: { isOpen: true, labelValue: grid.labelValue },
})
);
}
setIsVisible(!isVisible);
}, [isVisible, grid.labelValue]);
const ref = useRef<HTMLDivElement | null>(null);
useOnClickOutside(ref, hide, isVisible);
return (
<div ref={ref} className="components-label badge ps-1 pe-2">
<Manager>
<Reference>
{({ ref }) => (
<span
ref={ref}
onClick={toggle}
className="border-0 rounded-0 bg-inherit cursor-pointer px-1 py-0 components-grid-label-select-dropdown"
data-toggle="dropdown"
>
<FontAwesomeIcon className="text-muted" icon={faCaretDown} />
</span>
return (
<div ref={ref} className="components-label badge ps-1 pe-2">
<Manager>
<Reference>
{({ ref }) => (
<span
ref={ref}
onClick={toggle}
className="border-0 rounded-0 bg-inherit cursor-pointer px-1 py-0 components-grid-label-select-dropdown"
data-toggle="dropdown"
>
<FontAwesomeIcon className="text-muted" icon={faCaretDown} />
</span>
)}
</Reference>
<DropdownSlide in={isVisible} unmountOnExit>
<Popper modifiers={CommonPopperModifiers}>
{({ placement, ref, style }) => (
<Dropdown
popperPlacement={placement}
popperRef={ref}
popperStyle={style}
alertStore={alertStore}
settingsStore={settingsStore}
grid={grid}
onClose={toggle}
/>
)}
</Reference>
<DropdownSlide in={isVisible} unmountOnExit>
<Popper modifiers={CommonPopperModifiers}>
{({ placement, ref, style }) => (
<Dropdown
popperPlacement={placement}
popperRef={ref}
popperStyle={style}
alertStore={alertStore}
settingsStore={settingsStore}
grid={grid}
onClose={toggle}
/>
)}
</Popper>
</DropdownSlide>
</Manager>
</div>
);
}
);
</Popper>
</DropdownSlide>
</Manager>
</div>
);
});
export { GridLabelSelect };

View File

@@ -19,8 +19,6 @@ const Swimlane: FC<{
isExpanded: boolean;
onToggle: (event: MouseEvent) => void;
paddingTop: number;
onMenuOpen: () => void;
onMenuClose: () => void;
}> = ({
alertStore,
settingsStore,
@@ -28,8 +26,6 @@ const Swimlane: FC<{
isExpanded,
onToggle,
paddingTop,
onMenuOpen,
onMenuClose,
}) => {
return (
<h5
@@ -63,8 +59,6 @@ const Swimlane: FC<{
alertStore={alertStore}
settingsStore={settingsStore}
grid={grid}
onMenuOpen={onMenuOpen}
onMenuClose={onMenuClose}
/>
</span>
)}