diff --git a/packages/studio/src/components/editor/propertyPanelFlatLayoutSection.test.tsx b/packages/studio/src/components/editor/propertyPanelFlatLayoutSection.test.tsx index db80e32ba..827f9fad4 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatLayoutSection.test.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatLayoutSection.test.tsx @@ -3,7 +3,11 @@ import React, { act } from "react"; import { createRoot } from "react-dom/client"; import { afterEach, describe, expect, it, vi } from "vitest"; -import { LayoutGeometryRows } from "./propertyPanelFlatLayoutSection"; +import { + LayoutFlexBlock, + LayoutGeometryRows, + LayoutZIndexRow, +} from "./propertyPanelFlatLayoutSection"; (globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true; @@ -107,3 +111,55 @@ describe("LayoutGeometryRows", () => { act(() => root.unmount()); }); }); + +describe("LayoutZIndexRow", () => { + it("renders the current z-index at the default tier and commits edits", () => { + const onSetStyle = vi.fn(); + const { host, root } = renderInto( + , + ); + expect(host.textContent).toContain("Z-index"); + const input = host.querySelector("input"); + if (!input) throw new Error("expected an input"); + expect(input.value).toBe("3"); + const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")!.set!; + act(() => { + setter.call(input, "5"); + input.dispatchEvent(new Event("input", { bubbles: true })); + input.dispatchEvent(new Event("focusout", { bubbles: true })); + }); + expect(onSetStyle).toHaveBeenCalledWith("z-index", "5"); + act(() => root.unmount()); + }); +}); + +describe("LayoutFlexBlock", () => { + it("renders nothing when the element is not flex", () => { + const { host, root } = renderInto( + , + ); + expect(host.textContent).toBe(""); + act(() => root.unmount()); + }); + + it("renders direction/justify/align/gap and commits a direction change", () => { + const onSetStyle = vi.fn(); + const { host, root } = renderInto( + , + ); + expect(host.textContent).toContain("Flex"); + const columnOption = Array.from(host.querySelectorAll('[data-flat-segment="true"]')).find( + (el) => el.textContent === "Column", + ); + if (!columnOption) throw new Error("expected a Column segment option"); + act(() => + (columnOption as HTMLElement).dispatchEvent(new MouseEvent("click", { bubbles: true })), + ); + expect(onSetStyle).toHaveBeenCalledWith("flex-direction", "column"); + act(() => root.unmount()); + }); +}); diff --git a/packages/studio/src/components/editor/propertyPanelFlatLayoutSection.tsx b/packages/studio/src/components/editor/propertyPanelFlatLayoutSection.tsx index d5c8d5db9..477893d4e 100644 --- a/packages/studio/src/components/editor/propertyPanelFlatLayoutSection.tsx +++ b/packages/studio/src/components/editor/propertyPanelFlatLayoutSection.tsx @@ -1,7 +1,8 @@ -import { FlatRow } from "./propertyPanelFlatPrimitives"; +import { FlatRow, FlatSegmentedRow, FlatSelectRow } from "./propertyPanelFlatPrimitives"; import { KeyframeNavigation } from "./KeyframeNavigation"; import { formatPxMetricValue } from "./propertyPanelHelpers"; import { STUDIO_KEYFRAMES_ENABLED } from "./manualEditingAvailability"; +import { resolveValueTier } from "./propertyPanelValueTier"; type KeyframeEntry = Array<{ percentage: number; @@ -156,3 +157,81 @@ export function LayoutGeometryRows({ ); } + +export function LayoutZIndexRow({ + styles, + onSetStyle, +}: { + styles: Record; + onSetStyle: (prop: string, value: string) => void | Promise; +}) { + const zIndex = String(parseInt(styles["z-index"] || "auto", 10) || 0); + return ( + void onSetStyle("z-index", next)} + /> + ); +} + +export function LayoutFlexBlock({ + styles, + onSetStyle, + disabled, +}: { + styles: Record; + onSetStyle: (prop: string, value: string) => void | Promise; + disabled: boolean; +}) { + const isFlex = styles.display === "flex" || styles.display === "inline-flex"; + if (!isFlex) return null; + const direction = styles["flex-direction"] || "row"; + return ( +
+
+ Flex +
+ void onSetStyle("flex-direction", next)} + /> + void onSetStyle("justify-content", next)} + /> + void onSetStyle("align-items", next)} + /> + void onSetStyle("gap", next.endsWith("px") ? next : `${next}px`)} + /> +
+ ); +}