From f3dc31c13d013b8befed0fc1543ea4fd29b8bc29 Mon Sep 17 00:00:00 2001 From: Nathan Panchout Date: Tue, 30 Jun 2026 16:04:21 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F(frontend)=20let=20print=20DO?= =?UTF-8?q?M=20helpers=20target=20a=20custom=20root?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wrapMediaWithLink / wrapInterlinksWithAnchor defaulted to document. Accept a root node so the presenter export can reuse them on its off-screen tree. --- .../doc-export/__tests__/utilsPrint.test.ts | 70 +++++++++++++++++++ .../features/docs/doc-export/utils_print.ts | 8 +-- 2 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 src/frontend/apps/impress/src/features/docs/doc-export/__tests__/utilsPrint.test.ts diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/__tests__/utilsPrint.test.ts b/src/frontend/apps/impress/src/features/docs/doc-export/__tests__/utilsPrint.test.ts new file mode 100644 index 000000000..7421e58af --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-export/__tests__/utilsPrint.test.ts @@ -0,0 +1,70 @@ +import { afterEach, describe, expect, test } from 'vitest'; + +import { wrapInterlinksWithAnchor, wrapMediaWithLink } from '../utils_print'; + +describe('print DOM helpers', () => { + afterEach(() => { + document.body.innerHTML = ''; + }); + + test('wraps media links only inside the provided root', () => { + document.body.innerHTML = ` +
+
+
+
+
+
+
+
+
+
+ `; + + const root = document.getElementById('root'); + if (!root) { + throw new Error('missing root'); + } + + const cleanup = wrapMediaWithLink(root); + + const inside = document.getElementById('inside'); + const outside = document.getElementById('outside'); + + expect(inside?.firstElementChild?.shadowRoot?.textContent).toContain( + 'inside.mp3', + ); + expect(outside?.firstElementChild?.shadowRoot).toBeNull(); + + cleanup(); + + expect(inside?.firstElementChild?.shadowRoot).toBeNull(); + }); + + test('wraps interlinks only inside the provided root', () => { + document.body.innerHTML = ` +
+ outside +
+ inside +
+
+ `; + + const root = document.getElementById('root'); + if (!root) { + throw new Error('missing root'); + } + + const cleanup = wrapInterlinksWithAnchor(root); + + expect(root.querySelector('a[data-print-link]')?.textContent).toBe( + 'inside', + ); + expect(document.querySelectorAll('a[data-print-link]')).toHaveLength(1); + + cleanup(); + + expect(document.querySelectorAll('a[data-print-link]')).toHaveLength(0); + }); +}); diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/utils_print.ts b/src/frontend/apps/impress/src/features/docs/doc-export/utils_print.ts index cdbd30bf1..168869215 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-export/utils_print.ts +++ b/src/frontend/apps/impress/src/features/docs/doc-export/utils_print.ts @@ -175,7 +175,7 @@ function appendPrintOnlyStyles() { /** * Wraps media elements with links to their source URLs for printing. */ -function wrapMediaWithLink() { +export function wrapMediaWithLink(root: ParentNode = document) { const createdShadowWrapper: HTMLElement[] = []; const prependLink = ( @@ -222,7 +222,7 @@ function wrapMediaWithLink() { createdShadowWrapper.push(shadowWrapper); }; - document + root .querySelectorAll( '[data-content-type="pdf"], [data-content-type="file"], [data-content-type="audio"], [data-content-type="video"]', ) @@ -248,14 +248,14 @@ function wrapMediaWithLink() { * Wraps interlink inline content with anchor tags for printing, * so they appear as clickable links in the printed PDF. */ -function wrapInterlinksWithAnchor() { +export function wrapInterlinksWithAnchor(root: ParentNode = document) { const wrappedElements: Array<{ el: Element; anchor: HTMLAnchorElement; parent: Node; }> = []; - document + root .querySelectorAll('.--docs--interlinking-link-inline-content[data-href]') .forEach((el) => { const href = el.getAttribute('data-href');