mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-04 07:19:52 +00:00
fix(#969): producer path propagates data-hf-authored-id to host element
When the producer inlines a sub-composition with compId match, it takes innerRoot.innerHTML which strips the wrapper div and its id attribute. CSS/GSAP selectors rewritten from #ID to [data-hf-authored-id=ID] then match nothing. Fix: after innerHTML injection, copy the inner root's id as data-hf-authored-id on the host element. This makes #ID selectors work identically in both preview (bundler) and render (producer) paths. Closes #969
This commit is contained in:
@@ -131,46 +131,24 @@ describe("inlineSubCompositions – #ID selector scoping divergence", () => {
|
||||
expect(scopedCss).toContain('[data-hf-authored-id="intro"]');
|
||||
});
|
||||
|
||||
/**
|
||||
* Known divergence: the producer path strips the inner root element via
|
||||
* innerHTML (line 307 of inlineSubCompositions.ts), losing the id attribute.
|
||||
* The bundler path preserves it via flattenInnerRoot + data-hf-authored-id.
|
||||
*
|
||||
* Both paths rewrite CSS and GSAP selectors from `#intro` to
|
||||
* `[data-hf-authored-id="intro"]`, but only the bundler path actually adds
|
||||
* that attribute to an element. In the producer path, no element carries
|
||||
* `data-hf-authored-id`, so the rewritten selectors match nothing.
|
||||
*
|
||||
* Workaround: use [data-composition-id="name"] as scope in sub-compositions
|
||||
* instead of #name.
|
||||
*
|
||||
* Proper fix (follow-up): make the producer path add data-hf-authored-id
|
||||
* to the host element when the inner root has an id attribute.
|
||||
*/
|
||||
// FIXME(#969): flip these assertions once the producer path adds
|
||||
// data-hf-authored-id to the host element. See PR #965 "Proper fix (follow-up)".
|
||||
it("documents the divergence: producer path lacks data-hf-authored-id element", () => {
|
||||
it("producer path propagates data-hf-authored-id to host when inner root has id", () => {
|
||||
const document = makeHostDocument("intro");
|
||||
const host = document.querySelector('[data-composition-src="intro.html"]')!;
|
||||
|
||||
// Producer path: no flattenInnerRoot
|
||||
inlineSubCompositions(document, [host], {
|
||||
resolveHtml: () => SUB_COMP_HTML,
|
||||
parseHtml: (html) => parseHTML(html).document,
|
||||
});
|
||||
|
||||
// After producer inlining, no element inside the host has
|
||||
// data-hf-authored-id="intro". The rewritten CSS/GSAP selectors
|
||||
// targeting [data-hf-authored-id="intro"] will match nothing.
|
||||
const authoredIdElement = host.querySelector('[data-hf-authored-id="intro"]');
|
||||
expect(authoredIdElement).toBeNull();
|
||||
// The inner root's id="intro" is stripped (innerHTML), but the producer
|
||||
// now propagates it as data-hf-authored-id on the host element so that
|
||||
// rewritten #ID selectors ([data-hf-authored-id="intro"]) resolve.
|
||||
expect(host.getAttribute("data-hf-authored-id")).toBe("intro");
|
||||
|
||||
// The original #intro element is gone — innerHTML stripped it.
|
||||
// The original #intro element is still gone — innerHTML stripped it.
|
||||
const introById = host.querySelector("#intro");
|
||||
expect(introById).toBeNull();
|
||||
|
||||
// Only the host itself has data-composition-id="intro", which is the
|
||||
// correct workaround scope to use.
|
||||
expect(host.getAttribute("data-composition-id")).toBe("intro");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -305,6 +305,12 @@ export function inlineSubCompositions(
|
||||
hostEl.innerHTML = prepared.outerHTML || "";
|
||||
} else {
|
||||
hostEl.innerHTML = compId ? innerRoot.innerHTML || "" : innerRoot.outerHTML || "";
|
||||
// When the producer path strips the inner root (innerHTML), the
|
||||
// authored id attribute is lost. Propagate it to the host so that
|
||||
// rewritten #ID selectors ([data-hf-authored-id="X"]) still resolve.
|
||||
if (compId && authoredRootId) {
|
||||
hostEl.setAttribute("data-hf-authored-id", authoredRootId);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const child of [...contentDoc.querySelectorAll("style, script")]) child.remove();
|
||||
|
||||
Reference in New Issue
Block a user