mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-10 22:20:14 +00:00
fix(studio): sub-composition child clips restack via self-contained intent
applyTimelineStackingReorder resolved each z-index change by looking the clip up in the top-level timelineElements list, but sub-composition children live only in the expanded list, so the lookup missed them and the reorder silently bailed. Carry the element's locator (domId/selector/sourceFile) on each z-index change so the commit resolves the live element directly from the preview DOM. Verified E2E: dragging a clip inside a sub-composition restacks it and patches the sub-comp source, while the parent composition is untouched. Adds timelineEditingHelpers.test.ts (locator-based commit + audio no-op).
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
// @vitest-environment jsdom
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { applyTimelineStackingReorder } from "./timelineEditingHelpers";
|
||||
import type { TimelineElement } from "../player/store/playerStore";
|
||||
|
||||
function makeIframeWith(html: string): HTMLIFrameElement {
|
||||
const iframe = document.createElement("iframe");
|
||||
document.body.append(iframe);
|
||||
const doc = iframe.contentDocument;
|
||||
if (!doc) throw new Error("expected iframe document");
|
||||
doc.body.innerHTML = html;
|
||||
return iframe;
|
||||
}
|
||||
|
||||
function el(input: Partial<TimelineElement> & { id: string; tag: string }): TimelineElement {
|
||||
return {
|
||||
label: input.id,
|
||||
start: 0,
|
||||
duration: 5,
|
||||
track: 0,
|
||||
zIndex: 0,
|
||||
hasExplicitZIndex: false,
|
||||
stackingContextId: null,
|
||||
...input,
|
||||
};
|
||||
}
|
||||
|
||||
describe("applyTimelineStackingReorder", () => {
|
||||
it("commits via the change's own locator even when the element is not in timelineElements", () => {
|
||||
// Sub-comp children live in the preview iframe but NOT in the top-level
|
||||
// timelineElements list — the intent must be self-contained.
|
||||
const iframe = makeIframeWith(`<div id="chip" style="z-index: 1"></div>`);
|
||||
const commit = vi.fn<(entries: unknown[]) => void>();
|
||||
|
||||
applyTimelineStackingReorder({
|
||||
element: el({ id: "chip", tag: "div" }),
|
||||
targetTrack: 0,
|
||||
stackingReorder: {
|
||||
contextKey: "scene",
|
||||
placement: { type: "above", layerId: "layer:scene:x" },
|
||||
zIndexChanges: [
|
||||
{
|
||||
key: "scenes/scene.html#chip",
|
||||
zIndex: 5,
|
||||
domId: "chip",
|
||||
sourceFile: "scenes/scene.html",
|
||||
},
|
||||
],
|
||||
},
|
||||
timelineElements: [], // element intentionally absent from the top-level list
|
||||
iframe,
|
||||
activeCompPath: "index.html",
|
||||
commit,
|
||||
});
|
||||
|
||||
expect(commit).toHaveBeenCalledTimes(1);
|
||||
const entries = commit.mock.calls[0]![0] as Array<{
|
||||
zIndex: number;
|
||||
id?: string;
|
||||
sourceFile: string;
|
||||
}>;
|
||||
expect(entries).toHaveLength(1);
|
||||
expect(entries[0]!.zIndex).toBe(5);
|
||||
expect(entries[0]!.id).toBe("chip");
|
||||
expect(entries[0]!.sourceFile).toBe("scenes/scene.html");
|
||||
});
|
||||
|
||||
it("never commits when the dragged clip is audio", () => {
|
||||
const iframe = makeIframeWith(`<audio id="track"></audio>`);
|
||||
const commit = vi.fn<(entries: unknown[]) => void>();
|
||||
|
||||
applyTimelineStackingReorder({
|
||||
element: el({ id: "track", tag: "audio" }),
|
||||
targetTrack: 0,
|
||||
stackingReorder: {
|
||||
contextKey: "main",
|
||||
placement: { type: "above", layerId: "layer:main:x" },
|
||||
zIndexChanges: [{ key: "track", zIndex: 5, domId: "track" }],
|
||||
},
|
||||
timelineElements: [],
|
||||
iframe,
|
||||
activeCompPath: "index.html",
|
||||
commit,
|
||||
});
|
||||
|
||||
expect(commit).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user