import { expect } from "vitest"; import { defineToolcraft, vi } from "./controls-panel-test-utils"; export function createNavigationSchema(fieldTitle = "Field") { return defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: false }, panels: { controls: { sections: [ { id: "test-section-1", controls: { density: { applicability: { mode: "always" as const }, defaultValue: 50, label: "Density", max: 100, min: 0, target: "field.density", type: "slider", }, }, title: fieldTitle, }, { id: "test-section-2", controls: { enabled: { applicability: { mode: "always" as const }, defaultValue: true, label: "Enabled", target: "wind.enabled", type: "switch", }, }, title: "Wind", }, { id: "test-section-3", actionGroup: "secondary", controls: { export: { applicability: { mode: "always" as const }, actions: [ { command: "controls.apply", label: "Export", value: "export", variant: "default", }, ], target: "panel.actions", type: "panelActions", }, }, title: "Export", }, ], title: "Grass Studio", }, }, }, modules: [], }); } export function createRect({ height, left = 0, top, width = 300, }: { height: number; left?: number; top: number; width?: number; }): DOMRect { return { bottom: top + height, height, left, right: left + width, toJSON: () => ({}), top, width, x: left, y: top, }; } export function prepareScrollablePanel(container: HTMLElement) { const root = container.querySelector( "[data-toolcraft-controls-panel-shell]", ); const viewport = container.querySelector( '[data-slot="toolcraft-panel-content"]', ); const panel = container.querySelector( '[data-panel-id="properties"]', ); const sectionAnchors = Array.from( container.querySelectorAll( "[data-toolcraft-controls-section-anchor]", ), ); expect(root).toBeTruthy(); expect(panel).toBeTruthy(); expect(viewport).toBeTruthy(); expect(sectionAnchors).toHaveLength(4); Object.defineProperties(viewport!, { clientHeight: { configurable: true, value: 300 }, scrollHeight: { configurable: true, value: 900 }, scrollTop: { configurable: true, value: 0, writable: true }, }); vi.spyOn(root!, "getBoundingClientRect").mockReturnValue( createRect({ height: 360, left: 400, top: 10 }), ); vi.spyOn(panel!, "getBoundingClientRect").mockReturnValue( createRect({ height: 360, left: 400, top: 10 }), ); vi.spyOn(viewport!, "getBoundingClientRect").mockReturnValue( createRect({ height: 300, left: 400, top: 60 }), ); const sectionTops = [-200, 40, 240, 380]; sectionAnchors.forEach((section, index) => { vi.spyOn(section, "getBoundingClientRect").mockImplementation(() => createRect({ height: 180, left: 400, top: sectionTops[index] ?? 0, }), ); }); const scrollTo = vi.fn(); Object.defineProperty(viewport!, "scrollTo", { configurable: true, value: scrollTo, }); return { panel: panel!, root: root!, scrollTo, sectionAnchors, viewport: viewport!, }; }