import { describe, expect, it } from "vitest"; import { cleanup, createSchema, FileDrop, fireEvent, Panel, render, renderControlsPanelWithSchema, screen, waitFor, } from "../testing/controls-panel-test-utils"; describe("ControlsPanel collapsible section shell", () => { it("exposes stable schema target ownership without changing control layout", () => { const { container } = renderControlsPanelWithSchema(createSchema()); const opacityBoundary = container.querySelector( '[data-toolcraft-control-target="selectedLayer.opacity"]', ); const colorGroupBoundary = container.querySelector( '[data-toolcraft-control-targets]', ); expect(opacityBoundary?.className).toContain("contents"); expect(opacityBoundary?.querySelector('[data-slot="field"]')).toBeTruthy(); expect( JSON.parse(colorGroupBoundary?.getAttribute("data-toolcraft-control-targets") ?? "[]"), ).toEqual(["style.fill", "style.stroke"]); expect(colorGroupBoundary?.querySelectorAll('[data-slot="field"]')).toHaveLength(2); }); it("renders titled body sections as 36px collapsible header rows", () => { const schema = createSchema(); const { container } = renderControlsPanelWithSchema(schema); const basicTitle = screen.getByText("Basic"); const header = basicTitle.closest('[data-slot="control-section-header"]'); const section = basicTitle.closest("section"); const controlList = section?.querySelector("[data-control-list]"); const body = section?.querySelector('[data-slot="panel-section-collapsible-body"]'); expect(header?.className).toContain("h-9"); expect(header?.getAttribute("data-collapsible")).toBe(""); expect(header?.getAttribute("aria-expanded")).toBe("true"); expect(header?.getAttribute("role")).toBe("button"); expect(body?.className).toContain("transition-[grid-template-rows,opacity]"); expect(body?.className).toContain("grid-rows-[1fr]"); expect(controlList?.className).toContain("pt-2"); expect(controlList?.className).toContain("pb-6"); expect(section?.textContent).toContain("Prompt"); expect(section?.textContent).toContain("Opacity"); fireEvent.click(header!); expect(section?.getAttribute("data-collapsed")).toBe("true"); expect(header?.getAttribute("aria-expanded")).toBe("false"); expect(body?.getAttribute("aria-hidden")).toBe("true"); expect(body?.className).toContain("grid-rows-[0fr]"); expect(section?.textContent).toContain("Basic"); expect(section?.textContent).toContain("Prompt"); fireEvent.transitionEnd(body!); expect(section?.textContent).not.toContain("Prompt"); expect(section?.textContent).not.toContain("Opacity"); fireEvent.keyDown(header!, { key: "Enter" }); expect(section?.getAttribute("data-collapsed")).toBe("false"); expect(header?.getAttribute("aria-expanded")).toBe("true"); expect(section?.querySelector('[data-slot="panel-section-collapsible-body"]')).toBeTruthy(); expect(container.querySelector('[data-slot="control-section-header"]')).toBeTruthy(); }); it("suppresses child control transitions when a section re-expands", () => { const schema = createSchema(); renderControlsPanelWithSchema(schema); const basicTitle = screen.getByText("Basic"); const header = basicTitle.closest('[data-slot="control-section-header"]'); const section = basicTitle.closest("section"); const initialBody = section?.querySelector( '[data-slot="panel-section-collapsible-body"]', ); fireEvent.click(header!); fireEvent.transitionEnd(initialBody!); fireEvent.click(header!); const expandedBody = section?.querySelector( '[data-slot="panel-section-collapsible-body"]', ); const innerControls = expandedBody?.firstElementChild as HTMLElement | null; expect(expandedBody?.className).toContain("transition-[grid-template-rows,opacity]"); expect(expandedBody?.className).toContain("grid-rows-[1fr]"); expect(innerControls?.getAttribute("data-toolcraft-controls-mounting")).toBe("true"); }); it("persists ordinary section collapse state as a UI preference across remounts", async () => { const schema = createSchema(); const { unmount } = renderControlsPanelWithSchema(schema); const basicHeader = screen .getByText("Basic") .closest('[data-slot="control-section-header"]'); fireEvent.click(basicHeader!); expect(basicHeader?.closest("section")?.getAttribute("data-collapsed")).toBe("true"); if (schema.persistence.storage !== "localStorage") { throw new Error("Expected default local persistence"); } const persistenceKey = schema.persistence.key; const basicSectionId = schema.panels.controls?.sections.find( (section) => section.title === "Basic", )?.id; await waitFor(() => { const persisted = JSON.parse( window.localStorage.getItem(persistenceKey) ?? "{}", ); expect(persisted.state.panels.controls.collapsedSections).toEqual({ [basicSectionId!]: true, }); }); unmount(); cleanup(); renderControlsPanelWithSchema(schema); const restoredSection = screen.getByText("Basic").closest("section"); expect(restoredSection?.getAttribute("data-collapsed")).toBe("true"); fireEvent.click(screen.getByRole("button", { name: "Reset controls" })); expect(screen.getByText("Basic").closest("section")?.getAttribute("data-collapsed")).toBe( "true", ); }); it("puts ordinary implicit panel section spacing on the control list layer", () => { const { container } = render( , ); const section = container.querySelector("section"); const controlList = section?.querySelector("[data-control-list]"); expect(section?.className).toContain("py-0"); expect(controlList?.className).toContain("pt-2"); expect(controlList?.className).toContain("pb-6"); expect(controlList?.textContent).toContain("Click to upload an image"); }); });