import { cleanup, fireEvent, render, screen } from "@testing-library/react"; import * as React from "react"; import { afterEach, describe, expect, it, vi } from "vitest"; import { defineToolcraft } from "../../schema/define-toolcraft"; import type { ToolcraftPanelState } from "../../state/types"; import { ToolcraftRoot } from "../app-shell/toolcraft-root"; import { useToolcraft } from "../app-shell/use-toolcraft"; import { useToolcraftPanelBinding } from "./use-toolcraft-panel-binding"; const schema = defineToolcraft({ canvas: { enabled: true }, panels: { controls: { sections: [ { controls: { opacity: { defaultValue: 75, target: "appearance.opacity", type: "slider", }, }, id: "appearance", title: "Appearance", }, ], title: "Controls", }, }, }); afterEach(() => { cleanup(); window.localStorage.clear(); }); function PanelBindingProbe({ onPanelStateChange, panelState, }: { onPanelStateChange?: (patch: Partial) => void; panelState?: ToolcraftPanelState; }): React.JSX.Element { const { dispatch, state } = useToolcraft(); const binding = useToolcraftPanelBinding({ onPanelStateChange, panelId: "controls", panelState, }); return ( <> {JSON.stringify(binding.panelState)} {JSON.stringify(state.panels.controls)} {state.history.undo.length} ); } describe("useToolcraftPanelBinding", () => { it("binds panel changes to committed runtime state without history", () => { render( , ); fireEvent.click(screen.getByRole("button", { name: "Commit placement" })); expect(JSON.parse(screen.getByTestId("runtime-panel-state").textContent ?? "{}")) .toMatchObject({ collapsed: true, offset: { x: 64, y: 32 }, snapEdge: "right", }); expect(screen.getByTestId("history-count").textContent).toBe("0"); }); it("keeps deliberately controlled state authoritative", () => { const onPanelStateChange = vi.fn(); const controlledState: ToolcraftPanelState = { collapsed: false, offset: { x: 12, y: 18 }, snapEdge: "left", }; render( , ); fireEvent.click(screen.getByRole("button", { name: "Commit placement" })); expect(onPanelStateChange).toHaveBeenCalledWith({ collapsed: true, offset: { x: 64, y: 32 }, snapEdge: "right", }); expect(JSON.parse(screen.getByTestId("bound-panel-state").textContent ?? "{}")) .toEqual({ ...controlledState, collapsedSections: {} }); expect(JSON.parse(screen.getByTestId("runtime-panel-state").textContent ?? "{}")) .toMatchObject({ offset: { x: 0, y: 0 } }); }); it("keeps controls-specific runtime state alongside controlled base fields", () => { const controlledState: ToolcraftPanelState = { collapsed: false, offset: { x: 12, y: 18 }, snapEdge: "left", }; render( , ); fireEvent.click(screen.getByRole("button", { name: "Collapse appearance" })); expect(JSON.parse(screen.getByTestId("bound-panel-state").textContent ?? "{}")) .toEqual({ collapsed: false, collapsedSections: { appearance: true }, offset: { x: 12, y: 18 }, snapEdge: "left", }); }); });