fix(ui): fix grid label menu z-index

This commit is contained in:
Łukasz Mierzwa
2021-07-22 12:04:17 +01:00
committed by Łukasz Mierzwa
parent ae261c5f3a
commit 98edd7b073
5 changed files with 142 additions and 54 deletions

View File

@@ -109,8 +109,15 @@ const Grid: FC<{
repack();
});
const [isMenuOpen, setIsMenuOpen] = useState<boolean>(false);
return (
<>
<div
style={{
position: isMenuOpen ? "relative" : undefined,
zIndex: isMenuOpen ? 102 : undefined,
}}
>
<CSSTransition
key={grid.labelValue}
in={grid.labelName !== ""}
@@ -126,6 +133,8 @@ const Grid: FC<{
isExpanded={isExpanded}
onToggle={onCollapseClick}
paddingTop={paddingTop}
onMenuOpen={() => setIsMenuOpen(true)}
onMenuClose={() => setIsMenuOpen(false)}
/>
</CSSTransition>
<div
@@ -198,7 +207,7 @@ const Grid: FC<{
</CSSTransition>
)}
</TransitionGroup>
</>
</div>
);
};

View File

@@ -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("<GridLabelSelect />", () => {
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(
<AlertGrid
alertStore={alertStore}
settingsStore={settingsStore}
silenceFormStore={silenceFormStore}
/>,
{
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);
});
});

View File

@@ -131,47 +131,57 @@ const GridLabelSelect: FC<{
alertStore: AlertStore;
settingsStore: Settings;
grid: APIGridT;
}> = observer(({ alertStore, settingsStore, grid }) => {
const [isVisible, setIsVisible] = useState<boolean>(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<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);
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>
)}
</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}
/>
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>
)}
</Popper>
</DropdownSlide>
</Manager>
</div>
);
});
</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>
);
}
);
export { GridLabelSelect };

View File

@@ -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 (
<h5
@@ -59,6 +63,8 @@ const Swimlane: FC<{
alertStore={alertStore}
settingsStore={settingsStore}
grid={grid}
onMenuOpen={onMenuOpen}
onMenuClose={onMenuClose}
/>
</span>
)}

View File

@@ -77,18 +77,17 @@ const AlertGrid: FC<{
}}
/>
{alertStore.data.grids.map((grid) => (
<div key={`${grid.labelName}/${grid.labelValue}`}>
<Grid
alertStore={alertStore}
silenceFormStore={silenceFormStore}
settingsStore={settingsStore}
gridSizesConfig={gridSizesConfig}
groupWidth={groupWidth}
grid={grid}
outerPadding={alertStore.data.gridPadding}
paddingTop={paddingTop}
/>
</div>
<Grid
key={`${grid.labelName}/${grid.labelValue}`}
alertStore={alertStore}
silenceFormStore={silenceFormStore}
settingsStore={settingsStore}
gridSizesConfig={gridSizesConfig}
groupWidth={groupWidth}
grid={grid}
outerPadding={alertStore.data.gridPadding}
paddingTop={paddingTop}
/>
))}
</>
);