import { fireEvent, screen } from "@testing-library/react"; import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; import type { ToolcraftLayer } from "../../state/types"; import { cleanupLayersPanelTestEnv, installLayersPanelMatchMedia, mockLayerRowRect, renderLayersPanel, seededLayers, } from "./layers-panel-test-utils"; beforeAll(() => { installLayersPanelMatchMedia(); }); afterEach(() => { cleanupLayersPanelTestEnv(); }); describe("LayersPanel drag and drop", () => { it("drags layers into groups and keeps the moved layer under the group", () => { const { container } = renderLayersPanel(); const groupRow = container.querySelector('[data-layer-id="group-1"]'); const layerRow = container.querySelector('[data-layer-id="layer-2"]'); expect(groupRow).toBeTruthy(); expect(layerRow).toBeTruthy(); mockLayerRowRect(groupRow as HTMLElement); Object.defineProperty(document, "elementFromPoint", { configurable: true, value: vi.fn(() => groupRow), }); fireEvent.pointerDown(layerRow as HTMLElement, { button: 0, clientX: 0, clientY: 0, pointerId: 1, }); fireEvent.pointerMove(layerRow as HTMLElement, { clientX: 12, clientY: 16, pointerId: 1, }); fireEvent.pointerUp(layerRow as HTMLElement, { pointerId: 1 }); expect(screen.getByTestId("layer-tree").textContent).toBe( "group-1:root|layer-2:group-1|layer-1:group-1", ); }); it("opens closed groups after dropping a layer into them", () => { const collapsedLayers = [ { ...seededLayers[0]!, collapsed: true }, seededLayers[1]!, seededLayers[2]!, ]; const { container } = renderLayersPanel({}, collapsedLayers); const groupRow = container.querySelector('[data-layer-id="group-1"]'); const layerRow = container.querySelector('[data-layer-id="layer-2"]'); expect(groupRow).toBeTruthy(); expect(layerRow).toBeTruthy(); expect(screen.queryByText("Layer 1")).toBeNull(); mockLayerRowRect(groupRow as HTMLElement); Object.defineProperty(document, "elementFromPoint", { configurable: true, value: vi.fn(() => groupRow), }); fireEvent.pointerDown(layerRow as HTMLElement, { button: 0, clientX: 0, clientY: 0, pointerId: 1, }); fireEvent.pointerMove(layerRow as HTMLElement, { clientX: 12, clientY: 16, pointerId: 1, }); fireEvent.pointerUp(layerRow as HTMLElement, { pointerId: 1 }); expect(screen.getByTestId("group-collapsed").textContent).toBe("false"); expect(screen.getByTestId("layer-tree").textContent).toBe( "group-1:root|layer-2:group-1|layer-1:group-1", ); expect(screen.getByText("Layer 1")).toBeTruthy(); }); it("highlights group targets and suppresses ordinary row hover during drag", () => { const layers: ToolcraftLayer[] = [ ...seededLayers, { displayName: "Layer 3", id: "layer-3", kind: "layer", name: "layer-3", visible: true, }, ]; const { container } = renderLayersPanel({}, layers); const groupRow = container.querySelector('[data-layer-id="group-1"]'); const dragRow = container.querySelector('[data-layer-id="layer-2"]'); const idleRow = container.querySelector('[data-layer-id="layer-3"]'); expect(groupRow).toBeTruthy(); expect(dragRow).toBeTruthy(); expect(idleRow).toBeTruthy(); mockLayerRowRect(groupRow as HTMLElement); Object.defineProperty(document, "elementFromPoint", { configurable: true, value: vi.fn(() => groupRow), }); fireEvent.pointerDown(dragRow as HTMLElement, { button: 0, clientX: 0, clientY: 0, pointerId: 1, }); fireEvent.pointerMove(dragRow as HTMLElement, { clientX: 12, clientY: 16, pointerId: 1, }); const groupSurface = groupRow?.querySelector('[data-layer-row-surface]'); const idleSurface = idleRow?.querySelector('[data-layer-row-surface]'); const idleActions = idleRow?.querySelector("[data-layer-actions]"); expect(groupSurface?.className).toContain("border-[color:var(--accent)]"); expect(groupRow?.dataset.dropTarget).toBe("true"); expect(groupRow?.dataset.reorderDragging).toBe("true"); expect(idleRow?.dataset.reorderDragging).toBe("true"); expect(groupSurface?.className).toContain( "bg-[color:color-mix(in_oklab,var(--accent)_15%,transparent)]", ); expect(idleSurface?.className).not.toContain("hover:bg"); expect(idleActions?.className).not.toContain("group-hover/layer"); }); it("uses pointer depth to drag layers out of nested groups", () => { const nestedLayers = [ seededLayers[0], seededLayers[1], { ...seededLayers[2], parentGroupId: "group-1" }, { displayName: "Layer 3", id: "layer-3", kind: "layer" as const, name: "layer-3", visible: true, }, ]; const { container } = renderLayersPanel({}, nestedLayers); const sourceRow = container.querySelector('[data-layer-id="layer-1"]'); const targetRow = container.querySelector('[data-layer-id="layer-2"]'); expect(sourceRow).toBeTruthy(); expect(targetRow).toBeTruthy(); mockLayerRowRect(targetRow as HTMLElement, 32); Object.defineProperty(document, "elementFromPoint", { configurable: true, value: vi.fn(() => targetRow), }); fireEvent.pointerDown(sourceRow as HTMLElement, { button: 0, clientX: 40, clientY: 16, pointerId: 1, }); fireEvent.pointerMove(sourceRow as HTMLElement, { clientX: 0, clientY: 60, pointerId: 1, }); fireEvent.pointerUp(sourceRow as HTMLElement, { pointerId: 1 }); expect(screen.getByTestId("layer-tree").textContent).toBe( "group-1:root|layer-2:group-1|layer-1:root|layer-3:root", ); }); });