Skip to content

BNSplitBlock fix #72

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Feb 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 39 additions & 15 deletions packages/core/src/extensions/Blocks/nodes/BlockContainer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { mergeAttributes, Node } from "@tiptap/core";
import { Slice } from "prosemirror-model";
import { Fragment, Slice } from "prosemirror-model";
import { TextSelection } from "prosemirror-state";
import { BlockUpdate } from "../apiTypes";
import { getBlockInfoFromPos } from "../helpers/getBlockInfoFromPos";
Expand Down Expand Up @@ -222,20 +222,36 @@ export const BlockContainer = Node.create<IBlock>({
const { contentNode, contentType, startPos, endPos, depth } =
blockInfo;

const newBlockInsertionPos = endPos + 1;

// Creates new block first, otherwise positions get changed due to the original block's content changing.
// Only text content is transferred to the new block.
const secondBlockContent = state.doc.textBetween(posInBlock, endPos);
const originalBlockContent = state.doc.cut(startPos + 1, posInBlock);
const newBlockContent = state.doc.cut(posInBlock, endPos - 1);

const newBlock =
state.schema.nodes["blockContainer"].createAndFill()!;

const newBlockInsertionPos = endPos + 1;
const newBlockContentPos = newBlockInsertionPos + 2;

if (dispatch) {
// Creates a new block. Since the schema requires it to have a content node, a paragraph node is created
// automatically, spanning newBlockContentPos to newBlockContentPos + 1.
state.tr.insert(newBlockInsertionPos, newBlock);
state.tr.insertText(secondBlockContent, newBlockContentPos);

// Replaces the content of the newly created block's content node. Doesn't replace the whole content node so
// its type doesn't change.
state.tr.replace(
newBlockContentPos,
newBlockContentPos + 1,
newBlockContent.content.size > 0
? new Slice(
Fragment.from(newBlockContent),
depth + 2,
depth + 2
)
: undefined
);

// Changes the type of the content node. The range doesn't matter as long as both from and to positions are
// within the content node.
if (keepType) {
state.tr.setBlockType(
newBlockContentPos,
Expand All @@ -244,22 +260,30 @@ export const BlockContainer = Node.create<IBlock>({
contentNode.attrs
);
}
}

// Updates content of original block.
const firstBlockContent = state.doc.content.cut(startPos, posInBlock);
// Sets the selection to the start of the new block's content node.
state.tr.setSelection(
new TextSelection(state.doc.resolve(newBlockContentPos))
);

if (dispatch) {
// Replaces the content of the original block's content node. Doesn't replace the whole content node so its
// type doesn't change.
state.tr.replace(
startPos,
endPos,
new Slice(firstBlockContent, depth, depth)
startPos + 1,
endPos - 1,
originalBlockContent.content.size > 0
? new Slice(
Fragment.from(originalBlockContent),
depth + 2,
depth + 2
)
: undefined
);
}

return true;
},
// Changes the content of a block at a given position to a given type.
// Updates the type and attributes of a block at a given position.
BNUpdateBlock:
(posInBlock, blockUpdate) =>
({ state, dispatch }) => {
Expand Down
36 changes: 36 additions & 0 deletions tests/end-to-end/keyboardhandlers/keyboardhandlers.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { test } from "../../setup/setupScript";
import {
BASE_URL,
ITALIC_BUTTON_SELECTOR,
H_ONE_BLOCK_SELECTOR,
H_TWO_BLOCK_SELECTOR,
} from "../../utils/const";
Expand Down Expand Up @@ -34,4 +35,39 @@ test.describe("Check Keyboard Handlers' Behaviour", () => {

await compareDocToSnapshot(page, "enterSelectionNotEmpty.json");
});
test("Check Enter preserves marks", async ({ page }) => {
await focusOnEditor(page);
await insertHeading(page, 1);

const element = await page.locator(H_ONE_BLOCK_SELECTOR);
let boundingBox = await element.boundingBox();
let { x, y, height } = boundingBox;

await page.mouse.click(x + 35, y + height / 2, { clickCount: 2 });
await page.locator(ITALIC_BUTTON_SELECTOR).click();
await page.waitForTimeout(600);
await page.mouse.click(x + 35, y + height / 2);
await page.keyboard.press("Enter");

await page.pause();

await compareDocToSnapshot(page, "enterPreservesMarks.json");
});
test("Check Enter preserves nested blocks", async ({ page }) => {
await focusOnEditor(page);
await insertHeading(page, 1);
await page.keyboard.press("Tab");
await insertHeading(page, 2);
await page.keyboard.press("Tab");
await insertHeading(page, 3);

const element = await page.locator(H_ONE_BLOCK_SELECTOR);
let boundingBox = await element.boundingBox();
let { x, y, height } = boundingBox;

await page.mouse.click(x + 35, y + height / 2);
await page.keyboard.press("Enter");

await compareDocToSnapshot(page, "enterPreservesNestedBlocks.json");
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"type": "doc",
"content": [
{
"type": "blockGroup",
"content": [
{
"type": "blockContainer",
"attrs": {
"id": 0,
"textColor": "default",
"backgroundColor": "default"
},
"content": [
{
"type": "heading",
"attrs": {
"textAlignment": "left",
"level": "1"
},
"content": [
{
"type": "text",
"marks": [
{
"type": "italic"
}
],
"text": "H"
}
]
}
]
},
{
"type": "blockContainer",
"attrs": {
"id": 2,
"textColor": "default",
"backgroundColor": "default"
},
"content": [
{
"type": "paragraph",
"attrs": {
"textAlignment": "left"
},
"content": [
{
"type": "text",
"marks": [
{
"type": "italic"
}
],
"text": "eading"
}
]
}
]
},
{
"type": "blockContainer",
"attrs": {
"id": 1,
"textColor": "default",
"backgroundColor": "default"
},
"content": [
{
"type": "paragraph",
"attrs": {
"textAlignment": "left"
}
}
]
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"type": "doc",
"content": [
{
"type": "blockGroup",
"content": [
{
"type": "blockContainer",
"attrs": {
"id": 0,
"textColor": "default",
"backgroundColor": "default"
},
"content": [
{
"type": "heading",
"attrs": {
"textAlignment": "left",
"level": "1"
},
"content": [
{
"type": "text",
"marks": [
{
"type": "italic"
}
],
"text": "H"
}
]
}
]
},
{
"type": "blockContainer",
"attrs": {
"id": 2,
"textColor": "default",
"backgroundColor": "default"
},
"content": [
{
"type": "paragraph",
"attrs": {
"textAlignment": "left"
},
"content": [
{
"type": "text",
"marks": [
{
"type": "italic"
}
],
"text": "eading"
}
]
}
]
},
{
"type": "blockContainer",
"attrs": {
"id": 1,
"textColor": "default",
"backgroundColor": "default"
},
"content": [
{
"type": "paragraph",
"attrs": {
"textAlignment": "left"
}
}
]
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"type": "doc",
"content": [
{
"type": "blockGroup",
"content": [
{
"type": "blockContainer",
"attrs": {
"id": 0,
"textColor": "default",
"backgroundColor": "default"
},
"content": [
{
"type": "heading",
"attrs": {
"textAlignment": "left",
"level": "1"
},
"content": [
{
"type": "text",
"marks": [
{
"type": "italic"
}
],
"text": "H"
}
]
}
]
},
{
"type": "blockContainer",
"attrs": {
"id": 2,
"textColor": "default",
"backgroundColor": "default"
},
"content": [
{
"type": "paragraph",
"attrs": {
"textAlignment": "left"
},
"content": [
{
"type": "text",
"marks": [
{
"type": "italic"
}
],
"text": "eading"
}
]
}
]
},
{
"type": "blockContainer",
"attrs": {
"id": 1,
"textColor": "default",
"backgroundColor": "default"
},
"content": [
{
"type": "paragraph",
"attrs": {
"textAlignment": "left"
}
}
]
}
]
}
]
}
Loading