Support explicit empty slot drop zones in shared editor
Quality / quality (ubuntu-latest) (push) Failing after 22s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
Clintchiz committed 2026-09-05 16:28:11 +05:30
1 parent 7110e1a7f1
commit 0f8d10eb91
7 files changed
+100 -11

No files matched your search

+29 -2
View File
@@ -1,7 +1,7 @@
import { expect, test } from "bun:test";
import { readFileSync } from "node:fs";
import { Window } from "happy-dom";
import type { DndManagerOptions, SortableRegistration } from "@wrnexus/dnd";
import type { DndManagerOptions, SortableRegistration, DropZoneRegistration } from "@wrnexus/dnd";
import type { MountedEditor, MountEditorOptions } from "../src/runtime.ts";
const code = new Bun.Transpiler({ loader: "ts" })
@@ -19,13 +19,18 @@ function fixture(onMove: MountEditorOptions["onMove"] = () => false) {
const backends: Array<{
options: DndManagerOptions;
registrations: SortableRegistration[];
zones: DropZoneRegistration[];
destroyed: number;
}> = [];
const mount = new Function("createDndManager", code + ";return mountBlockEditor;")(
(options: DndManagerOptions) => {
const backend = { options, registrations: [] as SortableRegistration[], destroyed: 0 };
const backend = { options, registrations: [] as SortableRegistration[], zones: [] as DropZoneRegistration[], destroyed: 0 };
backends.push(backend);
return {
registerDropZone: (registration: DropZoneRegistration) => {
backend.zones.push(registration);
return () => {};
},
registerSortable: (registration: SortableRegistration) => {
backend.registrations.push(registration);
return () => {};
@@ -66,6 +71,28 @@ test("cancelled optimistic moves restore original DOM nodes across containers",
f.editor.destroy();
});
test("empty named slot targets produce validated editor move intents and refresh away when populated", async () => {
const moves: unknown[] = [];
const f = fixture(intent => { moves.push(intent); return true; });
const target = f.document.createElement("div");
target.dataset.blockParent = "parent:empty";
target.dataset.blockSlotTarget = "content:empty";
f.root.append(target);
f.editor.refresh();
const backend = f.backends.at(-1)!;
expect(backend.zones).toHaveLength(1);
const zone = backend.zones[0]!;
expect(backend.options.accept!(["a"], { group: zone.group, index: 0 })).toBe(true);
await backend.options.onMoveIntent({ ids: ["a"], from: [], to: { group: zone.group, index: 0 } });
expect(moves).toEqual([{ nodeIds: ["a"], to: { parentId: "parent:empty", slot: "content:empty", index: 0 } }]);
const child = f.root.querySelector('[data-block-id="a"]')! as any;
child.dataset.blockParent = "parent:empty"; child.dataset.blockSlot = "content:empty";
target.append(child);
f.editor.refresh();
expect(f.backends.at(-1)!.zones).toHaveLength(0);
f.editor.destroy();
});
test("slot and parent identifiers containing colons round-trip without collisions", async () => {
const moves: unknown[] = [];
const f = fixture((intent) => {