fix(studio): keep narrow timeline headers readable (#4232)

* fix(studio): hide narrow timeline header labels

* test(studio): cover narrow automation headers

* test(studio): cover narrow automation headers

* test(studio): assert narrow lane tooltip

* style(studio): format narrow header changes

* test(studio): cover narrow speed lane labels

* fix(studio): truncate timeline header names

* test(studio): remove stale header room plumbing
This commit is contained in:
Miguel Ángel
2026-09-20 19:49:29 -04:00
committed by GitHub
parent e86a530f0b
commit b4787082b2
4 changed files with 21 additions and 16 deletions
@@ -95,9 +95,9 @@ export function LayerDisclosureRow({
<span aria-hidden="true" className="shrink-0 text-[13px] leading-none text-white/40">
◇
</span>
{/* Wraps rather than truncating: a truncated name needs a hover to be
read, which a scanned column cannot rely on. */}
<span className="min-w-0 flex-1 wrap-break-word font-medium leading-tight">{name}</span>
<span title={name} className="min-w-0 flex-1 truncate font-medium leading-tight">
{name}
</span>
<TrackClipCount clipCount={clipCount} />
{children}
{/* Anchored right, on every header that has one: the lane toggle is the
@@ -65,9 +65,9 @@ function GroupNameButton({
<span aria-hidden="true" className="shrink-0 text-[12px] leading-none text-white/50">
▤
</span>
{/* Wraps rather than truncating — a name that needs a hover to be read
is no use in a column you scan. */}
<span className="min-w-0 wrap-break-word text-left font-medium leading-tight">{label}</span>
<span title={label} className="min-w-0 truncate text-left font-medium leading-tight">
{label}
</span>
<span
className="shrink-0 rounded-full bg-white/10 px-1 text-[9px] leading-[14px] tabular-nums text-white/55"
aria-hidden="true"
@@ -109,9 +109,7 @@ export function TimelineGroupHeader({
}}
>
{/* One line, like a track header's: caret and name, then every control
anchored to the right edge. The name wraps and the controls are
`shrink-0`, so they hold the edge and the name gives way — no second
line needed to keep five controls off the label. */}
anchored to the right edge. */}
<button
type="button"
tabIndex={-1}
@@ -136,4 +136,14 @@ describe("PlainTrackHeader", () => {
expect(labelOf(view.host)).toBe("Hide track 1");
view.unmount();
});
it("truncates long names and keeps the full name in the tooltip", () => {
const name = "A-very-long-unbroken-clip-name-for-the-timeline";
const view = renderHeader({ trackLabel: name });
const label = view.host.querySelector("span[title]");
expect(label?.className).toContain("truncate");
expect(label?.className).not.toContain("wrap-break-word");
expect(label?.getAttribute("title")).toBe(name);
view.unmount();
});
});
@@ -93,14 +93,11 @@ export function PlainTrackHeader({
)}
{/* No `flex-1`: the name takes only the width it needs, so the clip
count sits against it rather than being pushed out to meet the
controls. The slack goes to the `ml-auto` group below instead.
Wraps rather than truncating: a truncated name needs a hover to be
read at all, and a tooltip is no use to a name you are scanning a
column of. `wrap-break-word` so a long single token breaks instead of
forcing the column wider. */}
controls. The slack goes to the `ml-auto` group below instead. */}
{showTrackLabel && (
<span className="min-w-0 wrap-break-word text-[11px] leading-tight">{trackLabel}</span>
<span title={trackLabel} className="min-w-0 truncate text-[11px] leading-tight">
{trackLabel}
</span>
)}
{showTrackLabel && <TrackClipCount clipCount={clipCount} />}
{/* `ml-auto` is what anchors the group right: it absorbs the slack the