Skip to content

Commit

Permalink
Merge branch 'internal-html-changes' of github.com:TypeCellOS/BlockNo…
Browse files Browse the repository at this point in the history
…te into internal-html-changes
  • Loading branch information
YousefED committed Oct 3, 2024
2 parents afadc58 + 4d545ef commit 3769b71
Show file tree
Hide file tree
Showing 59 changed files with 417 additions and 1,134 deletions.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<img src="https://ralfvanveen.com/wp-content/uploads/2021/06/Placeholder-_-Glossary.svg" alt="BlockNote image" width="512" data-url="https://ralfvanveen.com/wp-content/uploads/2021/06/Placeholder-_-Glossary.svg">
<img src="https://ralfvanveen.com/wp-content/uploads/2021/06/Placeholder-_-Glossary.svg" alt="BlockNote image" width="512" data-url="https://ralfvanveen.com/wp-content/uploads/2021/06/Placeholder-_-Glossary.svg"><p>Nested Paragraph</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<strong>Bold</strong><em>Italic</em>Regular
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<em>Italic</em>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div class="bn-block-content" data-content-type="table"><table class="bn-inline-content"><tbody><tr><td colspan="1" rowspan="1"><p>Table Cell</p></td><td colspan="1" rowspan="1"><p>Table Cell</p></td></tr><tr><td colspan="1" rowspan="1"><p>Table Cell</p></td><td colspan="1" rowspan="1"><p>Table Cell</p></td></tr></tbody></table></div>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<tr><td colspan="1" rowspan="1"><p>Table Cell</p></td></tr>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Table Cell
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<tr><td colspan="1" rowspan="1"><p>Table Cell</p></td><td colspan="1" rowspan="1"><p>Table Cell</p></td></tr>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Regular
302 changes: 302 additions & 0 deletions packages/core/src/api/clipboard/clipboard.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
import { Node } from "prosemirror-model";
import { NodeSelection, Selection, TextSelection } from "prosemirror-state";
import { CellSelection } from "prosemirror-tables";
import * as pmView from "prosemirror-view";
import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";

import { PartialBlock } from "../../blocks/defaultBlocks";
import { BlockNoteEditor } from "../../editor/BlockNoteEditor";
import { initializeESMDependencies } from "../../util/esmDependencies";
import { selectedFragmentToHTML } from "./toClipboard/copyExtension";

type SelectionTestCase = {
testName: string;
createSelection: (doc: Node) => Selection;
};

// These tests are meant to test the copying of user selections in the editor.
// The test cases used for the other HTML conversion tests are not suitable here
// as they are represented in the BlockNote API, whereas here we want to test
// ProseMirror/TipTap selections directly.
describe("Test ProseMirror selection clipboard HTML", () => {
const initialContent: PartialBlock[] = [
{
type: "heading",
props: {
level: 2,
textColor: "red",
},
content: "Heading 1",
children: [
{
type: "paragraph",
content: "Nested Paragraph 1",
},
{
type: "paragraph",
content: "Nested Paragraph 2",
},
{
type: "paragraph",
content: "Nested Paragraph 3",
},
],
},
{
type: "heading",
props: {
level: 2,
textColor: "red",
},
content: "Heading 2",
children: [
{
type: "paragraph",
content: "Nested Paragraph 1",
},
{
type: "paragraph",
content: "Nested Paragraph 2",
},
{
type: "paragraph",
content: "Nested Paragraph 3",
},
],
},
{
type: "heading",
props: {
level: 2,
textColor: "red",
},
content: [
{
type: "text",
text: "Bold",
styles: {
bold: true,
},
},
{
type: "text",
text: "Italic",
styles: {
italic: true,
},
},
{
type: "text",
text: "Regular",
styles: {},
},
],
children: [
{
type: "image",
props: {
url: "https://ralfvanveen.com/wp-content/uploads/2021/06/Placeholder-_-Glossary.svg",
},
children: [
{
type: "paragraph",
content: "Nested Paragraph",
},
],
},
],
},
{
type: "table",
content: {
type: "tableContent",
rows: [
{
cells: ["Table Cell", "Table Cell"],
},
{
cells: ["Table Cell", "Table Cell"],
},
],
},
// Not needed as selections starting in table cells will get snapped to
// the table boundaries.
// children: [
// {
// type: "table",
// content: {
// type: "tableContent",
// rows: [
// {
// cells: ["Table Cell", "Table Cell"],
// },
// {
// cells: ["Table Cell", "Table Cell"],
// },
// ],
// },
// },
// ],
},
];

let editor: BlockNoteEditor;
const div = document.createElement("div");

beforeEach(() => {
editor.replaceBlocks(editor.document, initialContent);
});

beforeAll(async () => {
(window as any).__TEST_OPTIONS = (window as any).__TEST_OPTIONS || {};

editor = BlockNoteEditor.create();
editor.mount(div);

await initializeESMDependencies();
});

afterAll(() => {
editor.mount(undefined);
editor._tiptapEditor.destroy();
editor = undefined as any;

delete (window as Window & { __TEST_OPTIONS?: any }).__TEST_OPTIONS;
});

// Sets the editor selection to the given start and end positions, then
// exports the selected content to HTML and compares it to a snapshot.
async function testSelection(testCase: SelectionTestCase) {
editor.dispatch(
editor._tiptapEditor.state.tr.setSelection(
testCase.createSelection(editor._tiptapEditor.view.state.doc)
)
);

if (
"node" in editor._tiptapEditor.view.state.selection &&
(editor._tiptapEditor.view.state.selection.node as Node).type.spec
.group === "blockContent"
) {
editor.dispatch(
editor._tiptapEditor.state.tr.setSelection(
new NodeSelection(
editor._tiptapEditor.view.state.doc.resolve(
editor._tiptapEditor.view.state.selection.from - 1
)
)
)
);
}

const { clipboardHTML, externalHTML } = await selectedFragmentToHTML(
editor._tiptapEditor.view,
editor
);

expect(externalHTML).toMatchFileSnapshot(
`./__snapshots__/${testCase.testName}.html`
);

const originalDocument = editor.document;
editor._tiptapEditor.state.tr.replaceSelection(
(pmView as any).__parseFromClipboard(
editor._tiptapEditor.view,
"text",
clipboardHTML,
false,
editor._tiptapEditor.view.state.selection.$from
)
);
const newDocument = editor.document;

expect(newDocument).toStrictEqual(originalDocument);
}

const testCases: SelectionTestCase[] = [
// TODO: Consider adding test cases for nested blocks & double nested blocks.
// Selection spans all of first heading's children.
{
testName: "multipleChildren",
createSelection: (doc) => TextSelection.create(doc, 16, 78),
},
// Selection spans from start of first heading to end of its first child.
{
testName: "childToParent",
createSelection: (doc) => TextSelection.create(doc, 3, 34),
},
// Selection spans from middle of first heading to the middle of its first
// child.
{
testName: "partialChildToParent",
createSelection: (doc) => TextSelection.create(doc, 6, 23),
},
// Selection spans from start of first heading's first child to end of
// second heading's content (does not include second heading's children).
{
testName: "childrenToNextParent",
createSelection: (doc) => TextSelection.create(doc, 16, 93),
},
// Selection spans from start of first heading's first child to end of
// second heading's last child.
{
testName: "childrenToNextParentsChildren",
createSelection: (doc) => TextSelection.create(doc, 16, 159),
},
// Selection spans "Regular" text inside third heading.
{
testName: "unstyledText",
createSelection: (doc) => TextSelection.create(doc, 175, 182),
},
// Selection spans "Italic" text inside third heading.
{
testName: "styledText",
createSelection: (doc) => TextSelection.create(doc, 169, 175),
},
// Selection spans third heading's content (does not include third heading's
// children).
{
testName: "multipleStyledText",
createSelection: (doc) => TextSelection.create(doc, 165, 182),
},
// Selection spans the image block content.
{
testName: "image",
createSelection: (doc) => NodeSelection.create(doc, 185),
},
// Selection spans from start of third heading to end of it's last
// descendant.
{
testName: "nestedImage",
createSelection: (doc) => TextSelection.create(doc, 165, 205),
},
// Selection spans text in first cell of the table.
{
testName: "tableCellText",
createSelection: (doc) => TextSelection.create(doc, 216, 226),
},
// Selection spans first cell of the table.
// TODO: External HTML is wrapped in unnecessary `tr` element.
{
testName: "tableCell",
createSelection: (doc) => CellSelection.create(doc, 214),
},
// Selection spans first row of the table.
{
testName: "tableRow",
createSelection: (doc) => CellSelection.create(doc, 214, 228),
},
// Selection spans all cells of the table.
// TODO: External HTML is wrapped in unnecessary `blockContent` element.
{
testName: "tableAllCells",
createSelection: (doc) => CellSelection.create(doc, 214, 258),
},
];

for (const testCase of testCases) {
it(`${testCase.testName}`, async () => {
await testSelection(testCase);
});
}
});
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Extension } from "@tiptap/core";
import { Plugin } from "prosemirror-state";

import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
import { BlockSchema, InlineContentSchema, StyleSchema } from "../../schema";
import type { BlockNoteEditor } from "../../../editor/BlockNoteEditor";
import { BlockSchema, InlineContentSchema, StyleSchema } from "../../../schema";
import { handleFileInsertion } from "./handleFileInsertion";
import { acceptedMIMETypes } from "./acceptedMIMETypes";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
import { PartialBlock } from "../../blocks/defaultBlocks";
import type { BlockNoteEditor } from "../../../editor/BlockNoteEditor";
import { PartialBlock } from "../../../blocks/defaultBlocks";
import {
BlockSchema,
FileBlockConfig,
InlineContentSchema,
StyleSchema,
} from "../../schema";
import { getBlockInfoFromPos } from "../getBlockInfoFromPos";
} from "../../../schema";
import { getBlockInfoFromPos } from "../../getBlockInfoFromPos";
import { acceptedMIMETypes } from "./acceptedMIMETypes";

function checkFileExtensionsMatch(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Extension } from "@tiptap/core";
import { Plugin } from "prosemirror-state";

import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
import { BlockSchema, InlineContentSchema, StyleSchema } from "../../schema";
import type { BlockNoteEditor } from "../../../editor/BlockNoteEditor";
import { BlockSchema, InlineContentSchema, StyleSchema } from "../../../schema";
import { nestedListsToBlockNoteStructure } from "../../parsers/html/util/nestedLists";
import { acceptedMIMETypes } from "./acceptedMIMETypes";
import { handleFileInsertion } from "./handleFileInsertion";
import { nestedListsToBlockNoteStructure } from "./html/util/nestedLists";

export const createPasteFromClipboardExtension = <
BSchema extends BlockSchema,
Expand Down
Loading

0 comments on commit 3769b71

Please sign in to comment.