diff --git a/blocksuite/affine/block-list/src/commands/split-list.ts b/blocksuite/affine/block-list/src/commands/split-list.ts index 799d2d1ab..3ecd15d17 100644 --- a/blocksuite/affine/block-list/src/commands/split-list.ts +++ b/blocksuite/affine/block-list/src/commands/split-list.ts @@ -115,46 +115,92 @@ export const splitListCommand: Command<{ let newListId: string | null = null; if (model.children.length > 0 && !model.collapsed) { - /** - * case 3: list has children (list not collapsed) - * - * before: - * - aa|a <- split here - * - bbb - * - * after: - * - aa - * - |a - * - bbb - */ const afterText = model.text.split(inlineIndex); - newListId = doc.addBlock( - 'affine:list', - { - type: model.type, - text: afterText, - order: model.type === 'numbered' ? 1 : null, - }, - model, - 0 - ); - - if (model.type === 'numbered') { - const nextContinuousNumberedLists = getNextContinuousNumberedLists( - doc, - newListId + if (inlineIndex === 0) { + /** + * case 3: list has children (list not collapsed), split the list at the start of line + * + * before: + * - |aaa <- split here + * - bbb + * + * after: + * - + * - |aaa + * - bbb + */ + newListId = doc.addBlock( + 'affine:list', + { + type: model.type, + text: afterText, + order: + model.type === 'numbered' && model.order !== null + ? model.order + 1 + : null, + }, + parent, + modelIndex + 1 ); - let base = 2; - nextContinuousNumberedLists.forEach(list => { - doc.transact(() => { - list.order = base; + const newList = doc.getBlock(newListId)?.model; + if (!newList) return; + // move children to new list + doc.moveBlocks(model.children, newList); + + if (model.type === 'numbered' && model.order !== null) { + const nextContinuousNumberedLists = getNextContinuousNumberedLists( + doc, + newListId + ); + let base = model.order + 2; + nextContinuousNumberedLists.forEach(list => { + doc.transact(() => { + list.order = base; + }); + base += 1; }); - base += 1; - }); + } + } else { + /** + * case 4: list has children (list not collapsed), split the list not at the start of line + * + * before: + * - aa|a <- split here + * - bbb + * + * after: + * - aa + * - |a + * - bbb + */ + newListId = doc.addBlock( + 'affine:list', + { + type: model.type, + text: afterText, + order: model.type === 'numbered' ? 1 : null, + }, + model, + 0 + ); + + if (model.type === 'numbered') { + const nextContinuousNumberedLists = getNextContinuousNumberedLists( + doc, + newListId + ); + let base = 2; + nextContinuousNumberedLists.forEach(list => { + doc.transact(() => { + list.order = base; + }); + base += 1; + }); + } } } else { /** - * case 4: list has children (list collapsed) + * case 5: list has children (list collapsed) * * before: * - aa|a <- split here @@ -166,7 +212,7 @@ export const splitListCommand: Command<{ * - |a * * - * case 5: list does not have children + * case 6: list does not have children * * before: * - aa|a <- split here diff --git a/tests/affine-local/e2e/blocksuite/list.spec.ts b/tests/affine-local/e2e/blocksuite/list.spec.ts new file mode 100644 index 000000000..d639a6dc2 --- /dev/null +++ b/tests/affine-local/e2e/blocksuite/list.spec.ts @@ -0,0 +1,80 @@ +import { test } from '@affine-test/kit/playwright'; +import { + pressArrowUp, + pressEnter, + pressTab, +} from '@affine-test/kit/utils/keyboard'; +import { openHomePage } from '@affine-test/kit/utils/load-page'; +import { + clickNewPageButton, + type, + waitForEditorLoad, +} from '@affine-test/kit/utils/page-logic'; +import { expect } from '@playwright/test'; + +test.beforeEach(async ({ page }) => { + await openHomePage(page); + await clickNewPageButton(page, 'List Test'); + await waitForEditorLoad(page); +}); + +test.describe('split list', () => { + test('split at the start of the list', async ({ page }) => { + await pressEnter(page); + await type(page, '1. aaa\nbbb\nccc\nddd'); + await pressArrowUp(page, 2); + await pressTab(page); + await pressArrowUp(page, 2); + + const listLocator = page.locator('affine-list'); + + /** + * 1. |aaa + * a. bbb + * 2. ccc + * 3. ddd + */ + await expect(listLocator.nth(0).locator('rich-text')).toHaveText([ + 'aaa', + 'bbb', + ]); + await expect( + listLocator.nth(0).locator('.affine-list-block__numbered') + ).toHaveText(['1.', 'a.']); + await expect(listLocator.nth(2).locator('rich-text')).toHaveText('ccc'); + await expect( + listLocator.nth(2).locator('.affine-list-block__numbered') + ).toHaveText('2.'); + await expect(listLocator.nth(3).locator('rich-text')).toHaveText('ddd'); + expect( + listLocator.nth(3).locator('.affine-list-block__numbered') + ).toHaveText('3.'); + + await pressEnter(page); + /** + * 1. + * 2. aaa + * a. bbb + * 3. ccc + * 4. ddd + */ + await expect( + listLocator.nth(0).locator('.affine-list-block__numbered') + ).toHaveText('1.'); + await expect(listLocator.nth(1).locator('rich-text')).toHaveText([ + 'aaa', + 'bbb', + ]); + await expect( + listLocator.nth(1).locator('.affine-list-block__numbered') + ).toHaveText(['2.', 'a.']); + await expect(listLocator.nth(3).locator('rich-text')).toHaveText('ccc'); + await expect( + listLocator.nth(3).locator('.affine-list-block__numbered') + ).toHaveText('3.'); + await expect(listLocator.nth(4).locator('rich-text')).toHaveText('ddd'); + await expect( + listLocator.nth(4).locator('.affine-list-block__numbered') + ).toHaveText('4.'); + }); +}); diff --git a/tests/kit/src/utils/keyboard.ts b/tests/kit/src/utils/keyboard.ts index cb472c2d2..c3436a38a 100644 --- a/tests/kit/src/utils/keyboard.ts +++ b/tests/kit/src/utils/keyboard.ts @@ -32,6 +32,12 @@ export async function pressEnter(page: Page, count = 1) { } } +export async function pressArrowUp(page: Page, count = 1) { + for (let i = 0; i < count; i++) { + await page.keyboard.press('ArrowUp', { delay: 50 }); + } +} + export async function pressTab(page: Page) { await page.keyboard.press('Tab', { delay: 50 }); }