Skip to content

Commit f5f9d8a

Browse files
committed
✨(frontend) interlinking export
Create interlinking link mapping for docx and pdf export.
1 parent e7709ba commit f5f9d8a

File tree

9 files changed

+134
-6
lines changed

9 files changed

+134
-6
lines changed

src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import cs from 'convert-stream';
55
import pdf from 'pdf-parse';
66

77
import { createDoc, verifyDocName } from './utils-common';
8+
import { createRootSubPage } from './utils-sub-pages';
89

910
test.beforeEach(async ({ page }) => {
1011
await page.goto('/');
@@ -411,4 +412,72 @@ test.describe('Doc Export', () => {
411412
expect(pdfData.text).toContain('Column 2');
412413
expect(pdfData.text).toContain('Column 3');
413414
});
415+
416+
test('it exports the doc with interlinking', async ({
417+
page,
418+
browserName,
419+
}) => {
420+
const [randomDoc] = await createDoc(
421+
page,
422+
'export-interlinking',
423+
browserName,
424+
1,
425+
);
426+
427+
await verifyDocName(page, randomDoc);
428+
429+
const { name: docChild } = await createRootSubPage(
430+
page,
431+
browserName,
432+
'export-interlink-child',
433+
);
434+
435+
await verifyDocName(page, docChild);
436+
437+
await page.locator('.bn-block-outer').last().fill('/');
438+
await page.getByText('Link a doc').first().click();
439+
440+
await page
441+
.locator(
442+
"span[data-inline-content-type='interlinkingSearchInline'] input",
443+
)
444+
.fill('interlink-child');
445+
446+
await page
447+
.locator('.quick-search-container')
448+
.getByText('interlink-child')
449+
.click();
450+
451+
const interlink = page.getByRole('link', {
452+
name: 'interlink-child',
453+
});
454+
455+
await expect(interlink).toBeVisible();
456+
457+
const downloadPromise = page.waitForEvent('download', (download) => {
458+
return download.suggestedFilename().includes(`${docChild}.pdf`);
459+
});
460+
461+
await page
462+
.getByRole('button', {
463+
name: 'download',
464+
exact: true,
465+
})
466+
.click();
467+
468+
void page
469+
.getByRole('button', {
470+
name: 'Download',
471+
exact: true,
472+
})
473+
.click();
474+
475+
const download = await downloadPromise;
476+
expect(download.suggestedFilename()).toBe(`${docChild}.pdf`);
477+
478+
const pdfBuffer = await cs.toBuffer(await download.createReadStream());
479+
const pdfData = await pdf(pdfBuffer);
480+
481+
expect(pdfData.text).toContain('interlink-child'); // This is the pdf text
482+
});
414483
});
338 Bytes
Loading

src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/paragraphPDF.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const blockMappingParagraphPDF: DocsExporterPDF['mappings']['blockMapping
1010
*/
1111
if (Array.isArray(block.content)) {
1212
block.content.forEach((content) => {
13-
if (content.type === 'text' && !content.text) {
13+
if (content.type === 'text' && 'text' in content && !content.text) {
1414
content.text = ' ';
1515
}
1616
});

src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/quoteDocx.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,17 @@ export const blockMappingQuoteDocx: DocsExporterDocx['mappings']['blockMapping']
88
if (Array.isArray(block.content)) {
99
block.content.forEach((content) => {
1010
if (content.type === 'text') {
11-
content.styles = {
12-
...content.styles,
13-
italic: true,
14-
textColor: 'gray',
15-
};
11+
if (
12+
'styles' in content &&
13+
typeof content.styles === 'object' &&
14+
content.styles !== null
15+
) {
16+
content.styles = {
17+
...content.styles,
18+
italic: true,
19+
textColor: 'gray',
20+
};
21+
}
1622
}
1723
});
1824
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './interlinkingLinkPDF';
2+
export * from './interlinkingLinkDocx';
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { ExternalHyperlink, TextRun } from 'docx';
2+
3+
import { DocsExporterDocx } from '../types';
4+
5+
export const inlineContentMappingInterlinkingLinkDocx: DocsExporterDocx['mappings']['inlineContentMapping']['interlinkingLinkInline'] =
6+
(inline) => {
7+
return new ExternalHyperlink({
8+
children: [
9+
new TextRun({
10+
text: `📄${inline.props.title}`,
11+
bold: true,
12+
}),
13+
],
14+
link: window.location.origin + inline.props.url,
15+
});
16+
};
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/* eslint-disable jsx-a11y/alt-text */
2+
import { Image, Link, Text } from '@react-pdf/renderer';
3+
4+
import DocSelectedIcon from '../assets/doc-selected.png';
5+
import { DocsExporterPDF } from '../types';
6+
7+
export const inlineContentMappingInterlinkingLinkPDF: DocsExporterPDF['mappings']['inlineContentMapping']['interlinkingLinkInline'] =
8+
(inline) => {
9+
return (
10+
<Link
11+
src={window.location.origin + inline.props.url}
12+
style={{
13+
textDecoration: 'none',
14+
color: 'black',
15+
}}
16+
>
17+
{' '}
18+
<Image src={DocSelectedIcon.src} />{' '}
19+
<Text>{inline.props.title}</Text>{' '}
20+
</Link>
21+
);
22+
};

src/frontend/apps/impress/src/features/docs/doc-export/mappingDocx.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { docxDefaultSchemaMappings } from '@blocknote/xl-docx-exporter';
2+
import { Paragraph } from 'docx';
23

34
import {
45
blockMappingCalloutDocx,
56
blockMappingDividerDocx,
67
blockMappingImageDocx,
78
blockMappingQuoteDocx,
89
} from './blocks-mapping';
10+
import { inlineContentMappingInterlinkingLinkDocx } from './inline-content-mapping';
911
import { DocsExporterDocx } from './types';
1012

1113
export const docxDocsSchemaMappings: DocsExporterDocx['mappings'] = {
@@ -17,4 +19,9 @@ export const docxDocsSchemaMappings: DocsExporterDocx['mappings'] = {
1719
quote: blockMappingQuoteDocx,
1820
image: blockMappingImageDocx,
1921
},
22+
inlineContentMapping: {
23+
...docxDefaultSchemaMappings.inlineContentMapping,
24+
interlinkingSearchInline: () => new Paragraph(''),
25+
interlinkingLinkInline: inlineContentMappingInterlinkingLinkDocx,
26+
},
2027
};

src/frontend/apps/impress/src/features/docs/doc-export/mappingPDF.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
blockMappingQuotePDF,
1010
blockMappingTablePDF,
1111
} from './blocks-mapping';
12+
import { inlineContentMappingInterlinkingLinkPDF } from './inline-content-mapping';
1213
import { DocsExporterPDF } from './types';
1314

1415
export const pdfDocsSchemaMappings: DocsExporterPDF['mappings'] = {
@@ -23,4 +24,9 @@ export const pdfDocsSchemaMappings: DocsExporterPDF['mappings'] = {
2324
quote: blockMappingQuotePDF,
2425
table: blockMappingTablePDF,
2526
},
27+
inlineContentMapping: {
28+
...pdfDefaultSchemaMappings.inlineContentMapping,
29+
interlinkingSearchInline: () => <></>,
30+
interlinkingLinkInline: inlineContentMappingInterlinkingLinkPDF,
31+
},
2632
};

0 commit comments

Comments
 (0)