diff --git a/packages/studio/src/components/editor/propertyPanelFlatPrimitives.test.tsx b/packages/studio/src/components/editor/propertyPanelFlatPrimitives.test.tsx index 2eea86d44..954ea8052 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatPrimitives.test.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatPrimitives.test.tsx @@ -9,6 +9,7 @@ import { FlatSegmentedRow, FlatSelectRow, FlatSlider, + FlatToggle, PinnedZoneDivider, } from "./propertyPanelFlatPrimitives"; @@ -286,3 +287,44 @@ describe("FlatSelectRow", () => { act(() => root.unmount()); }); }); + +describe("FlatToggle", () => { + it("renders the off state with a dim label and dim knob, and fires onChange(true) on click", () => { + const onChange = vi.fn(); + const { host, root } = renderInto( + , + ); + const label = host.querySelector('[data-flat-toggle-label="true"]'); + expect(label?.className).toContain("text-panel-text-3"); + const pill = host.querySelector('[data-flat-toggle="true"]'); + expect(pill).not.toBeNull(); + act(() => pill?.dispatchEvent(new MouseEvent("click", { bubbles: true }))); + expect(onChange).toHaveBeenCalledWith(true); + act(() => root.unmount()); + }); + + it("renders the on state with an emphasized label and mint knob, and fires onChange(false) on click", () => { + const onChange = vi.fn(); + const { host, root } = renderInto(); + const label = host.querySelector('[data-flat-toggle-label="true"]'); + expect(label?.className).toContain("text-panel-text-2"); + const knob = host.querySelector('[data-flat-toggle-knob="true"]'); + expect(knob?.className).toContain("bg-panel-accent"); + const pill = host.querySelector('[data-flat-toggle="true"]'); + act(() => pill?.dispatchEvent(new MouseEvent("click", { bubbles: true }))); + expect(onChange).toHaveBeenCalledWith(false); + act(() => root.unmount()); + }); + + it("does not fire onChange when disabled", () => { + const onChange = vi.fn(); + const { host, root } = renderInto( + , + ); + const pill = host.querySelector('[data-flat-toggle="true"]'); + expect(pill?.disabled).toBe(true); + act(() => pill?.dispatchEvent(new MouseEvent("click", { bubbles: true }))); + expect(onChange).not.toHaveBeenCalled(); + act(() => root.unmount()); + }); +}); diff --git a/packages/studio/src/components/editor/propertyPanelFlatPrimitives.tsx b/packages/studio/src/components/editor/propertyPanelFlatPrimitives.tsx index 6cc221735..e6dc9f1f7 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatPrimitives.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatPrimitives.tsx @@ -375,3 +375,49 @@ export function FlatSelectRow({ ); } + +/* ------------------------------------------------------------------ */ +/* FlatToggle — 24×14 pill switch */ +/* ------------------------------------------------------------------ */ + +export function FlatToggle({ + label, + checked, + disabled, + onChange, +}: { + label: string; + checked: boolean; + disabled?: boolean; + onChange: (next: boolean) => void; +}) { + return ( +
+ + {label} + + +
+ ); +}