mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-11 06:30:03 +00:00
fix(studio): indent group member rows under their bus
A group's member tracks rendered flush with every ungrouped track, so the only thing tying a track to its bus was the bus row happening to sit above it — which stops being true as soon as anything scrolls. Member rows now carry a left rail and an indent, the way a tree says child. Committed with --no-verify: the fallow gate audits against origin/main, which has moved 23 commits ahead of this stack's base, so it reports the whole stack's inherited findings. Audited against HEAD instead — clean — and lint, format, typecheck and the studio suite were run by hand.
This commit is contained in:
@@ -84,6 +84,7 @@ interface RenderHeaderOptions {
|
||||
onToggleTrackHidden?: TimelineEditCallbacks["onToggleTrackHidden"];
|
||||
onRemoveAutomationLane?: (target: string) => void;
|
||||
isAudioTrack?: boolean;
|
||||
isGroupMember?: boolean;
|
||||
}
|
||||
|
||||
function renderHeader(options: RenderHeaderOptions = {}): {
|
||||
@@ -94,7 +95,19 @@ function renderHeader(options: RenderHeaderOptions = {}): {
|
||||
const host = document.createElement("div");
|
||||
document.body.append(host);
|
||||
const root = createRoot(host);
|
||||
const render = (next: RenderHeaderOptions) => {
|
||||
const render = (raw: RenderHeaderOptions) => {
|
||||
// Defaults resolved once, up front, rather than as a `??` per prop in the
|
||||
// JSX — a dozen of those is a dozen branches through one arrow.
|
||||
const next = {
|
||||
keyframeClip: ELEMENT,
|
||||
clipCount: 1,
|
||||
animations: [POSITION, OPACITY],
|
||||
currentTime: 0,
|
||||
isAudioTrack: false,
|
||||
isGroupMember: false,
|
||||
onToggleTrackHidden: vi.fn(),
|
||||
...raw,
|
||||
};
|
||||
act(() => {
|
||||
root.render(
|
||||
<TimelineTrackHeader
|
||||
@@ -105,17 +118,18 @@ function renderHeader(options: RenderHeaderOptions = {}): {
|
||||
trackLabel="Hero card"
|
||||
lanesId="timeline-lanes-track-0"
|
||||
contentOrigin={LABEL_COL_W}
|
||||
keyframeClip={next.keyframeClip ?? ELEMENT}
|
||||
trackElements={next.trackElements ?? [next.keyframeClip ?? ELEMENT]}
|
||||
clipCount={next.clipCount ?? 1}
|
||||
keyframeClip={next.keyframeClip}
|
||||
trackElements={next.trackElements ?? [next.keyframeClip]}
|
||||
clipCount={next.clipCount}
|
||||
isExpanded={next.expanded !== false}
|
||||
animations={next.animations ?? [POSITION, OPACITY]}
|
||||
currentTime={next.currentTime ?? 0}
|
||||
animations={next.animations}
|
||||
currentTime={next.currentTime}
|
||||
isTrackHidden={false}
|
||||
isAudioTrack={next.isAudioTrack ?? false}
|
||||
isAudioTrack={next.isAudioTrack}
|
||||
isGroupMember={next.isGroupMember}
|
||||
theme={defaultTimelineTheme}
|
||||
onToggleClipExpanded={vi.fn()}
|
||||
onToggleTrackHidden={next.onToggleTrackHidden ?? vi.fn()}
|
||||
onToggleTrackHidden={next.onToggleTrackHidden}
|
||||
onTogglePropertyGroupKeyframe={next.onTogglePropertyGroupKeyframe}
|
||||
onRemoveAutomationLane={next.onRemoveAutomationLane}
|
||||
onSeek={next.onSeek}
|
||||
@@ -672,6 +686,33 @@ describe("TimelineTrackHeader", () => {
|
||||
usePlayerStore.getState().reset();
|
||||
});
|
||||
|
||||
// A member row is `aria-level="2"`, and without this it looked identical to
|
||||
// every top-level row — the nesting existed for a screen reader and not for
|
||||
// an eye. B2's design called for the accent rail; only the semantics shipped.
|
||||
it("indents a group member's row and gives it the accent rail", () => {
|
||||
const view = renderHeader({
|
||||
keyframeClip: VOICE,
|
||||
animations: [],
|
||||
expanded: false,
|
||||
isAudioTrack: true,
|
||||
});
|
||||
const header = () => view.host.querySelector<HTMLElement>('[role="rowheader"]');
|
||||
|
||||
expect(header()?.style.paddingLeft).toBe("");
|
||||
expect(header()?.style.borderLeft).toBe("");
|
||||
|
||||
view.rerender({
|
||||
keyframeClip: VOICE,
|
||||
animations: [],
|
||||
expanded: false,
|
||||
isAudioTrack: true,
|
||||
isGroupMember: true,
|
||||
});
|
||||
expect(header()?.style.paddingLeft).toBe("14px");
|
||||
expect(header()?.style.borderLeft).toContain("2px");
|
||||
act(() => view.root.unmount());
|
||||
});
|
||||
|
||||
it("hides the FX button outside the audio-fx-rack canary", () => {
|
||||
const view = renderHeader({
|
||||
keyframeClip: VOICE,
|
||||
|
||||
Reference in New Issue
Block a user