diff --git a/blocksuite/framework/store/src/__tests__/doc.unit.spec.ts b/blocksuite/framework/store/src/__tests__/doc.unit.spec.ts index 5ae37b21da..ef9a1e5e25 100644 --- a/blocksuite/framework/store/src/__tests__/doc.unit.spec.ts +++ b/blocksuite/framework/store/src/__tests__/doc.unit.spec.ts @@ -1,6 +1,7 @@ -import { expect, test, vi } from 'vitest'; +import { beforeEach, describe, expect, test, vi } from 'vitest'; import * as Y from 'yjs'; +import type { BlockModel, Store } from '../model/index.js'; import { Schema } from '../schema/index.js'; import { createAutoIncrementIdGenerator } from '../test/index.js'; import { TestWorkspace } from '../test/test-workspace.js'; @@ -267,3 +268,40 @@ test('local readonly', () => { expect(doc2?.readonly).toBeTruthy(); expect(doc3?.readonly).toBeFalsy(); }); + +describe('move blocks', () => { + type Context = { doc: Store; page: BlockModel; notes: BlockModel[] }; + beforeEach((context: Context) => { + const options = createTestOptions(); + const collection = new TestWorkspace(options); + collection.meta.initialize(); + + const doc = collection.createDoc({ id: 'home' }); + doc.load(); + const pageId = doc.addBlock('affine:page'); + const page = doc.getBlock(pageId)!.model; + + const noteIds = doc.addBlocks( + [1, 2, 3].map(i => ({ + flavour: 'affine:note', + blockProps: { id: `${i}` }, + })), + page + ); + const notes = noteIds.map(id => doc.getBlock(id)!.model); + + context.doc = doc; + context.page = page; + context.notes = notes; + }); + + test('move block to itself', ({ doc, page, notes }: Context) => { + const noteIds = notes.map(({ id }) => id); + + doc.moveBlocks([notes[0]], page, notes[0], true); + expect(page.children.map(({ id }) => id)).toEqual(noteIds); + + doc.moveBlocks([notes[0]], page, notes[0], false); + expect(page.children.map(({ id }) => id)).toEqual(noteIds); + }); +}); diff --git a/blocksuite/framework/store/src/model/store/crud.ts b/blocksuite/framework/store/src/model/store/crud.ts index a895ad0595..b4dc660937 100644 --- a/blocksuite/framework/store/src/model/store/crud.ts +++ b/blocksuite/framework/store/src/model/store/crud.ts @@ -264,6 +264,28 @@ export class DocCRUD { targetSibling: string | null = null, shouldInsertBeforeSibling = true ) { + if ( + blocksToMove.length > 1 && + targetSibling && + blocksToMove.includes(targetSibling) + ) { + console.error( + 'Cannot move blocks when the target sibling is in the blocks to move' + ); + return; + } + + if (blocksToMove.length === 1 && targetSibling === blocksToMove[0]) { + return; + } + + if (blocksToMove.includes(newParent)) { + console.error( + 'Cannot move blocks when the new parent is in the blocks to move' + ); + return; + } + // A map to store parent block and their respective child blocks const childBlocksPerParent = new Map();