feat(editor): autofill turn-into-linked-doc modal with fisrt paragraph (#12032)

Close [BS-3288](https://linear.app/affine-design/issue/BS-3288/从note转换-linked-doc-时,默认把note的第一行填入doc拟定的标题)

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - When converting a note block to a linked document, the initial document title now uses the first paragraph in the note (if available) as a placeholder.
- **Tests**
  - Updated tests to verify that the linked document creation modal pre-fills the title with the note's first paragraph and checks for correct paragraph order.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
L-Sun
2025-04-30 04:55:04 +00:00
parent 9f4800ffba
commit 0d6c595adf
2 changed files with 28 additions and 11 deletions

View File

@@ -23,12 +23,16 @@ import {
ImageBlockModel, ImageBlockModel,
isExternalEmbedModel, isExternalEmbedModel,
NoteBlockModel, NoteBlockModel,
ParagraphBlockModel,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
import type { import type {
ToolbarActions, ToolbarActions,
ToolbarContext, ToolbarContext,
} from '@blocksuite/affine-shared/services'; } from '@blocksuite/affine-shared/services';
import { type ReorderingType } from '@blocksuite/affine-shared/utils'; import {
matchModels,
type ReorderingType,
} from '@blocksuite/affine-shared/utils';
import { Bound, getCommonBoundWithRotation } from '@blocksuite/global/gfx'; import { Bound, getCommonBoundWithRotation } from '@blocksuite/global/gfx';
import { import {
ArrowDownBigBottomIcon, ArrowDownBigBottomIcon,
@@ -219,8 +223,18 @@ export const moreActions = [
const model = ctx.getCurrentModelByType(NoteBlockModel); const model = ctx.getCurrentModelByType(NoteBlockModel);
if (!model) return; if (!model) return;
let placeholder = '';
for (const child of model.children) {
if (matchModels(child, [ParagraphBlockModel])) {
if (child.props.text.length === 0) continue;
placeholder = child.props.text.toString();
break;
}
break;
}
const create = async () => { const create = async () => {
const title = await promptDocTitle(ctx.std); const title = await promptDocTitle(ctx.std, placeholder);
if (title === null) return; if (title === null) return;
const edgeless = getEdgelessWith(ctx); const edgeless = getEdgelessWith(ctx);

View File

@@ -431,13 +431,13 @@ test('should convert note block to linked doc when clicking turn into linked doc
page, page,
}) => { }) => {
await createEdgelessNoteBlock(page, [100, 100]); await createEdgelessNoteBlock(page, [100, 100]);
await page.keyboard.type('Is this the real life?'); await type(page, '# Two Questions');
await page.keyboard.press('Enter'); await pressEnter(page);
await page.keyboard.type('Is this just fantasy?'); await type(page, 'Is this the real life?');
await pressEnter(page);
await type(page, 'Is this just fantasy?');
await page.keyboard.press('Escape'); await pressEscape(page, 3);
await page.keyboard.press('Escape');
await page.keyboard.press('Escape');
const note = page.locator('affine-edgeless-note', { const note = page.locator('affine-edgeless-note', {
hasText: 'Is this just fantasy?', hasText: 'Is this just fantasy?',
@@ -450,7 +450,9 @@ test('should convert note block to linked doc when clicking turn into linked doc
name: 'Turn into linked doc', name: 'Turn into linked doc',
}); });
await turnIntoLinkedDocBtn.click(); await turnIntoLinkedDocBtn.click();
await page.keyboard.type('Bohemian Rhapsody'); const inputModal = page.getByRole('dialog').locator('input');
await expect(inputModal).toHaveValue('Two Questions');
await type(page, 'Bohemian Rhapsody');
const confirmBtn = page.getByTestId('confirm-modal-confirm'); const confirmBtn = page.getByTestId('confirm-modal-confirm');
await confirmBtn.click(); await confirmBtn.click();
@@ -462,6 +464,7 @@ test('should convert note block to linked doc when clicking turn into linked doc
await expect(noteInSyncedDoc).toBeVisible(); await expect(noteInSyncedDoc).toBeVisible();
const paragraphs = noteInSyncedDoc.locator('affine-paragraph'); const paragraphs = noteInSyncedDoc.locator('affine-paragraph');
await expect(paragraphs.first()).toContainText('Is this the real life?'); await expect(paragraphs.nth(0)).toContainText('Two Questions');
await expect(paragraphs.last()).toContainText('Is this just fantasy?'); await expect(paragraphs.nth(1)).toContainText('Is this the real life?');
await expect(paragraphs.nth(2)).toContainText('Is this just fantasy?');
}); });