✨(frontend) add pdf blocks to the editor
Added pdf block in the editor. Signed-off-by: dakshesh14 <65905942+dakshesh14@users.noreply.github.com>
This commit is contained in:
1
.github/workflows/impress.yml
vendored
1
.github/workflows/impress.yml
vendored
@@ -79,6 +79,7 @@ jobs:
|
||||
--check-filenames \
|
||||
--ignore-words-list "Dokument,afterAll,excpt,statics" \
|
||||
--skip "./git/" \
|
||||
--skip "**/*.pdf" \
|
||||
--skip "**/*.po" \
|
||||
--skip "**/*.pot" \
|
||||
--skip "**/*.json" \
|
||||
|
||||
@@ -7,6 +7,10 @@ and this project adheres to
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- ✨(frontend) add pdf block to the editor #1293
|
||||
|
||||
### Changed
|
||||
|
||||
- ♻️(frontend) replace Arial font-family with token font #1411
|
||||
|
||||
BIN
src/frontend/apps/e2e/__tests__/app-impress/assets/test-pdf.pdf
Normal file
BIN
src/frontend/apps/e2e/__tests__/app-impress/assets/test-pdf.pdf
Normal file
Binary file not shown.
@@ -840,4 +840,38 @@ test.describe('Doc Editor', () => {
|
||||
).toBeInViewport();
|
||||
await expect(editor.getByText('Hello Child 14')).not.toBeInViewport();
|
||||
});
|
||||
|
||||
test('it embeds PDF', async ({ page, browserName }) => {
|
||||
await createDoc(page, 'doc-toolbar', browserName, 1);
|
||||
|
||||
await openSuggestionMenu({ page });
|
||||
await page.getByText('Embed a PDF file').click();
|
||||
|
||||
const pdfBlock = page.locator('div[data-content-type="pdf"]').first();
|
||||
|
||||
await expect(pdfBlock).toBeVisible();
|
||||
|
||||
await page.getByText('Add PDF').click();
|
||||
const fileChooserPromise = page.waitForEvent('filechooser');
|
||||
await page.getByText('Upload file').click();
|
||||
const fileChooser = await fileChooserPromise;
|
||||
|
||||
console.log(path.join(__dirname, 'assets/test-pdf.pdf'));
|
||||
await fileChooser.setFiles(path.join(__dirname, 'assets/test-pdf.pdf'));
|
||||
|
||||
// Wait for the media-check to be processed
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
const pdfEmbed = page
|
||||
.locator('.--docs--editor-container embed.bn-visual-media')
|
||||
.first();
|
||||
|
||||
// Check src of pdf
|
||||
expect(await pdfEmbed.getAttribute('src')).toMatch(
|
||||
/http:\/\/localhost:8083\/media\/.*\/attachments\/.*.pdf/,
|
||||
);
|
||||
|
||||
await expect(pdfEmbed).toHaveAttribute('type', 'application/pdf');
|
||||
await expect(pdfEmbed).toHaveAttribute('role', 'presentation');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
AccessibleImageBlock,
|
||||
CalloutBlock,
|
||||
DividerBlock,
|
||||
PdfBlock,
|
||||
} from './custom-blocks';
|
||||
import {
|
||||
InterlinkingLinkInlineContent,
|
||||
@@ -54,6 +55,7 @@ const baseBlockNoteSchema = withPageBreak(
|
||||
callout: CalloutBlock,
|
||||
divider: DividerBlock,
|
||||
image: AccessibleImageBlock,
|
||||
pdf: PdfBlock,
|
||||
},
|
||||
inlineContentSpecs: {
|
||||
...defaultInlineContentSpecs,
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
import {
|
||||
getCalloutReactSlashMenuItems,
|
||||
getDividerReactSlashMenuItems,
|
||||
getPdfReactSlashMenuItems,
|
||||
} from './custom-blocks';
|
||||
import { useGetInterlinkingMenuItems } from './custom-inline-content';
|
||||
import XLMultiColumn from './xl-multi-column';
|
||||
@@ -32,7 +33,10 @@ export const BlockNoteSuggestionMenu = () => {
|
||||
DocsStyleSchema
|
||||
>();
|
||||
const { t } = useTranslation();
|
||||
const basicBlocksName = useDictionary().slash_menu.page_break.group;
|
||||
const dictionaryDate = useDictionary();
|
||||
const basicBlocksName = dictionaryDate.slash_menu.page_break.group;
|
||||
const fileBlocksName = dictionaryDate.slash_menu.file.group;
|
||||
|
||||
const getInterlinkingMenuItems = useGetInterlinkingMenuItems();
|
||||
|
||||
const getSlashMenuItems = useMemo(() => {
|
||||
@@ -56,11 +60,12 @@ export const BlockNoteSuggestionMenu = () => {
|
||||
getMultiColumnSlashMenuItems?.(editor) || [],
|
||||
getPageBreakReactSlashMenuItems(editor),
|
||||
getDividerReactSlashMenuItems(editor, t, basicBlocksName),
|
||||
getPdfReactSlashMenuItems(editor, t, fileBlocksName),
|
||||
),
|
||||
query,
|
||||
),
|
||||
);
|
||||
}, [basicBlocksName, editor, getInterlinkingMenuItems, t]);
|
||||
}, [basicBlocksName, editor, getInterlinkingMenuItems, t, fileBlocksName]);
|
||||
|
||||
return (
|
||||
<SuggestionMenuController
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { insertOrUpdateBlock } from '@blocknote/core';
|
||||
import {
|
||||
AddFileButton,
|
||||
ResizableFileBlockWrapper,
|
||||
createReactBlockSpec,
|
||||
} from '@blocknote/react';
|
||||
import { TFunction } from 'i18next';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { createGlobalStyle } from 'styled-components';
|
||||
|
||||
import { Box, Icon } from '@/components';
|
||||
|
||||
import { DocsBlockNoteEditor } from '../../types';
|
||||
|
||||
const PDFBlockStyle = createGlobalStyle`
|
||||
.bn-block-content[data-content-type="pdf"] {
|
||||
width: fit-content;
|
||||
}
|
||||
`;
|
||||
|
||||
type FileBlockEditor = Parameters<typeof AddFileButton>[0]['editor'];
|
||||
|
||||
export const PdfBlock = createReactBlockSpec(
|
||||
{
|
||||
type: 'pdf',
|
||||
content: 'none',
|
||||
propSchema: {
|
||||
name: { default: '' as const },
|
||||
url: { default: '' as const },
|
||||
caption: { default: '' as const },
|
||||
showPreview: { default: true },
|
||||
previewWidth: { default: undefined, type: 'number' },
|
||||
},
|
||||
isFileBlock: true,
|
||||
fileBlockAccept: ['application/pdf'],
|
||||
},
|
||||
{
|
||||
render: ({ editor, block, contentRef }) => {
|
||||
const { t } = useTranslation();
|
||||
const pdfUrl = block.props.url;
|
||||
|
||||
return (
|
||||
<Box ref={contentRef} className="bn-file-block-content-wrapper">
|
||||
<PDFBlockStyle />
|
||||
<ResizableFileBlockWrapper
|
||||
buttonIcon={<Icon iconName="upload" />}
|
||||
block={block}
|
||||
editor={editor as unknown as FileBlockEditor}
|
||||
buttonText={t('Add PDF')}
|
||||
>
|
||||
<Box
|
||||
className="bn-visual-media"
|
||||
role="presentation"
|
||||
as="embed"
|
||||
$width="100%"
|
||||
$height="450px"
|
||||
type="application/pdf"
|
||||
src={pdfUrl}
|
||||
contentEditable={false}
|
||||
draggable={false}
|
||||
onClick={() => editor.setTextCursorPosition(block)}
|
||||
/>
|
||||
</ResizableFileBlockWrapper>
|
||||
</Box>
|
||||
);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export const getPdfReactSlashMenuItems = (
|
||||
editor: DocsBlockNoteEditor,
|
||||
t: TFunction<'translation', undefined>,
|
||||
group: string,
|
||||
) => [
|
||||
{
|
||||
title: t('PDF'),
|
||||
onItemClick: () => {
|
||||
insertOrUpdateBlock(editor, { type: 'pdf' });
|
||||
},
|
||||
aliases: [t('pdf'), t('document'), t('embed'), t('file')],
|
||||
group,
|
||||
icon: <Icon iconName="picture_as_pdf" $size="18px" />,
|
||||
subtext: t('Embed a PDF file'),
|
||||
},
|
||||
];
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './AccessibleImageBlock';
|
||||
export * from './CalloutBlock';
|
||||
export * from './DividerBlock';
|
||||
export * from './PdfBlock';
|
||||
|
||||
@@ -16,6 +16,10 @@ export const docxDocsSchemaMappings: DocsExporterDocx['mappings'] = {
|
||||
...docxDefaultSchemaMappings.blockMapping,
|
||||
callout: blockMappingCalloutDocx,
|
||||
divider: blockMappingDividerDocx,
|
||||
// We're using the file block mapping for PDF blocks
|
||||
// The types don't match exactly but the implementation is compatible
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
pdf: docxDefaultSchemaMappings.blockMapping.file as any,
|
||||
quote: blockMappingQuoteDocx,
|
||||
image: blockMappingImageDocx,
|
||||
},
|
||||
|
||||
@@ -23,6 +23,10 @@ export const pdfDocsSchemaMappings: DocsExporterPDF['mappings'] = {
|
||||
divider: blockMappingDividerPDF,
|
||||
quote: blockMappingQuotePDF,
|
||||
table: blockMappingTablePDF,
|
||||
// We're using the file block mapping for PDF blocks
|
||||
// The types don't match exactly but the implementation is compatible
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
pdf: pdfDefaultSchemaMappings.blockMapping.file as any,
|
||||
},
|
||||
inlineContentMapping: {
|
||||
...pdfDefaultSchemaMappings.inlineContentMapping,
|
||||
|
||||
Reference in New Issue
Block a user