mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-16 17:46:18 +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 {
|
||||
type InsertedLinkType,
|
||||
insertEmbedLinkedDocCommand,
|
||||
insertEmbedSyncedDocCommand,
|
||||
type LinkableFlavour,
|
||||
} 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 { insertBookmarkCommand } from './insert-bookmark';
|
||||
@@ -26,12 +31,26 @@ export const insertLinkByQuickSearchCommand: Command<
|
||||
|
||||
// add linked doc
|
||||
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,
|
||||
params: result.params,
|
||||
});
|
||||
|
||||
return {
|
||||
flavour: 'affine:embed-linked-doc',
|
||||
flavour,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -5,6 +5,7 @@ import type { Command } from '@blocksuite/std';
|
||||
export type LinkableFlavour =
|
||||
| 'affine:bookmark'
|
||||
| 'affine:embed-linked-doc'
|
||||
| 'affine:embed-synced-doc'
|
||||
| 'affine:embed-iframe'
|
||||
| 'affine:embed-figma'
|
||||
| 'affine:embed-github'
|
||||
|
||||
@@ -224,7 +224,7 @@ const conversionsActionGroup = {
|
||||
) {
|
||||
const editorSetting = ctx.std.getOptional(EditorSettingProvider);
|
||||
editorSetting?.set?.(
|
||||
'docDropCanvasPreferView',
|
||||
'docCanvasPreferView',
|
||||
'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);
|
||||
editorSetting?.set?.(
|
||||
'docDropCanvasPreferView',
|
||||
'docCanvasPreferView',
|
||||
'affine:embed-linked-doc'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './adapters';
|
||||
export * from './commands';
|
||||
export * from './configs';
|
||||
export * from './edgeless-clipboard-config';
|
||||
export * from './embed-synced-doc-block';
|
||||
|
||||
@@ -10,7 +10,7 @@ export const GeneralSettingSchema = z
|
||||
.object({
|
||||
edgelessScrollZoom: z.boolean().default(false),
|
||||
edgelessDisableScheduleUpdate: z.boolean().default(false),
|
||||
docDropCanvasPreferView: z
|
||||
docCanvasPreferView: z
|
||||
.enum(['affine:embed-linked-doc', 'affine:embed-synced-doc'])
|
||||
.default('affine:embed-synced-doc'),
|
||||
})
|
||||
|
||||
+2
@@ -70,6 +70,8 @@ const ToggleButton = ({ model }: { model: EmbedSyncedDocModel }) => {
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
data-testid="edgeless-embed-synced-doc-fold-button"
|
||||
data-folded={isFolded}
|
||||
className={styles.button}
|
||||
size={styles.iconSize}
|
||||
onClick={toggle}
|
||||
|
||||
@@ -153,8 +153,7 @@ export class DndService extends Service {
|
||||
|
||||
const flavour =
|
||||
dropTarget === 'canvas'
|
||||
? this.editorSettingService.editorSetting.docDropCanvasPreferView
|
||||
.value
|
||||
? this.editorSettingService.editorSetting.docCanvasPreferView.value
|
||||
: 'affine:embed-linked-doc';
|
||||
|
||||
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.getByTestId('cmdk-label').getByText('Getting Started').click();
|
||||
const toolbar = locateToolbar(page);
|
||||
await toolbar.getByLabel('Switch view').click();
|
||||
await toolbar.getByLabel('Card view').click();
|
||||
await toolbar.getByLabel('Edit').click();
|
||||
|
||||
const editingModal = page.locator('embed-card-edit-modal');
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { test } from '@affine-test/kit/playwright';
|
||||
import {
|
||||
clickEdgelessModeButton,
|
||||
clickView,
|
||||
createEdgelessNoteBlock,
|
||||
fitViewportToContent,
|
||||
locateEditorContainer,
|
||||
} from '@affine-test/kit/utils/editor';
|
||||
import { pressEnter } from '@affine-test/kit/utils/keyboard';
|
||||
import { openHomePage } from '@affine-test/kit/utils/load-page';
|
||||
import {
|
||||
clickNewPageButton,
|
||||
@@ -18,6 +21,8 @@ test.beforeEach(async ({ page }) => {
|
||||
await openHomePage(page);
|
||||
await waitForEditorLoad(page);
|
||||
await clickNewPageButton(page, title);
|
||||
await pressEnter(page);
|
||||
await page.keyboard.type('test content');
|
||||
await clickEdgelessModeButton(page);
|
||||
const container = locateEditorContainer(page);
|
||||
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(/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
|
||||
await page.keyboard.press('Enter');
|
||||
await expect(
|
||||
page.locator('affine-embed-edgeless-linked-doc-block')
|
||||
page.locator('affine-embed-edgeless-synced-doc-block')
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.locator('.affine-embed-linked-doc-content-title')
|
||||
page.getByTestId('edgeless-embed-synced-doc-title')
|
||||
).toContainText(docTitle);
|
||||
|
||||
// focus on the note block
|
||||
await page.waitForTimeout(500);
|
||||
await page
|
||||
.locator('affine-embed-edgeless-linked-doc-block')
|
||||
.locator('affine-embed-edgeless-synced-doc-block')
|
||||
.click({ force: true });
|
||||
await page.waitForTimeout(500);
|
||||
// double clock to show peek view
|
||||
await page
|
||||
.locator('affine-embed-edgeless-linked-doc-block')
|
||||
.locator('affine-embed-edgeless-synced-doc-block')
|
||||
.dblclick({ force: true });
|
||||
await expect(page.getByTestId('peek-view-modal')).toBeVisible();
|
||||
});
|
||||
|
||||
@@ -163,6 +163,17 @@ export async function setViewportZoom(page: Page, zoom = 1, editorIndex = 0) {
|
||||
}, 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
|
||||
* @param point the coordinate on the canvas
|
||||
|
||||
Reference in New Issue
Block a user