UNPKG

@blocknote/core

Version:

A "Notion-style" block-based extensible text editor built on top of Prosemirror and Tiptap.

189 lines (153 loc) 5.47 kB
import { afterEach, describe, expect, it } from "vite-plus/test"; import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js"; import { BlockNoteSchema } from "../../blocks/BlockNoteSchema.js"; import { defaultBlockSpecs } from "../../blocks/defaultBlocks.js"; import { createParagraphBlockSpec } from "../../blocks/Paragraph/block.js"; import { createHeadingBlockSpec } from "../../blocks/Heading/block.js"; /** * @vitest-environment jsdom */ const editorsToCleanup: BlockNoteEditor<any, any, any>[] = []; afterEach(() => { for (const editor of editorsToCleanup) { editor.unmount(); } editorsToCleanup.length = 0; }); function withMetadata< T extends { config: { propSchema: Record<string, unknown> } }, >(spec: T): T { return { ...spec, config: { ...spec.config, propSchema: { ...spec.config.propSchema, metadata: { default: "" }, }, }, } as T; } function createExtendedSchema() { return BlockNoteSchema.create({ blockSpecs: Object.fromEntries( Object.entries(defaultBlockSpecs).map(([key, spec]) => [ key, withMetadata(spec), ]), ) as typeof defaultBlockSpecs, }); } function createExtendedEditor() { const editor = BlockNoteEditor.create({ schema: createExtendedSchema() }); editorsToCleanup.push(editor); return editor; } describe("extending default block specs with additional props", () => { it("creates an editor with an extended paragraph prop without crashing", () => { const paragraph = createParagraphBlockSpec(); const schema = BlockNoteSchema.create({ blockSpecs: { ...defaultBlockSpecs, paragraph: { ...paragraph, config: { ...paragraph.config, propSchema: { ...paragraph.config.propSchema, metadata: { default: "" }, }, }, }, }, }); const editor = BlockNoteEditor.create({ schema }); editorsToCleanup.push(editor); expect(editor.document[0].type).toBe("paragraph"); expect((editor.document[0].props as any).metadata).toBe(""); }); it("creates an editor using a helper that extends all block specs", () => { const editor = createExtendedEditor(); expect(editor.document[0].type).toBe("paragraph"); expect((editor.document[0].props as any).metadata).toBe(""); }); it("can update the extended prop on a block", () => { const editor = createExtendedEditor(); editor.updateBlock(editor.document[0], { props: { metadata: '{"source":"xwiki"}' } as any, }); expect((editor.document[0].props as any).metadata).toBe( '{"source":"xwiki"}', ); }); it("renders the extended prop in full HTML export", () => { const editor = createExtendedEditor(); editor.updateBlock(editor.document[0], { props: { metadata: "test-value" } as any, content: "Hello", }); const html = editor.blocksToFullHTML(editor.document); expect(html).toContain('data-metadata="test-value"'); }); it("does not render the extended prop when it equals the default", () => { const editor = createExtendedEditor(); editor.updateBlock(editor.document[0], { content: "Hello", }); const html = editor.blocksToFullHTML(editor.document); expect(html).not.toContain("data-metadata"); }); it("works when extending only specific block types", () => { const paragraph = createParagraphBlockSpec(); const heading = createHeadingBlockSpec(); const schema = BlockNoteSchema.create({ blockSpecs: { ...defaultBlockSpecs, paragraph: withMetadata(paragraph), heading: withMetadata(heading), }, }); const editor = BlockNoteEditor.create({ schema }); editorsToCleanup.push(editor); editor.replaceBlocks(editor.document, [ { type: "paragraph", content: "Text", props: { metadata: "p-meta" } as any, }, { type: "heading", content: "Title", props: { metadata: "h-meta", level: 1 } as any, }, { type: "bulletListItem", content: "Item" }, ]); expect((editor.document[0].props as any).metadata).toBe("p-meta"); expect((editor.document[1].props as any).metadata).toBe("h-meta"); expect(editor.document[2].props).not.toHaveProperty("metadata"); const html = editor.blocksToFullHTML(editor.document); expect(html).toContain('data-metadata="p-meta"'); expect(html).toContain('data-metadata="h-meta"'); }); it("round-trips extended props through full HTML export and parse", () => { const editor = createExtendedEditor(); editor.updateBlock(editor.document[0], { props: { metadata: '{"key":"value"}' } as any, content: "Round trip test", }); const html = editor.blocksToFullHTML(editor.document); const parsed = editor.tryParseHTMLToBlocks(html); expect((parsed[0].props as any).metadata).toBe('{"key":"value"}'); }); it("mounts the editor to a DOM element without crashing", () => { const editor = createExtendedEditor(); const container = document.createElement("div"); editor.mount(container); expect(container.innerHTML).toContain("bn-block-content"); editor.updateBlock(editor.document[0], { props: { metadata: "mounted-test" } as any, content: "Mounted", }); expect((editor.document[0].props as any).metadata).toBe("mounted-test"); }); });