diff --git a/packages/core/src/api/parsers/html/util/__snapshots__/nestedLists.test.ts.snap b/packages/core/src/api/parsers/html/util/__snapshots__/nestedLists.test.ts.snap index 1db488255b..c35cb364a3 100644 --- a/packages/core/src/api/parsers/html/util/__snapshots__/nestedLists.test.ts.snap +++ b/packages/core/src/api/parsers/html/util/__snapshots__/nestedLists.test.ts.snap @@ -1,5 +1,9 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`Lift nested lists > Does not merge
before
after
"`; + +exports[`Lift nested lists > Wraps consecutive barebefore
after
`; + await testHTML(html); + }); + + it("LeavesPasted
`); + + const blocks = editor.document; + expect(blocks[0].type).toBe(type); + expect(blocks[0].content).toEqual([ + { type: "text", text: "Pasted", styles: {} }, + ]); + }, + ); + + it("inserts paragraph content into an empty list item without dropping marks", () => { + const editor = BlockNoteEditor.create({ + initialContent: [{ type: "bulletListItem", content: "" }], + }); + mountEditor(editor); + selectStartOfFirstBlock(editor); + + editor.pasteHTML(`Hello world
`); + + const blocks = editor.document; + expect(blocks[0].type).toBe("bulletListItem"); + expect(blocks[0].content).toEqual([ + { type: "text", text: "Hello ", styles: {} }, + { type: "text", text: "world", styles: { bold: true } }, + ]); + }); + + it("merges leading paragraph into empty list item and inserts rest as siblings", () => { + const editor = BlockNoteEditor.create({ + initialContent: [{ type: "bulletListItem", content: "" }], + }); + mountEditor(editor); + selectStartOfFirstBlock(editor); + + editor.pasteHTML(`First
Second
`); + + const blocks = editor.document; + expect(blocks[0].type).toBe("bulletListItem"); + expect(blocks[0].content).toEqual([ + { type: "text", text: "First", styles: {} }, + ]); + expect(blocks[1].type).toBe("paragraph"); + expect(blocks[1].content).toEqual([ + { type: "text", text: "Second", styles: {} }, + ]); + }); + + it("replaces an empty list item with a heading when pasting a heading", () => { + const editor = BlockNoteEditor.create({ + initialContent: [{ type: "bulletListItem", content: "" }], + }); + mountEditor(editor); + selectStartOfFirstBlock(editor); + + editor.pasteHTML(`Body
`); + + const blocks = editor.document; + expect(blocks[0].type).toBe("heading"); + expect(blocks[0].content).toEqual([ + { type: "text", text: "Heading", styles: {} }, + ]); + expect(blocks[1].type).toBe("paragraph"); + expect(blocks[1].content).toEqual([ + { type: "text", text: "Body", styles: {} }, + ]); + }); + + it("still replaces the empty list item when pasting another list item", () => { + const editor = BlockNoteEditor.create({ + initialContent: [{ type: "bulletListItem", content: "" }], + }); + mountEditor(editor); + selectStartOfFirstBlock(editor); + + editor.pasteHTML(`hello
`); + + const blocks = editor.document; + expect(blocks[0].type).toBe("bulletListItem"); + expect(blocks[0].content).toEqual([ + { type: "text", text: "abchello", styles: {} }, + ]); + }); + + it("pastes bareadded
`); + + const blocks = editor.document; + expect(blocks[0].type).toBe("paragraph"); + expect(blocks[0].content).toEqual([ + { type: "text", text: "Existing added", styles: {} }, + ]); + }); +}); diff --git a/packages/core/src/editor/transformPasted.ts b/packages/core/src/editor/transformPasted.ts index 2985ad33dc..4f0515df95 100644 --- a/packages/core/src/editor/transformPasted.ts +++ b/packages/core/src/editor/transformPasted.ts @@ -113,6 +113,11 @@ export function transformPasted(slice: Slice, view: EditorView) { let f = Fragment.from(slice.content); f = wrapTableRows(f, view.state.schema); + const retyped = retypeLeadingParagraphForEmptyTarget(f, view, slice); + if (retyped) { + return retyped; + } + if (isInTableCell(view)) { let hasTableContent = false; f.descendants((node) => { @@ -173,6 +178,78 @@ export function transformPasted(slice: Slice, view: EditorView) { return new Slice(f, slice.openStart, slice.openEnd); } +/** + * Pasting plain text into an empty inline-content block (e.g. an empty + * bullet list item) would normally replace that block with a paragraph: + * BlockNote's serializer always wraps content in + * `blockGroup > blockContainer >