mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-25 04:56:02 +08:00
feat(editor): insert embed doc with quick search by default (#12165)
Close [BS-3404](https://linear.app/affine-design/issue/BS-3404/通过embed-doc时,插入的doc还是card-view,应该要默认embed-view) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added the ability to insert and display embedded synced documents, supporting different link types based on editor mode and user preferences. - Introduced new UI interactions and view options for embedded synced documents in edgeless mode. - **Bug Fixes** - Updated UI selectors and preference keys to ensure consistent behavior and correct application of user settings. - **Tests** - Added and updated end-to-end tests for embedding synced documents, including header interactions and viewport fitting. - Improved test coverage for quick search insertion and edgeless embed synced doc features. - **Chores** - Renamed settings and updated exports to align with new embedding functionality. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -2,9 +2,14 @@ import { insertEmbedIframeWithUrlCommand } from '@blocksuite/affine-block-embed'
|
|||||||
import {
|
import {
|
||||||
type InsertedLinkType,
|
type InsertedLinkType,
|
||||||
insertEmbedLinkedDocCommand,
|
insertEmbedLinkedDocCommand,
|
||||||
|
insertEmbedSyncedDocCommand,
|
||||||
type LinkableFlavour,
|
type LinkableFlavour,
|
||||||
} from '@blocksuite/affine-block-embed-doc';
|
} from '@blocksuite/affine-block-embed-doc';
|
||||||
import { QuickSearchProvider } from '@blocksuite/affine-shared/services';
|
import {
|
||||||
|
DocModeProvider,
|
||||||
|
EditorSettingProvider,
|
||||||
|
QuickSearchProvider,
|
||||||
|
} from '@blocksuite/affine-shared/services';
|
||||||
import type { Command } from '@blocksuite/std';
|
import type { Command } from '@blocksuite/std';
|
||||||
|
|
||||||
import { insertBookmarkCommand } from './insert-bookmark';
|
import { insertBookmarkCommand } from './insert-bookmark';
|
||||||
@@ -26,12 +31,26 @@ export const insertLinkByQuickSearchCommand: Command<
|
|||||||
|
|
||||||
// add linked doc
|
// add linked doc
|
||||||
if ('docId' in result) {
|
if ('docId' in result) {
|
||||||
std.command.exec(insertEmbedLinkedDocCommand, {
|
const editorMode = std.get(DocModeProvider).getEditorMode();
|
||||||
|
const editorSettings = std.get(EditorSettingProvider);
|
||||||
|
let flavour: LinkableFlavour = 'affine:embed-linked-doc';
|
||||||
|
if (editorMode === 'edgeless') {
|
||||||
|
flavour =
|
||||||
|
editorSettings.setting$.value.docCanvasPreferView ?? flavour;
|
||||||
|
}
|
||||||
|
|
||||||
|
const insertCommand =
|
||||||
|
flavour === 'affine:embed-linked-doc'
|
||||||
|
? insertEmbedLinkedDocCommand
|
||||||
|
: insertEmbedSyncedDocCommand;
|
||||||
|
|
||||||
|
std.command.exec(insertCommand, {
|
||||||
docId: result.docId,
|
docId: result.docId,
|
||||||
params: result.params,
|
params: result.params,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
flavour: 'affine:embed-linked-doc',
|
flavour,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -5,6 +5,7 @@ import type { Command } from '@blocksuite/std';
|
|||||||
export type LinkableFlavour =
|
export type LinkableFlavour =
|
||||||
| 'affine:bookmark'
|
| 'affine:bookmark'
|
||||||
| 'affine:embed-linked-doc'
|
| 'affine:embed-linked-doc'
|
||||||
|
| 'affine:embed-synced-doc'
|
||||||
| 'affine:embed-iframe'
|
| 'affine:embed-iframe'
|
||||||
| 'affine:embed-figma'
|
| 'affine:embed-figma'
|
||||||
| 'affine:embed-github'
|
| 'affine:embed-github'
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ const conversionsActionGroup = {
|
|||||||
) {
|
) {
|
||||||
const editorSetting = ctx.std.getOptional(EditorSettingProvider);
|
const editorSetting = ctx.std.getOptional(EditorSettingProvider);
|
||||||
editorSetting?.set?.(
|
editorSetting?.set?.(
|
||||||
'docDropCanvasPreferView',
|
'docCanvasPreferView',
|
||||||
'affine:embed-synced-doc'
|
'affine:embed-synced-doc'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export { insertEmbedSyncedDocCommand } from './insert-embed-synced-doc';
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
import { insertEmbedCard } from '@blocksuite/affine-block-embed';
|
||||||
|
import type { EmbedCardStyle, ReferenceParams } from '@blocksuite/affine-model';
|
||||||
|
import type { Command } from '@blocksuite/std';
|
||||||
|
|
||||||
|
export const insertEmbedSyncedDocCommand: Command<
|
||||||
|
{
|
||||||
|
docId: string;
|
||||||
|
params?: ReferenceParams;
|
||||||
|
},
|
||||||
|
{ blockId: string }
|
||||||
|
> = (ctx, next) => {
|
||||||
|
const { docId, params, std } = ctx;
|
||||||
|
const flavour = 'affine:embed-synced-doc';
|
||||||
|
const targetStyle: EmbedCardStyle = 'syncedDoc';
|
||||||
|
const props: Record<string, unknown> = { pageId: docId };
|
||||||
|
if (params) props.params = params;
|
||||||
|
const blockId = insertEmbedCard(std, { flavour, targetStyle, props });
|
||||||
|
if (!blockId) return;
|
||||||
|
next({ blockId });
|
||||||
|
};
|
||||||
@@ -171,7 +171,7 @@ const conversionsActionGroup = {
|
|||||||
) {
|
) {
|
||||||
const editorSetting = ctx.std.getOptional(EditorSettingProvider);
|
const editorSetting = ctx.std.getOptional(EditorSettingProvider);
|
||||||
editorSetting?.set?.(
|
editorSetting?.set?.(
|
||||||
'docDropCanvasPreferView',
|
'docCanvasPreferView',
|
||||||
'affine:embed-linked-doc'
|
'affine:embed-linked-doc'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export * from './adapters';
|
export * from './adapters';
|
||||||
|
export * from './commands';
|
||||||
export * from './configs';
|
export * from './configs';
|
||||||
export * from './edgeless-clipboard-config';
|
export * from './edgeless-clipboard-config';
|
||||||
export * from './embed-synced-doc-block';
|
export * from './embed-synced-doc-block';
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const GeneralSettingSchema = z
|
|||||||
.object({
|
.object({
|
||||||
edgelessScrollZoom: z.boolean().default(false),
|
edgelessScrollZoom: z.boolean().default(false),
|
||||||
edgelessDisableScheduleUpdate: z.boolean().default(false),
|
edgelessDisableScheduleUpdate: z.boolean().default(false),
|
||||||
docDropCanvasPreferView: z
|
docCanvasPreferView: z
|
||||||
.enum(['affine:embed-linked-doc', 'affine:embed-synced-doc'])
|
.enum(['affine:embed-linked-doc', 'affine:embed-synced-doc'])
|
||||||
.default('affine:embed-synced-doc'),
|
.default('affine:embed-synced-doc'),
|
||||||
})
|
})
|
||||||
|
|||||||
+2
@@ -70,6 +70,8 @@ const ToggleButton = ({ model }: { model: EmbedSyncedDocModel }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<IconButton
|
<IconButton
|
||||||
|
data-testid="edgeless-embed-synced-doc-fold-button"
|
||||||
|
data-folded={isFolded}
|
||||||
className={styles.button}
|
className={styles.button}
|
||||||
size={styles.iconSize}
|
size={styles.iconSize}
|
||||||
onClick={toggle}
|
onClick={toggle}
|
||||||
|
|||||||
@@ -153,8 +153,7 @@ export class DndService extends Service {
|
|||||||
|
|
||||||
const flavour =
|
const flavour =
|
||||||
dropTarget === 'canvas'
|
dropTarget === 'canvas'
|
||||||
? this.editorSettingService.editorSetting.docDropCanvasPreferView
|
? this.editorSettingService.editorSetting.docCanvasPreferView.value
|
||||||
.value
|
|
||||||
: 'affine:embed-linked-doc';
|
: 'affine:embed-linked-doc';
|
||||||
|
|
||||||
const { entity, bsEntity } = args.source.data;
|
const { entity, bsEntity } = args.source.data;
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ test('should close embed editing modal when editor switching to page mode by sho
|
|||||||
await page.keyboard.press('@');
|
await page.keyboard.press('@');
|
||||||
await page.getByTestId('cmdk-label').getByText('Getting Started').click();
|
await page.getByTestId('cmdk-label').getByText('Getting Started').click();
|
||||||
const toolbar = locateToolbar(page);
|
const toolbar = locateToolbar(page);
|
||||||
|
await toolbar.getByLabel('Switch view').click();
|
||||||
|
await toolbar.getByLabel('Card view').click();
|
||||||
await toolbar.getByLabel('Edit').click();
|
await toolbar.getByLabel('Edit').click();
|
||||||
|
|
||||||
const editingModal = page.locator('embed-card-edit-modal');
|
const editingModal = page.locator('embed-card-edit-modal');
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import { test } from '@affine-test/kit/playwright';
|
import { test } from '@affine-test/kit/playwright';
|
||||||
import {
|
import {
|
||||||
clickEdgelessModeButton,
|
clickEdgelessModeButton,
|
||||||
|
clickView,
|
||||||
createEdgelessNoteBlock,
|
createEdgelessNoteBlock,
|
||||||
|
fitViewportToContent,
|
||||||
locateEditorContainer,
|
locateEditorContainer,
|
||||||
} from '@affine-test/kit/utils/editor';
|
} from '@affine-test/kit/utils/editor';
|
||||||
|
import { pressEnter } from '@affine-test/kit/utils/keyboard';
|
||||||
import { openHomePage } from '@affine-test/kit/utils/load-page';
|
import { openHomePage } from '@affine-test/kit/utils/load-page';
|
||||||
import {
|
import {
|
||||||
clickNewPageButton,
|
clickNewPageButton,
|
||||||
@@ -18,6 +21,8 @@ test.beforeEach(async ({ page }) => {
|
|||||||
await openHomePage(page);
|
await openHomePage(page);
|
||||||
await waitForEditorLoad(page);
|
await waitForEditorLoad(page);
|
||||||
await clickNewPageButton(page, title);
|
await clickNewPageButton(page, title);
|
||||||
|
await pressEnter(page);
|
||||||
|
await page.keyboard.type('test content');
|
||||||
await clickEdgelessModeButton(page);
|
await clickEdgelessModeButton(page);
|
||||||
const container = locateEditorContainer(page);
|
const container = locateEditorContainer(page);
|
||||||
await container.click();
|
await container.click();
|
||||||
@@ -56,3 +61,43 @@ test('should not show hidden note in embed view page mode', async ({
|
|||||||
await expect(embedLink.getByText(/visible content/)).toBeVisible();
|
await expect(embedLink.getByText(/visible content/)).toBeVisible();
|
||||||
await expect(embedLink.getByText(/hidden content/)).toBeHidden();
|
await expect(embedLink.getByText(/hidden content/)).toBeHidden();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.describe('edgeless', () => {
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await clickNewPageButton(page);
|
||||||
|
await clickEdgelessModeButton(page);
|
||||||
|
await clickView(page, [0, 0]);
|
||||||
|
await page.keyboard.type('@' + title);
|
||||||
|
await page
|
||||||
|
.getByTestId('cmdk-quick-search')
|
||||||
|
.getByText(/^Synced Block Test$/)
|
||||||
|
.click();
|
||||||
|
await fitViewportToContent(page);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('header of edgeless embed synced doc', () => {
|
||||||
|
test('should fold button works', async ({ page }) => {
|
||||||
|
const embedBlock = page.locator('affine-embed-edgeless-synced-doc-block');
|
||||||
|
const foldButton = embedBlock.getByTestId(
|
||||||
|
'edgeless-embed-synced-doc-fold-button'
|
||||||
|
);
|
||||||
|
const content = embedBlock.locator('editor-host');
|
||||||
|
|
||||||
|
await expect(foldButton).toHaveAttribute('data-folded', 'false');
|
||||||
|
await expect(content).toBeVisible();
|
||||||
|
|
||||||
|
await foldButton.click();
|
||||||
|
|
||||||
|
await expect(content).toBeHidden();
|
||||||
|
await expect(foldButton).toHaveAttribute('data-folded', 'true');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show title in header', async ({ page }) => {
|
||||||
|
const embedBlock = page.locator('affine-embed-edgeless-synced-doc-block');
|
||||||
|
const headerTitle = embedBlock.getByTestId(
|
||||||
|
'edgeless-embed-synced-doc-title'
|
||||||
|
);
|
||||||
|
await expect(headerTitle).toHaveText(title);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -484,21 +484,21 @@ test('can use @ to open quick search to search for doc and insert into canvas',
|
|||||||
// press enter to insert the page to canvas
|
// press enter to insert the page to canvas
|
||||||
await page.keyboard.press('Enter');
|
await page.keyboard.press('Enter');
|
||||||
await expect(
|
await expect(
|
||||||
page.locator('affine-embed-edgeless-linked-doc-block')
|
page.locator('affine-embed-edgeless-synced-doc-block')
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
await expect(
|
await expect(
|
||||||
page.locator('.affine-embed-linked-doc-content-title')
|
page.getByTestId('edgeless-embed-synced-doc-title')
|
||||||
).toContainText(docTitle);
|
).toContainText(docTitle);
|
||||||
|
|
||||||
// focus on the note block
|
// focus on the note block
|
||||||
await page.waitForTimeout(500);
|
await page.waitForTimeout(500);
|
||||||
await page
|
await page
|
||||||
.locator('affine-embed-edgeless-linked-doc-block')
|
.locator('affine-embed-edgeless-synced-doc-block')
|
||||||
.click({ force: true });
|
.click({ force: true });
|
||||||
await page.waitForTimeout(500);
|
await page.waitForTimeout(500);
|
||||||
// double clock to show peek view
|
// double clock to show peek view
|
||||||
await page
|
await page
|
||||||
.locator('affine-embed-edgeless-linked-doc-block')
|
.locator('affine-embed-edgeless-synced-doc-block')
|
||||||
.dblclick({ force: true });
|
.dblclick({ force: true });
|
||||||
await expect(page.getByTestId('peek-view-modal')).toBeVisible();
|
await expect(page.getByTestId('peek-view-modal')).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -163,6 +163,17 @@ export async function setViewportZoom(page: Page, zoom = 1, editorIndex = 0) {
|
|||||||
}, zoom);
|
}, zoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fitViewportToContent(page: Page, editorIndex = 0) {
|
||||||
|
const container = locateEditorContainer(page, editorIndex);
|
||||||
|
return container.evaluate(container => {
|
||||||
|
const root = container.querySelector('affine-edgeless-root');
|
||||||
|
if (!root) {
|
||||||
|
throw new Error('Edgeless root not found');
|
||||||
|
}
|
||||||
|
root.gfx.fitToScreen();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a canvas point to view coordinate
|
* Convert a canvas point to view coordinate
|
||||||
* @param point the coordinate on the canvas
|
* @param point the coordinate on the canvas
|
||||||
|
|||||||
Reference in New Issue
Block a user