mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 12:54:29 +00:00
fix(studio): hide a group's lane toggle when it automates nothing
A track header already gates its own automation toggle on having something to disclose (`disclosable`). A group's did not, so every group offered a wave button that opened an empty row — which is how the author learns the group has no automation, one click too late. Gated on the same count the badge already used. Nothing becomes unreachable: automation appears on a group by being written, from the rack or a keyframe, not by opening this. The suite had nothing covering the toggle's presence, so the test is new and was mutation-checked — forcing the condition true fails it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ff55adfcb6
commit
624166f380
@@ -141,27 +141,33 @@ export function TimelineGroupHeader({
|
||||
auditionSpans={auditionSpans}
|
||||
onOpenRack={onOpenFxRack}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
tabIndex={-1}
|
||||
aria-expanded={isLaneOpen}
|
||||
aria-label={`${isLaneOpen ? "Hide" : "Show"} ${label} lanes`}
|
||||
title={`${isLaneOpen ? "Hide" : "Show"} lanes`}
|
||||
// Anchored right, matching every other header's lane toggle.
|
||||
className={`ml-auto flex h-6 items-center justify-center gap-0.5 rounded border-0 bg-transparent px-1 text-[11px] leading-none focus-visible:outline focus-visible:outline-1 focus-visible:outline-[#3CE6AC] ${
|
||||
isLaneOpen ? "text-[#3CE6AC]" : "text-white/55 hover:text-white"
|
||||
}`}
|
||||
onPointerDown={(event) => event.stopPropagation()}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onToggleLanes();
|
||||
}}
|
||||
>
|
||||
<span aria-hidden="true">∿</span>
|
||||
{laneCount > 0 && (
|
||||
{/* No lanes, no control: an author who opens it meets an empty row and
|
||||
learns nothing. A track header already gates its own `∿` this way
|
||||
(`disclosable`); the group's was the one that still offered a
|
||||
disclosure over nothing. Automation appears by being written — from
|
||||
the rack or a keyframe — not by opening this, so nothing is
|
||||
unreachable while it is hidden. */}
|
||||
{laneCount > 0 && (
|
||||
<button
|
||||
type="button"
|
||||
tabIndex={-1}
|
||||
aria-expanded={isLaneOpen}
|
||||
aria-label={`${isLaneOpen ? "Hide" : "Show"} ${label} lanes`}
|
||||
title={`${isLaneOpen ? "Hide" : "Show"} lanes`}
|
||||
// Anchored right, matching every other header's lane toggle.
|
||||
className={`ml-auto flex h-6 items-center justify-center gap-0.5 rounded border-0 bg-transparent px-1 text-[11px] leading-none focus-visible:outline focus-visible:outline-1 focus-visible:outline-[#3CE6AC] ${
|
||||
isLaneOpen ? "text-[#3CE6AC]" : "text-white/55 hover:text-white"
|
||||
}`}
|
||||
onPointerDown={(event) => event.stopPropagation()}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onToggleLanes();
|
||||
}}
|
||||
>
|
||||
<span aria-hidden="true">∿</span>
|
||||
<span className="text-[9px] tabular-nums text-white/55">{laneCount}</span>
|
||||
)}
|
||||
</button>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -36,7 +36,7 @@ const GROUP: TimelineTrackGroupInfo = {
|
||||
hidden: false,
|
||||
};
|
||||
|
||||
function renderRow() {
|
||||
function renderRow(overrides: Partial<TimelineTrackGroupInfo> = {}) {
|
||||
const onSetAudioGroupAttributeQuiet = vi.fn();
|
||||
const onSetElementAttributeQuiet = vi.fn();
|
||||
const host = document.createElement("div");
|
||||
@@ -47,7 +47,7 @@ function renderRow() {
|
||||
<TimelineGroupRow
|
||||
index={0}
|
||||
rowKey={0}
|
||||
group={GROUP}
|
||||
group={{ ...GROUP, ...overrides }}
|
||||
logicalRow={{ id: "g", level: 1, kind: "track" } as never}
|
||||
top={0}
|
||||
height={48}
|
||||
@@ -93,4 +93,28 @@ describe("TimelineGroupRow", () => {
|
||||
// The members are the point: not one write reaches them.
|
||||
expect(onSetElementAttributeQuiet).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// A disclosure over nothing tells the author their group has no automation
|
||||
// only AFTER they open an empty row. Track headers already gate their own
|
||||
// toggle on having something to disclose; the group's did not.
|
||||
it("hides the lane toggle until the group actually automates something", () => {
|
||||
const laneToggle = (host: HTMLElement) =>
|
||||
Array.from(host.querySelectorAll("button")).find((b) =>
|
||||
/lanes$/.test(b.getAttribute("aria-label") ?? ""),
|
||||
);
|
||||
|
||||
expect(laneToggle(renderRow().host)).toBeUndefined();
|
||||
|
||||
const automated = renderRow({
|
||||
fxChain: JSON.stringify({
|
||||
version: 1,
|
||||
nodes: [{ type: "peaking", id: "p1", params: { frequency: 1000, gain: -3, q: 1 } }],
|
||||
}),
|
||||
automation: JSON.stringify({
|
||||
version: 1,
|
||||
lanes: [{ target: "fx.p1.gain", points: [{ t: 0, v: 0 }] }],
|
||||
}),
|
||||
});
|
||||
expect(laneToggle(automated.host)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user