Skip to content

Commit 8b7cd7a

Browse files
TuYvTuYv
authored andcommitted
fix(export): keep WeChat pseudo-element styles aligned
1 parent 1392515 commit 8b7cd7a

2 files changed

Lines changed: 50 additions & 8 deletions

File tree

next/src/lib/export/__tests__/wechat.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,46 @@ describe("toWechatHtmlFromDocument", () => {
147147
}
148148
});
149149

150+
it("keeps descendant styles aligned when an ancestor has generated content", () => {
151+
document.body.innerHTML = `
152+
<div class="card"><span class="label">Hello</span></div>
153+
`;
154+
155+
const original = window.getComputedStyle.bind(window);
156+
const stub = ((el: Element, pseudo?: string | null) => {
157+
const base = original(el);
158+
const overrides: Record<string, string> = {};
159+
if (pseudo === "::before" && (el as HTMLElement).matches?.(".card")) {
160+
overrides.content = '"Badge"';
161+
} else if (!pseudo && (el as HTMLElement).matches?.(".label")) {
162+
overrides.color = "rgb(255, 0, 0)";
163+
}
164+
return new Proxy(base, {
165+
get(target, prop) {
166+
if (prop === "getPropertyValue") {
167+
return (name: string) => overrides[name] ?? target.getPropertyValue(name);
168+
}
169+
const value = (target as unknown as Record<string | symbol, unknown>)[prop];
170+
return typeof value === "function" ? (value as () => unknown).bind(target) : value;
171+
},
172+
});
173+
}) as typeof window.getComputedStyle;
174+
window.getComputedStyle = stub;
175+
176+
try {
177+
const body = parseFragment(toWechatHtmlFromDocument(document));
178+
const card = body.querySelector(".card");
179+
const pseudo = card?.querySelector("[data-pseudo='::before']");
180+
const label = card?.querySelector(".label");
181+
182+
expect(pseudo?.textContent).toBe("Badge");
183+
expect(pseudo?.getAttribute("style") ?? "").not.toContain("color: rgb(255, 0, 0)");
184+
expect(label?.getAttribute("style") ?? "").toContain("color: rgb(255, 0, 0)");
185+
} finally {
186+
window.getComputedStyle = original as typeof window.getComputedStyle;
187+
}
188+
});
189+
150190
it("clamps oversized spacing and drops negative margins", () => {
151191
document.head.innerHTML = `
152192
<style>

next/src/lib/export/wechat.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,16 @@ function inlineComputedTree(source: Element, clone: Element, view: Window): void
115115
if (styleText) clone.setAttribute("style", styleText);
116116
materializePseudos(source, clone, view);
117117

118-
const sourceEls = Array.from(source.querySelectorAll("*"));
119-
const cloneEls = Array.from(clone.querySelectorAll("*"));
120-
for (let i = 0; i < sourceEls.length; i++) {
121-
const cloneEl = cloneEls[i];
122-
if (!cloneEl) continue;
123-
const childStyle = computedStyleText(sourceEls[i], view);
124-
if (childStyle) cloneEl.setAttribute("style", childStyle);
125-
materializePseudos(sourceEls[i], cloneEl, view);
118+
// Pseudo-elements are materialized only in the clone, so global descendant
119+
// lists no longer have matching indexes. Recurse through real element
120+
// children in lockstep and ignore the generated pseudo spans when matching.
121+
const sourceChildren = Array.from(source.children);
122+
const cloneChildren = Array.from(clone.children).filter(
123+
(child) => !child.hasAttribute("data-pseudo"),
124+
);
125+
for (let i = 0; i < sourceChildren.length; i++) {
126+
const cloneChild = cloneChildren[i];
127+
if (cloneChild) inlineComputedTree(sourceChildren[i], cloneChild, view);
126128
}
127129
}
128130

0 commit comments

Comments
 (0)