diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts
index b70ebd01..f66aecf8 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts
@@ -5,6 +5,7 @@ import cs from 'convert-stream';
import pdf from 'pdf-parse';
import { createDoc, verifyDocName } from './utils-common';
+import { createRootSubPage } from './utils-sub-pages';
test.beforeEach(async ({ page }) => {
await page.goto('/');
@@ -411,4 +412,72 @@ test.describe('Doc Export', () => {
expect(pdfData.text).toContain('Column 2');
expect(pdfData.text).toContain('Column 3');
});
+
+ test('it exports the doc with interlinking', async ({
+ page,
+ browserName,
+ }) => {
+ const [randomDoc] = await createDoc(
+ page,
+ 'export-interlinking',
+ browserName,
+ 1,
+ );
+
+ await verifyDocName(page, randomDoc);
+
+ const { name: docChild } = await createRootSubPage(
+ page,
+ browserName,
+ 'export-interlink-child',
+ );
+
+ await verifyDocName(page, docChild);
+
+ await page.locator('.bn-block-outer').last().fill('/');
+ await page.getByText('Link a doc').first().click();
+
+ await page
+ .locator(
+ "span[data-inline-content-type='interlinkingSearchInline'] input",
+ )
+ .fill('interlink-child');
+
+ await page
+ .locator('.quick-search-container')
+ .getByText('interlink-child')
+ .click();
+
+ const interlink = page.getByRole('link', {
+ name: 'interlink-child',
+ });
+
+ await expect(interlink).toBeVisible();
+
+ const downloadPromise = page.waitForEvent('download', (download) => {
+ return download.suggestedFilename().includes(`${docChild}.pdf`);
+ });
+
+ await page
+ .getByRole('button', {
+ name: 'download',
+ exact: true,
+ })
+ .click();
+
+ void page
+ .getByRole('button', {
+ name: 'Download',
+ exact: true,
+ })
+ .click();
+
+ const download = await downloadPromise;
+ expect(download.suggestedFilename()).toBe(`${docChild}.pdf`);
+
+ const pdfBuffer = await cs.toBuffer(await download.createReadStream());
+ const pdfData = await pdf(pdfBuffer);
+
+ expect(pdfData.text).toContain('interlink-child'); // This is the pdf text
+ });
});
diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/assets/doc-selected.png b/src/frontend/apps/impress/src/features/docs/doc-export/assets/doc-selected.png
new file mode 100644
index 00000000..d4375fb4
Binary files /dev/null and b/src/frontend/apps/impress/src/features/docs/doc-export/assets/doc-selected.png differ
diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/paragraphPDF.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/paragraphPDF.tsx
index 19c253b7..89ebb5ea 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/paragraphPDF.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/paragraphPDF.tsx
@@ -10,7 +10,7 @@ export const blockMappingParagraphPDF: DocsExporterPDF['mappings']['blockMapping
*/
if (Array.isArray(block.content)) {
block.content.forEach((content) => {
- if (content.type === 'text' && !content.text) {
+ if (content.type === 'text' && 'text' in content && !content.text) {
content.text = ' ';
}
});
diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/quoteDocx.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/quoteDocx.tsx
index bcdaa68c..1691386f 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/quoteDocx.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/quoteDocx.tsx
@@ -8,11 +8,17 @@ export const blockMappingQuoteDocx: DocsExporterDocx['mappings']['blockMapping']
if (Array.isArray(block.content)) {
block.content.forEach((content) => {
if (content.type === 'text') {
- content.styles = {
- ...content.styles,
- italic: true,
- textColor: 'gray',
- };
+ if (
+ 'styles' in content &&
+ typeof content.styles === 'object' &&
+ content.styles !== null
+ ) {
+ content.styles = {
+ ...content.styles,
+ italic: true,
+ textColor: 'gray',
+ };
+ }
}
});
}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/inline-content-mapping/index.ts b/src/frontend/apps/impress/src/features/docs/doc-export/inline-content-mapping/index.ts
new file mode 100644
index 00000000..0b037c17
--- /dev/null
+++ b/src/frontend/apps/impress/src/features/docs/doc-export/inline-content-mapping/index.ts
@@ -0,0 +1,2 @@
+export * from './interlinkingLinkPDF';
+export * from './interlinkingLinkDocx';
diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/inline-content-mapping/interlinkingLinkDocx.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/inline-content-mapping/interlinkingLinkDocx.tsx
new file mode 100644
index 00000000..afb8c0e7
--- /dev/null
+++ b/src/frontend/apps/impress/src/features/docs/doc-export/inline-content-mapping/interlinkingLinkDocx.tsx
@@ -0,0 +1,16 @@
+import { ExternalHyperlink, TextRun } from 'docx';
+
+import { DocsExporterDocx } from '../types';
+
+export const inlineContentMappingInterlinkingLinkDocx: DocsExporterDocx['mappings']['inlineContentMapping']['interlinkingLinkInline'] =
+ (inline) => {
+ return new ExternalHyperlink({
+ children: [
+ new TextRun({
+ text: `📄${inline.props.title}`,
+ bold: true,
+ }),
+ ],
+ link: window.location.origin + inline.props.url,
+ });
+ };
diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/inline-content-mapping/interlinkingLinkPDF.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/inline-content-mapping/interlinkingLinkPDF.tsx
new file mode 100644
index 00000000..15732722
--- /dev/null
+++ b/src/frontend/apps/impress/src/features/docs/doc-export/inline-content-mapping/interlinkingLinkPDF.tsx
@@ -0,0 +1,22 @@
+/* eslint-disable jsx-a11y/alt-text */
+import { Image, Link, Text } from '@react-pdf/renderer';
+
+import DocSelectedIcon from '../assets/doc-selected.png';
+import { DocsExporterPDF } from '../types';
+
+export const inlineContentMappingInterlinkingLinkPDF: DocsExporterPDF['mappings']['inlineContentMapping']['interlinkingLinkInline'] =
+ (inline) => {
+ return (
+
+ {' '}
+ {' '}
+ {inline.props.title}{' '}
+
+ );
+ };
diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/mappingDocx.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/mappingDocx.tsx
index 434daa99..46263b92 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-export/mappingDocx.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-export/mappingDocx.tsx
@@ -1,4 +1,5 @@
import { docxDefaultSchemaMappings } from '@blocknote/xl-docx-exporter';
+import { Paragraph } from 'docx';
import {
blockMappingCalloutDocx,
@@ -6,6 +7,7 @@ import {
blockMappingImageDocx,
blockMappingQuoteDocx,
} from './blocks-mapping';
+import { inlineContentMappingInterlinkingLinkDocx } from './inline-content-mapping';
import { DocsExporterDocx } from './types';
export const docxDocsSchemaMappings: DocsExporterDocx['mappings'] = {
@@ -17,4 +19,9 @@ export const docxDocsSchemaMappings: DocsExporterDocx['mappings'] = {
quote: blockMappingQuoteDocx,
image: blockMappingImageDocx,
},
+ inlineContentMapping: {
+ ...docxDefaultSchemaMappings.inlineContentMapping,
+ interlinkingSearchInline: () => new Paragraph(''),
+ interlinkingLinkInline: inlineContentMappingInterlinkingLinkDocx,
+ },
};
diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/mappingPDF.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/mappingPDF.tsx
index 4224045d..53cc9061 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-export/mappingPDF.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-export/mappingPDF.tsx
@@ -9,6 +9,7 @@ import {
blockMappingQuotePDF,
blockMappingTablePDF,
} from './blocks-mapping';
+import { inlineContentMappingInterlinkingLinkPDF } from './inline-content-mapping';
import { DocsExporterPDF } from './types';
export const pdfDocsSchemaMappings: DocsExporterPDF['mappings'] = {
@@ -23,4 +24,9 @@ export const pdfDocsSchemaMappings: DocsExporterPDF['mappings'] = {
quote: blockMappingQuotePDF,
table: blockMappingTablePDF,
},
+ inlineContentMapping: {
+ ...pdfDefaultSchemaMappings.inlineContentMapping,
+ interlinkingSearchInline: () => <>>,
+ interlinkingLinkInline: inlineContentMappingInterlinkingLinkPDF,
+ },
};