✨(frontend) adapt export to quote block
We have a new block type, the quote block. We have to adapt the export to handle this new block type.
This commit is contained in:
@@ -11,6 +11,7 @@ and this project adheres to
|
||||
## Added
|
||||
|
||||
- 💄(frontend) add error pages #643
|
||||
- ✨(frontend) Custom block quote with export #646
|
||||
|
||||
## Changed
|
||||
|
||||
@@ -24,6 +25,7 @@ and this project adheres to
|
||||
- 🐛(backend) allow any type of extensions for media download #671
|
||||
- ♻️(frontend) improve table pdf rendering
|
||||
|
||||
|
||||
## [2.2.0] - 2025-02-10
|
||||
|
||||
## Added
|
||||
|
||||
@@ -197,4 +197,49 @@ test.describe('Doc Export', () => {
|
||||
|
||||
expect(pdfText).toContain('Hello World');
|
||||
});
|
||||
|
||||
test('it exports the doc with quotes', async ({ page, browserName }) => {
|
||||
const [randomDoc] = await createDoc(page, 'export-quotes', browserName, 1);
|
||||
|
||||
const downloadPromise = page.waitForEvent('download', (download) => {
|
||||
return download.suggestedFilename().includes(`${randomDoc}.pdf`);
|
||||
});
|
||||
|
||||
const editor = page.locator('.ProseMirror');
|
||||
// Trigger slash menu to show menu
|
||||
await editor.click();
|
||||
await editor.fill('/');
|
||||
await page.getByText('Add a quote block').click();
|
||||
|
||||
await expect(
|
||||
editor.locator('.bn-block-content[data-content-type="quote"]'),
|
||||
).toBeVisible();
|
||||
|
||||
await editor.fill('Hello World');
|
||||
|
||||
await expect(editor.getByText('Hello World')).toHaveCSS(
|
||||
'font-style',
|
||||
'italic',
|
||||
);
|
||||
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'download',
|
||||
})
|
||||
.click();
|
||||
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Download',
|
||||
})
|
||||
.click();
|
||||
|
||||
const download = await downloadPromise;
|
||||
expect(download.suggestedFilename()).toBe(`${randomDoc}.pdf`);
|
||||
|
||||
const pdfBuffer = await cs.toBuffer(await download.createReadStream());
|
||||
const pdfData = await pdf(pdfBuffer);
|
||||
|
||||
expect(pdfData.text).toContain('Hello World'); // This is the pdf text
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './DocEditor';
|
||||
export * from './custom-blocks/';
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export * from './headingPDF';
|
||||
export * from './paragraphPDF';
|
||||
export * from './quoteDocx';
|
||||
export * from './quotePDF';
|
||||
export * from './tablePDF';
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Paragraph } from 'docx';
|
||||
|
||||
import { DocsExporterDocx } from '../types';
|
||||
import { docxBlockPropsToStyles } from '../utils';
|
||||
|
||||
export const blockMappingQuoteDocx: DocsExporterDocx['mappings']['blockMapping']['quote'] =
|
||||
(block, exporter) => {
|
||||
if (Array.isArray(block.content)) {
|
||||
block.content.forEach((content) => {
|
||||
if (content.type === 'text') {
|
||||
content.styles = {
|
||||
...content.styles,
|
||||
italic: true,
|
||||
textColor: 'gray',
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return new Paragraph({
|
||||
...docxBlockPropsToStyles(block.props, exporter.options.colors),
|
||||
spacing: { before: 10, after: 10 },
|
||||
border: {
|
||||
left: {
|
||||
color: '#cecece',
|
||||
space: 4,
|
||||
style: 'thick',
|
||||
},
|
||||
},
|
||||
style: 'Normal',
|
||||
children: exporter.transformInlineContent(block.content),
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Text } from '@react-pdf/renderer';
|
||||
|
||||
import { DocsExporterPDF } from '../types';
|
||||
|
||||
export const blockMappingQuotePDF: DocsExporterPDF['mappings']['blockMapping']['quote'] =
|
||||
(block, exporter) => {
|
||||
return (
|
||||
<Text
|
||||
style={{
|
||||
fontStyle: 'italic',
|
||||
marginVertical: 10,
|
||||
paddingVertical: 5,
|
||||
paddingLeft: 10,
|
||||
borderLeft: '4px solid #cecece',
|
||||
color: '#666',
|
||||
}}
|
||||
>
|
||||
{exporter.transformInlineContent(block.content)}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
@@ -1,10 +1,12 @@
|
||||
import { docxDefaultSchemaMappings } from '@blocknote/xl-docx-exporter';
|
||||
|
||||
import { blockMappingQuoteDocx } from './blocks-mapping/';
|
||||
import { DocsExporterDocx } from './types';
|
||||
|
||||
export const docxDocsSchemaMappings: DocsExporterDocx['mappings'] = {
|
||||
...docxDefaultSchemaMappings,
|
||||
blockMapping: {
|
||||
...docxDefaultSchemaMappings.blockMapping,
|
||||
quote: blockMappingQuoteDocx,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@ import { pdfDefaultSchemaMappings } from '@blocknote/xl-pdf-exporter';
|
||||
import {
|
||||
blockMappingHeadingPDF,
|
||||
blockMappingParagraphPDF,
|
||||
blockMappingQuotePDF,
|
||||
blockMappingTablePDF,
|
||||
} from './blocks-mapping';
|
||||
import { DocsExporterPDF } from './types';
|
||||
@@ -13,6 +14,7 @@ export const pdfDocsSchemaMappings: DocsExporterPDF['mappings'] = {
|
||||
...pdfDefaultSchemaMappings.blockMapping,
|
||||
heading: blockMappingHeadingPDF,
|
||||
paragraph: blockMappingParagraphPDF,
|
||||
quote: blockMappingQuotePDF,
|
||||
table: blockMappingTablePDF,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
import {
|
||||
COLORS_DEFAULT,
|
||||
DefaultProps,
|
||||
UnreachableCaseError,
|
||||
} from '@blocknote/core';
|
||||
import { IParagraphOptions, ShadingType } from 'docx';
|
||||
|
||||
export function downloadFile(blob: Blob, filename: string) {
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
@@ -30,3 +37,39 @@ export const exportResolveFileUrl = async (
|
||||
|
||||
return url;
|
||||
};
|
||||
|
||||
export function docxBlockPropsToStyles(
|
||||
props: Partial<DefaultProps>,
|
||||
colors: typeof COLORS_DEFAULT,
|
||||
): IParagraphOptions {
|
||||
return {
|
||||
shading:
|
||||
props.backgroundColor === 'default' || !props.backgroundColor
|
||||
? undefined
|
||||
: {
|
||||
type: ShadingType.SOLID,
|
||||
color:
|
||||
colors[
|
||||
props.backgroundColor as keyof typeof colors
|
||||
].background.slice(1),
|
||||
},
|
||||
run:
|
||||
props.textColor === 'default' || !props.textColor
|
||||
? undefined
|
||||
: {
|
||||
color: colors[props.textColor as keyof typeof colors].text.slice(1),
|
||||
},
|
||||
alignment:
|
||||
!props.textAlignment || props.textAlignment === 'left'
|
||||
? undefined
|
||||
: props.textAlignment === 'center'
|
||||
? 'center'
|
||||
: props.textAlignment === 'right'
|
||||
? 'right'
|
||||
: props.textAlignment === 'justify'
|
||||
? 'distribute'
|
||||
: (() => {
|
||||
throw new UnreachableCaseError(props.textAlignment);
|
||||
})(),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user