From a3dc0745742615b32819e2b6f7d796d6737bed3a Mon Sep 17 00:00:00 2001 From: pengx17 Date: Tue, 8 Oct 2024 02:06:59 +0000 Subject: [PATCH] feat: ctrl click to open embeded doc in new tab (#8401) fix AF-1176 depends on https://github.com/toeverything/blocksuite/pull/8478 --- .../block-suite-editor/lit-adaper.tsx | 2 + .../specs/custom/spec-patchers.tsx | 17 +++++ packages/frontend/core/src/utils/event.ts | 2 +- .../e2e/local-first-openpage-newtab.spec.ts | 63 +++++++++++++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-editor/lit-adaper.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-editor/lit-adaper.tsx index 0590df8b8f..2d7d42bc02 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-editor/lit-adaper.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-editor/lit-adaper.tsx @@ -44,6 +44,7 @@ import { BlocksuiteEditorJournalDocTitle } from './journal-doc-title'; import { patchDocModeService, patchEdgelessClipboard, + patchEmbedLinkedDocBlockConfig, patchForSharedPage, patchNotificationService, patchParseDocUrlExtension, @@ -135,6 +136,7 @@ const usePatchSpecs = (shared: boolean, mode: DocMode) => { patched = patched.concat(patchEdgelessClipboard()); patched = patched.concat(patchParseDocUrlExtension(framework)); patched = patched.concat(patchQuickSearchService(framework)); + patched = patched.concat(patchEmbedLinkedDocBlockConfig(framework)); if (shared) { patched = patched.concat(patchForSharedPage()); } diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx index 63ae7ae70a..5728825224 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx @@ -20,6 +20,8 @@ import { RecentDocsQuickSearchSession, } from '@affine/core/modules/quicksearch'; import { ExternalLinksQuickSearchSession } from '@affine/core/modules/quicksearch/impls/external-links'; +import { WorkbenchService } from '@affine/core/modules/workbench'; +import { isNewTabTrigger } from '@affine/core/utils'; import { DebugLogger } from '@affine/debug'; import { track } from '@affine/track'; import { @@ -41,6 +43,7 @@ import { DocModeExtension, EdgelessRootBlockComponent, EmbedLinkedDocBlockComponent, + EmbedLinkedDocBlockConfigExtension, NotificationExtension, ParseDocUrlExtension, PeekViewExtension, @@ -224,6 +227,20 @@ export function patchNotificationService({ }); } +export function patchEmbedLinkedDocBlockConfig(framework: FrameworkProvider) { + const getWorkbench = () => framework.get(WorkbenchService).workbench; + + return EmbedLinkedDocBlockConfigExtension({ + handleClick(e, _, refInfo) { + if (isNewTabTrigger(e)) { + const workbench = getWorkbench(); + workbench.openDoc(refInfo.pageId, { at: 'new-tab' }); + e.preventDefault(); + } + }, + }); +} + export function patchPeekViewService(service: PeekViewService) { return PeekViewExtension({ peek: (target: ActivePeekView['target'], template?: TemplateResult) => { diff --git a/packages/frontend/core/src/utils/event.ts b/packages/frontend/core/src/utils/event.ts index 4d0fd45750..c2a15b3027 100644 --- a/packages/frontend/core/src/utils/event.ts +++ b/packages/frontend/core/src/utils/event.ts @@ -13,6 +13,6 @@ export function stopEvent(event: BaseSyntheticEvent) { event.preventDefault(); } -export function isNewTabTrigger(event?: React.MouseEvent) { +export function isNewTabTrigger(event?: React.MouseEvent | MouseEvent) { return event ? event.ctrlKey || event.metaKey || event.button === 1 : false; } diff --git a/tests/affine-local/e2e/local-first-openpage-newtab.spec.ts b/tests/affine-local/e2e/local-first-openpage-newtab.spec.ts index b04d25fa52..0997c13a94 100644 --- a/tests/affine-local/e2e/local-first-openpage-newtab.spec.ts +++ b/tests/affine-local/e2e/local-first-openpage-newtab.spec.ts @@ -83,3 +83,66 @@ test('mid click all page and open in new tab', async ({ page }) => { timeout: 15000, }); }); + +test('ctrl click embedded doc link and open in new tab', async ({ page }) => { + await openHomePage(page); + await clickNewPageButton(page); + + await getBlockSuiteEditorTitle(page).click(); + await getBlockSuiteEditorTitle(page).fill('this is a new page'); + const newPageUrl = page.url(); + + await clickNewPageButton(page); + await page.keyboard.press('Enter'); // goto main content + + // paste new page url to create linked page + await page.evaluate( + async ([url]) => { + const clipData = { + 'text/plain': url, + }; + const e = new ClipboardEvent('paste', { + clipboardData: new DataTransfer(), + }); + Object.defineProperty(e, 'target', { + writable: false, + value: document, + }); + Object.entries(clipData).forEach(([key, value]) => { + e.clipboardData?.setData(key, value); + }); + document.dispatchEvent(e); + }, + [newPageUrl] + ); + + const referenceNode = page.locator( + 'affine-reference:has-text("this is a new page")' + ); + + // hover on the reference node and change it to embedded card mode + await referenceNode.hover(); + + const referencePopup = page.locator( + 'reference-popup .affine-reference-popover-container' + ); + + await expect(referencePopup).toBeVisible(); + await referencePopup.getByRole('button', { name: 'Switch view' }).click(); + await page.getByRole('button', { name: 'Card view' }).click(); + + const embededDocBlock = page.locator('affine-embed-linked-doc-block'); + + await expect(embededDocBlock).toBeVisible(); + + // open in new tab + const [newTabPage] = await Promise.all([ + page.waitForEvent('popup'), + embededDocBlock.click({ + button: 'left', + modifiers: ['ControlOrMeta'], + }), + ]); + + await expect(newTabPage).toHaveURL(newPageUrl, { timeout: 15000 }); +});