diff --git a/src/ui/widgets/DisplayReactGridLayout/displayGridLayout.test.tsx b/src/ui/widgets/DisplayReactGridLayout/displayGridLayout.test.tsx index 6328ced7..f718db9a 100644 --- a/src/ui/widgets/DisplayReactGridLayout/displayGridLayout.test.tsx +++ b/src/ui/widgets/DisplayReactGridLayout/displayGridLayout.test.tsx @@ -1,5 +1,5 @@ import { describe, it, expect, vi } from "vitest"; -import { render, screen } from "@testing-library/react"; +import { render, screen, fireEvent } from "@testing-library/react"; import React from "react"; import { DisplayGridLayoutComponent } from "./displayGridLayout"; @@ -13,7 +13,16 @@ vi.mock("react-grid-layout", async () => { const useGridLayout = vi.fn(({ layout, cols }: any) => ({ layout, - cols + cols, + isInteracting: true, + dragState: { + activeDrag: { i: "a", w: 0, h: 0 }, + oldDragItem: null, + oldLayout: null + }, + onDragStart: vi.fn(), + onDragStop: vi.fn(), + onResizeStop: vi.fn() })); return { @@ -95,15 +104,23 @@ vi.mock("../register", () => ({ registerWidget: vi.fn() })); -const TestChild = ({ id }: { id: string }) => ( -
child {id}
+const TestChild = ({ + id, + onClick +}: { + id: string; + onClick?: (e: React.MouseEvent) => void; +}) => ( +
+ child {id} +
); -const renderGrid = (props: any = {}) => +const renderGrid = (props: any = {}, clickHandler?: any) => render( - + @@ -305,4 +322,22 @@ describe("DisplayGridLayoutComponent", () => { expect(config?.margin).toEqual([10, 12]); expect(config?.rowHeight).toBe(25); }); + + it("does not click through to child when dragging", async () => { + const rgl = (await import("react-grid-layout")) as any; + const childClickHandler = vi.fn(); + renderGrid({ gridLayout: [{ i: "a", w: 8, h: 4 }] }, childClickHandler); + + const wrapper = screen.getByTestId("child-a").parentElement; + expect(wrapper).toBeInTheDocument(); + + const gridProps = rgl.__getLastGridProps(); + gridProps.onDragStart([], { i: "a" }, { i: "a" }, null, null, wrapper); + + const overlay = screen.getByTestId("drag-overlay"); + expect(overlay).toBeInTheDocument(); + + fireEvent.click(overlay); + expect(childClickHandler).not.toHaveBeenCalled(); + }); }); diff --git a/src/ui/widgets/DisplayReactGridLayout/displayGridLayout.tsx b/src/ui/widgets/DisplayReactGridLayout/displayGridLayout.tsx index 99d08624..b71c3622 100644 --- a/src/ui/widgets/DisplayReactGridLayout/displayGridLayout.tsx +++ b/src/ui/widgets/DisplayReactGridLayout/displayGridLayout.tsx @@ -205,7 +205,14 @@ export const DisplayGridLayoutComponent = ( gridCellResizeEnabled ]); - const { layout } = useGridLayout({ + const { + layout, + isInteracting, + dragState, + onDragStart: hookOnDragStart, + onDragStop: hookOnDragStop, + onResizeStop: hookOnResizeStop + } = useGridLayout({ layout: (props.gridLayout || []) as Layout, cols: columns }); @@ -219,16 +226,38 @@ export const DisplayGridLayoutComponent = ( throw new Error("All grid items must have a stable id"); } + const activeId = dragState?.activeDrag?.i; + const isActiveDragging = isInteracting && activeId === id; + return (
{child} + {isActiveDragging && ( + // overlay captures clicks during drag so child won't toggle +
{ + e.stopPropagation(); + e.preventDefault(); + }} + /> + )}
); }), - [childrenArray, gridCellDragEnabled] + [childrenArray, gridCellDragEnabled, isInteracting, dragState] ); return ( @@ -261,10 +290,16 @@ export const DisplayGridLayoutComponent = ( ) => { if (element?.style && gridCellDragEnabled) element.style.cursor = "grabbing"; + if (newItem) { + hookOnDragStart(newItem.i, newItem.x, newItem.y); + } }} onDragStop={(layout, oldItem, newItem, placeholder, e, element) => { if (element?.style && gridCellDragEnabled) element.style.cursor = "grab"; + if (newItem) { + hookOnDragStop(newItem.i, newItem.x, newItem.y); + } dispatch( displayInstanceUpdateGridLayout({ embeddedDisplayUuid: props.embeddedDisplayUuid, @@ -273,7 +308,10 @@ export const DisplayGridLayoutComponent = ( }) ); }} - onResizeStop={layout => { + onResizeStop={(layout, oldItem, newItem) => { + if (newItem) { + hookOnResizeStop(newItem.i, newItem.w, newItem.h); + } dispatch( displayInstanceUpdateGridLayout({ embeddedDisplayUuid: props.embeddedDisplayUuid,