✨(frontend) interlinking export
Create interlinking link mapping for docx and pdf export.
This commit is contained in:
@@ -5,6 +5,7 @@ import cs from 'convert-stream';
|
|||||||
import pdf from 'pdf-parse';
|
import pdf from 'pdf-parse';
|
||||||
|
|
||||||
import { createDoc, verifyDocName } from './utils-common';
|
import { createDoc, verifyDocName } from './utils-common';
|
||||||
|
import { createRootSubPage } from './utils-sub-pages';
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await page.goto('/');
|
await page.goto('/');
|
||||||
@@ -411,4 +412,72 @@ test.describe('Doc Export', () => {
|
|||||||
expect(pdfData.text).toContain('Column 2');
|
expect(pdfData.text).toContain('Column 2');
|
||||||
expect(pdfData.text).toContain('Column 3');
|
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
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 338 B |
@@ -10,7 +10,7 @@ export const blockMappingParagraphPDF: DocsExporterPDF['mappings']['blockMapping
|
|||||||
*/
|
*/
|
||||||
if (Array.isArray(block.content)) {
|
if (Array.isArray(block.content)) {
|
||||||
block.content.forEach((content) => {
|
block.content.forEach((content) => {
|
||||||
if (content.type === 'text' && !content.text) {
|
if (content.type === 'text' && 'text' in content && !content.text) {
|
||||||
content.text = ' ';
|
content.text = ' ';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,11 +8,17 @@ export const blockMappingQuoteDocx: DocsExporterDocx['mappings']['blockMapping']
|
|||||||
if (Array.isArray(block.content)) {
|
if (Array.isArray(block.content)) {
|
||||||
block.content.forEach((content) => {
|
block.content.forEach((content) => {
|
||||||
if (content.type === 'text') {
|
if (content.type === 'text') {
|
||||||
content.styles = {
|
if (
|
||||||
...content.styles,
|
'styles' in content &&
|
||||||
italic: true,
|
typeof content.styles === 'object' &&
|
||||||
textColor: 'gray',
|
content.styles !== null
|
||||||
};
|
) {
|
||||||
|
content.styles = {
|
||||||
|
...content.styles,
|
||||||
|
italic: true,
|
||||||
|
textColor: 'gray',
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './interlinkingLinkPDF';
|
||||||
|
export * from './interlinkingLinkDocx';
|
||||||
@@ -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,
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -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 (
|
||||||
|
<Link
|
||||||
|
src={window.location.origin + inline.props.url}
|
||||||
|
style={{
|
||||||
|
textDecoration: 'none',
|
||||||
|
color: 'black',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{' '}
|
||||||
|
<Image src={DocSelectedIcon.src} />{' '}
|
||||||
|
<Text>{inline.props.title}</Text>{' '}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { docxDefaultSchemaMappings } from '@blocknote/xl-docx-exporter';
|
import { docxDefaultSchemaMappings } from '@blocknote/xl-docx-exporter';
|
||||||
|
import { Paragraph } from 'docx';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
blockMappingCalloutDocx,
|
blockMappingCalloutDocx,
|
||||||
@@ -6,6 +7,7 @@ import {
|
|||||||
blockMappingImageDocx,
|
blockMappingImageDocx,
|
||||||
blockMappingQuoteDocx,
|
blockMappingQuoteDocx,
|
||||||
} from './blocks-mapping';
|
} from './blocks-mapping';
|
||||||
|
import { inlineContentMappingInterlinkingLinkDocx } from './inline-content-mapping';
|
||||||
import { DocsExporterDocx } from './types';
|
import { DocsExporterDocx } from './types';
|
||||||
|
|
||||||
export const docxDocsSchemaMappings: DocsExporterDocx['mappings'] = {
|
export const docxDocsSchemaMappings: DocsExporterDocx['mappings'] = {
|
||||||
@@ -17,4 +19,9 @@ export const docxDocsSchemaMappings: DocsExporterDocx['mappings'] = {
|
|||||||
quote: blockMappingQuoteDocx,
|
quote: blockMappingQuoteDocx,
|
||||||
image: blockMappingImageDocx,
|
image: blockMappingImageDocx,
|
||||||
},
|
},
|
||||||
|
inlineContentMapping: {
|
||||||
|
...docxDefaultSchemaMappings.inlineContentMapping,
|
||||||
|
interlinkingSearchInline: () => new Paragraph(''),
|
||||||
|
interlinkingLinkInline: inlineContentMappingInterlinkingLinkDocx,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
blockMappingQuotePDF,
|
blockMappingQuotePDF,
|
||||||
blockMappingTablePDF,
|
blockMappingTablePDF,
|
||||||
} from './blocks-mapping';
|
} from './blocks-mapping';
|
||||||
|
import { inlineContentMappingInterlinkingLinkPDF } from './inline-content-mapping';
|
||||||
import { DocsExporterPDF } from './types';
|
import { DocsExporterPDF } from './types';
|
||||||
|
|
||||||
export const pdfDocsSchemaMappings: DocsExporterPDF['mappings'] = {
|
export const pdfDocsSchemaMappings: DocsExporterPDF['mappings'] = {
|
||||||
@@ -23,4 +24,9 @@ export const pdfDocsSchemaMappings: DocsExporterPDF['mappings'] = {
|
|||||||
quote: blockMappingQuotePDF,
|
quote: blockMappingQuotePDF,
|
||||||
table: blockMappingTablePDF,
|
table: blockMappingTablePDF,
|
||||||
},
|
},
|
||||||
|
inlineContentMapping: {
|
||||||
|
...pdfDefaultSchemaMappings.inlineContentMapping,
|
||||||
|
interlinkingSearchInline: () => <></>,
|
||||||
|
interlinkingLinkInline: inlineContentMappingInterlinkingLinkPDF,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user