import { CodeBlockPlugin, CodeLinePlugin } from "@platejs/code-block/react";
import { createSlateEditor, createSlatePlugin, KEYS } from "platejs";
import { describe, expect, it } from "vitest";
import { handleCodeBlockDeleteBackward } from "@/lib/markdown/code-block-delete";
const TestParagraphPlugin = createSlatePlugin({
key: KEYS.p,
node: { isElement: true },
});
function createCodeEditor(
value: Array<Record<string, unknown>>,
selection: { path: number[]; offset: number },
) {
const editor = createSlateEditor({
plugins: [
TestParagraphPlugin,
CodeBlockPlugin.overrideEditor(({ editor, tf: { deleteBackward } }) => ({
transforms: {
deleteBackward(unit) {
if (handleCodeBlockDeleteBackward(editor)) return;
deleteBackward(unit);
},
},
})),
CodeLinePlugin,
],
value,
});
editor.tf.select({
anchor: selection,
focus: selection,
});
return editor;
}
describe("code block deleteBackward (#178)", () => {
it("unwraps an empty code block instead of jumping to an earlier one", () => {
const editor = createCodeEditor(
[
{
type: KEYS.codeBlock,
children: [{ type: KEYS.codeLine, children: [{ text: "keep me" }] }],
},
{ type: KEYS.p, children: [{ text: "" }] },
{
type: KEYS.codeBlock,
children: [{ type: KEYS.codeLine, children: [{ text: "" }] }],
},
],
{ path: [2, 0, 0], offset: 0 },
);
editor.tf.deleteBackward("character");
expect(editor.children).toMatchObject([
{
type: KEYS.codeBlock,
children: [{ type: KEYS.codeLine, children: [{ text: "keep me" }] }],
},
{ type: KEYS.p, children: [{ text: "" }] },
{ type: KEYS.p, children: [{ text: "" }] },
]);
expect(editor.selection?.anchor.path[0]).toBe(2);
expect(editor.api.string([0])).toBe("keep me");
});
it("removes an empty trailing code line within the same block only", () => {
const editor = createCodeEditor(
[
{
type: KEYS.codeBlock,
children: [{ type: KEYS.codeLine, children: [{ text: "line one" }] }],
},
{
type: KEYS.codeBlock,
children: [
{ type: KEYS.codeLine, children: [{ text: "a" }] },
{ type: KEYS.codeLine, children: [{ text: "" }] },
],
},
],
{ path: [1, 1, 0], offset: 0 },
);
editor.tf.deleteBackward("character");
expect(editor.children).toMatchObject([
{
type: KEYS.codeBlock,
children: [{ type: KEYS.codeLine, children: [{ text: "line one" }] }],
},
{
type: KEYS.codeBlock,
children: [{ type: KEYS.codeLine, children: [{ text: "a" }] }],
},
]);
expect(editor.selection?.anchor).toMatchObject({
path: [1, 0, 0],
offset: 1,
});
});
it("does not leave a non-empty first line via backspace-at-start", () => {
const editor = createCodeEditor(
[
{
type: KEYS.codeBlock,
children: [{ type: KEYS.codeLine, children: [{ text: "hello" }] }],
},
],
{ path: [0, 0, 0], offset: 0 },
);
editor.tf.deleteBackward("character");
expect(editor.children).toMatchObject([
{
type: KEYS.codeBlock,
children: [{ type: KEYS.codeLine, children: [{ text: "hello" }] }],
},
]);
expect(editor.selection?.anchor).toMatchObject({
path: [0, 0, 0],
offset: 0,
});
});
});