fix(studio): right-align FlatRow's value input in the flat inspector

FlatRow lays out label…gap…value across a `justify-between` row, but the
shared CommitField input it wraps had no text-align, so its text hugged
the LEFT edge of the value's own (often much wider) right-hand box —
looking left-aligned relative to the row, out of step with FlatSelectRow
and FlatSlider, which already right-align.

Added an optional `align` prop to CommitField (default "left", preserving
the legacy panel's MetricField/DetailField layouts where label-then-value
sits inline and left reads naturally) and pass `align="right"` from
FlatRow. Left the Motion Timing row's Start/End/Duration cells alone —
those stack label-above-value in a grid, a different pattern from the
inline label…value row this fix targets.

New tests: FlatRow's input has `text-right` (not `text-left`); the legacy
MetricField's input keeps `text-left` (not `text-right`), pinning
CommitField's default so the shared component doesn't drift for the
panel that didn't ask for this.

Full studio suite (2645 tests) green; typecheck/oxlint/oxfmt clean.
This commit is contained in:
Vance Ingalls
2026-07-16 00:48:53 -07:00
parent b0bb468877
commit b32f9a3e86
4 changed files with 27 additions and 1 deletions
@@ -38,6 +38,16 @@ describe("FlatRow", () => {
act(() => root.unmount());
});
it("right-aligns the value input — the flat inspector's justify-between row layout leaves a left-aligned value looking stranded at the edge of its own box", () => {
const { host, root } = renderInto(
<FlatRow label="Size" value="72px" tier="explicitCustom" onCommit={vi.fn()} />,
);
const input = host.querySelector("input");
expect(input?.className).toContain("text-right");
expect(input?.className).not.toContain("text-left");
act(() => root.unmount());
});
it("renders the explicitCustom tier with a mint value and a reset button", () => {
const onReset = vi.fn();
const { host, root } = renderInto(
@@ -51,6 +51,7 @@ export function FlatRow({
value={value}
disabled={disabled}
liveCommit={liveCommit}
align="right"
onCommit={(nextValue) => {
track("metric", label);
onCommit(nextValue);
@@ -134,6 +134,13 @@ describe("classic property-panel primitive telemetry", () => {
const input = host.querySelector("input");
if (!input) throw new Error("expected metric input");
// Regression guard for CommitField's shared `align` default: the classic
// panel lays out label-then-value inline, where left-aligned reads
// naturally — this must stay left even though the flat inspector's
// FlatRow now opts into `align="right"` for its own justify-between rows.
expect(input.className).toContain("text-left");
expect(input.className).not.toContain("text-right");
act(() => blurInput(input));
expect(trackStudioEvent).not.toHaveBeenCalled();
@@ -9,11 +9,17 @@ export function CommitField({
value,
disabled,
liveCommit,
align = "left",
onCommit,
}: {
value: string;
disabled?: boolean;
liveCommit?: boolean;
/** The legacy panel lays out label-then-value inline (left reads naturally);
* the flat inspector lays out labelvalue across a `justify-between` row,
* where a left-aligned value looks stranded at the edge of its own
* right-hand box instead of lining up with every other row's value. */
align?: "left" | "right";
onCommit: (nextValue: string) => void;
}) {
const [draft, setDraft] = useState(value);
@@ -92,7 +98,9 @@ export function CommitField({
scheduleCommit(nextDraft);
}}
title={parseNumericToken(value) ? "Scroll or use Arrow keys to adjust" : undefined}
className="min-w-0 w-full bg-transparent text-[11px] font-medium text-neutral-100 outline-none disabled:cursor-not-allowed disabled:text-neutral-600"
className={`min-w-0 w-full bg-transparent text-[11px] font-medium text-neutral-100 outline-none disabled:cursor-not-allowed disabled:text-neutral-600 ${
align === "right" ? "text-right" : "text-left"
}`}
/>
);
}