feat: linked doc supports aliases (#9009)

Closes: [AF-1882](https://linear.app/affine-design/issue/AF-1882/实现-generatedocurlservice)
Upstreams: https://github.com/toeverything/blocksuite/pull/8806
This commit is contained in:
fundon
2024-12-09 03:12:09 +00:00
parent efa4a80019
commit 0bafc35aad
8 changed files with 394 additions and 33 deletions

View File

@@ -30,6 +30,7 @@ import * as styles from './styles.css';
interface AffinePageReferenceProps { interface AffinePageReferenceProps {
pageId: string; pageId: string;
params?: URLSearchParams; params?: URLSearchParams;
title?: string | null; // title alias
className?: string; className?: string;
Icon?: ComponentType; Icon?: ComponentType;
onClick?: (e: MouseEvent) => void; onClick?: (e: MouseEvent) => void;
@@ -38,20 +39,21 @@ interface AffinePageReferenceProps {
function AffinePageReferenceInner({ function AffinePageReferenceInner({
pageId, pageId,
params, params,
title,
Icon: UserIcon, Icon: UserIcon,
}: AffinePageReferenceProps) { }: AffinePageReferenceProps) {
const docDisplayMetaService = useService(DocDisplayMetaService); const docDisplayMetaService = useService(DocDisplayMetaService);
const docsService = useService(DocsService); const docsService = useService(DocsService);
const i18n = useI18n(); const i18n = useI18n();
let linkWithMode: DocMode | null = null; let referenceWithMode: DocMode | null = null;
let linkToNode = false; let referenceToNode = false;
if (params) { if (params) {
const m = params.get('mode'); const m = params.get('mode');
if (m && (m === 'page' || m === 'edgeless')) { if (m && (m === 'page' || m === 'edgeless')) {
linkWithMode = m as DocMode; referenceWithMode = m as DocMode;
} }
linkToNode = params.has('blockIds') || params.has('elementIds'); referenceToNode = params.has('blockIds') || params.has('elementIds');
} }
const Icon = useLiveData( const Icon = useLiveData(
@@ -61,25 +63,33 @@ function AffinePageReferenceInner({
} }
return get( return get(
docDisplayMetaService.icon$(pageId, { docDisplayMetaService.icon$(pageId, {
mode: linkWithMode ?? undefined, mode: referenceWithMode ?? undefined,
reference: true, reference: true,
referenceToNode: linkToNode, referenceToNode,
hasTitleAlias: Boolean(title),
}) })
); );
}) })
); );
const notFound = !useLiveData(docsService.list.doc$(pageId)); const notFound = !useLiveData(docsService.list.doc$(pageId));
let title = useLiveData( const docTitle = useLiveData(
docDisplayMetaService.title$(pageId, { reference: true }) docDisplayMetaService.title$(pageId, { reference: true })
); );
title = notFound ? i18n.t('com.affine.notFoundPage.title') : title; if (notFound) {
title = i18n.t('com.affine.notFoundPage.title');
}
if (!title) {
title = i18n.t(docTitle);
}
return ( return (
<span className={notFound ? styles.notFound : ''}> <span className={notFound ? styles.notFound : ''}>
<Icon className={styles.pageReferenceIcon} /> <Icon className={styles.pageReferenceIcon} />
<span className="affine-reference-title">{i18n.t(title)}</span> <span className="affine-reference-title">{title}</span>
</span> </span>
); );
} }
@@ -87,6 +97,7 @@ function AffinePageReferenceInner({
export function AffinePageReference({ export function AffinePageReference({
pageId, pageId,
params, params,
title,
className, className,
Icon, Icon,
onClick: userOnClick, onClick: userOnClick,
@@ -152,7 +163,12 @@ export function AffinePageReference({
onClick={onClick} onClick={onClick}
className={clsx(styles.pageReferenceLink, className)} className={clsx(styles.pageReferenceLink, className)}
> >
<AffinePageReferenceInner pageId={pageId} params={params} Icon={Icon} /> <AffinePageReferenceInner
pageId={pageId}
params={params}
title={title}
Icon={Icon}
/>
</WorkbenchLink> </WorkbenchLink>
); );
} }
@@ -161,6 +177,7 @@ export function AffineSharedPageReference({
pageId, pageId,
docCollection, docCollection,
params, params,
title,
Icon, Icon,
onClick: userOnClick, onClick: userOnClick,
}: AffinePageReferenceProps & { }: AffinePageReferenceProps & {
@@ -213,7 +230,12 @@ export function AffineSharedPageReference({
onClick={onClick} onClick={onClick}
className={styles.pageReferenceLink} className={styles.pageReferenceLink}
> >
<AffinePageReferenceInner pageId={pageId} params={params} Icon={Icon} /> <AffinePageReferenceInner
pageId={pageId}
params={params}
title={title}
Icon={Icon}
/>
</Link> </Link>
); );
} }

View File

@@ -56,6 +56,7 @@ import {
patchEmbedLinkedDocBlockConfig, patchEmbedLinkedDocBlockConfig,
patchForMobile, patchForMobile,
patchForSharedPage, patchForSharedPage,
patchGenerateDocUrlExtension,
patchNotificationService, patchNotificationService,
patchParseDocUrlExtension, patchParseDocUrlExtension,
patchPeekViewService, patchPeekViewService,
@@ -112,6 +113,8 @@ const usePatchSpecs = (shared: boolean, mode: DocMode) => {
const pageId = data.pageId; const pageId = data.pageId;
if (!pageId) return <span />; if (!pageId) return <span />;
// title alias
const title = data.title;
const params = toURLSearchParams(data.params); const params = toURLSearchParams(data.params);
if (workspaceService.workspace.openOptions.isSharedMode) { if (workspaceService.workspace.openOptions.isSharedMode) {
@@ -120,11 +123,14 @@ const usePatchSpecs = (shared: boolean, mode: DocMode) => {
docCollection={workspaceService.workspace.docCollection} docCollection={workspaceService.workspace.docCollection}
pageId={pageId} pageId={pageId}
params={params} params={params}
title={title}
/> />
); );
} }
return <AffinePageReference pageId={pageId} params={params} />; return (
<AffinePageReference pageId={pageId} params={params} title={title} />
);
}; };
}, [workspaceService]); }, [workspaceService]);
@@ -147,6 +153,7 @@ const usePatchSpecs = (shared: boolean, mode: DocMode) => {
patched = patched.concat(patchPeekViewService(peekViewService)); patched = patched.concat(patchPeekViewService(peekViewService));
patched = patched.concat(patchEdgelessClipboard()); patched = patched.concat(patchEdgelessClipboard());
patched = patched.concat(patchParseDocUrlExtension(framework)); patched = patched.concat(patchParseDocUrlExtension(framework));
patched = patched.concat(patchGenerateDocUrlExtension(framework));
patched = patched.concat(patchQuickSearchService(framework)); patched = patched.concat(patchQuickSearchService(framework));
patched = patched.concat(patchEmbedLinkedDocBlockConfig(framework)); patched = patched.concat(patchEmbedLinkedDocBlockConfig(framework));
if (shared) { if (shared) {

View File

@@ -52,7 +52,7 @@ function createCopyLinkToBlockMenuItem(
const options: UseSharingUrl = { const options: UseSharingUrl = {
workspaceId, workspaceId,
pageId, pageId,
shareMode: mode, mode,
blockIds: [model.id], blockIds: [model.id],
}; };

View File

@@ -7,6 +7,7 @@ import {
toReactNode, toReactNode,
type useConfirmModal, type useConfirmModal,
} from '@affine/component'; } from '@affine/component';
import { WorkspaceServerService } from '@affine/core/modules/cloud';
import type { EditorService } from '@affine/core/modules/editor'; import type { EditorService } from '@affine/core/modules/editor';
import { EditorSettingService } from '@affine/core/modules/editor-setting'; import { EditorSettingService } from '@affine/core/modules/editor-setting';
import { resolveLinkToDoc } from '@affine/core/modules/navigation'; import { resolveLinkToDoc } from '@affine/core/modules/navigation';
@@ -47,6 +48,7 @@ import {
EdgelessRootBlockComponent, EdgelessRootBlockComponent,
EmbedLinkedDocBlockComponent, EmbedLinkedDocBlockComponent,
EmbedLinkedDocBlockConfigExtension, EmbedLinkedDocBlockConfigExtension,
GenerateDocUrlExtension,
MobileSpecsPatches, MobileSpecsPatches,
NotificationExtension, NotificationExtension,
ParseDocUrlExtension, ParseDocUrlExtension,
@@ -55,6 +57,7 @@ import {
ReferenceNodeConfigExtension, ReferenceNodeConfigExtension,
} from '@blocksuite/affine/blocks'; } from '@blocksuite/affine/blocks';
import { type BlockSnapshot, Text } from '@blocksuite/affine/store'; import { type BlockSnapshot, Text } from '@blocksuite/affine/store';
import type { ReferenceParams } from '@blocksuite/affine-model';
import { import {
AIChatBlockSchema, AIChatBlockSchema,
type DocProps, type DocProps,
@@ -68,6 +71,7 @@ import { customElement } from 'lit/decorators.js';
import { literal } from 'lit/static-html.js'; import { literal } from 'lit/static-html.js';
import { pick } from 'lodash-es'; import { pick } from 'lodash-es';
import { generateUrl } from '../../../../hooks/affine/use-share-url';
import { createKeyboardToolbarConfig } from './widgets/keyboard-toolbar'; import { createKeyboardToolbarConfig } from './widgets/keyboard-toolbar';
export type ReferenceReactRenderer = ( export type ReferenceReactRenderer = (
@@ -110,13 +114,13 @@ export function patchReferenceRenderer(
reactToLit: (element: ElementOrFactory) => TemplateResult, reactToLit: (element: ElementOrFactory) => TemplateResult,
reactRenderer: ReferenceReactRenderer reactRenderer: ReferenceReactRenderer
): ExtensionType { ): ExtensionType {
const litRenderer = (reference: AffineReference) => { const customContent = (reference: AffineReference) => {
const node = reactRenderer(reference); const node = reactRenderer(reference);
return reactToLit(node); return reactToLit(node);
}; };
return ReferenceNodeConfigExtension({ return ReferenceNodeConfigExtension({
customContent: litRenderer, customContent,
}); });
} }
@@ -463,18 +467,32 @@ export function patchParseDocUrlExtension(framework: FrameworkProvider) {
const info = resolveLinkToDoc(url); const info = resolveLinkToDoc(url);
if (!info || info.workspaceId !== workspaceService.workspace.id) return; if (!info || info.workspaceId !== workspaceService.workspace.id) return;
return { delete info.refreshKey;
docId: info.docId,
blockIds: info.blockIds, return info;
elementIds: info.elementIds,
mode: info.mode,
};
}, },
}); });
return [ParseDocUrl]; return [ParseDocUrl];
} }
export function patchGenerateDocUrlExtension(framework: FrameworkProvider) {
const workspaceService = framework.get(WorkspaceService);
const workspaceServerService = framework.get(WorkspaceServerService);
const GenerateDocUrl = GenerateDocUrlExtension({
generateDocUrl(pageId: string, params?: ReferenceParams) {
return generateUrl({
...params,
pageId,
workspaceId: workspaceService.workspace.id,
baseUrl: workspaceServerService.server?.baseUrl ?? location.origin,
});
},
});
return [GenerateDocUrl];
}
export function patchEdgelessClipboard() { export function patchEdgelessClipboard() {
class EdgelessClipboardWatcher extends BlockServiceWatcher { class EdgelessClipboardWatcher extends BlockServiceWatcher {
static override readonly flavour = 'affine:page'; static override readonly flavour = 'affine:page';

View File

@@ -93,7 +93,7 @@ function createCopyLinkToBlockMenuItem(
const mode = editor.mode$.value; const mode = editor.mode$.value;
const pageId = editor.doc.id; const pageId = editor.doc.id;
const workspaceId = editor.doc.workspace.id; const workspaceId = editor.doc.workspace.id;
const options: UseSharingUrl = { workspaceId, pageId, shareMode: mode }; const options: UseSharingUrl = { workspaceId, pageId, mode };
let type = ''; let type = '';
if (mode === 'page') { if (mode === 'page') {

View File

@@ -13,7 +13,7 @@ import { useCallback } from 'react';
export type UseSharingUrl = { export type UseSharingUrl = {
workspaceId: string; workspaceId: string;
pageId: string; pageId: string;
shareMode?: DocMode; mode?: DocMode;
blockIds?: string[]; blockIds?: string[];
elementIds?: string[]; elementIds?: string[];
xywh?: string; // not needed currently xywh?: string; // not needed currently
@@ -30,7 +30,7 @@ export const generateUrl = ({
pageId, pageId,
blockIds, blockIds,
elementIds, elementIds,
shareMode: mode, mode,
xywh, // not needed currently xywh, // not needed currently
}: UseSharingUrl & { baseUrl: string }) => { }: UseSharingUrl & { baseUrl: string }) => {
try { try {
@@ -38,23 +38,24 @@ export const generateUrl = ({
const search = toURLSearchParams({ mode, blockIds, elementIds, xywh }); const search = toURLSearchParams({ mode, blockIds, elementIds, xywh });
if (search?.size) url.search = search.toString(); if (search?.size) url.search = search.toString();
return url.toString(); return url.toString();
} catch { } catch (err) {
return null; console.error(err);
return undefined;
} }
}; };
const getShareLinkType = ({ const getShareLinkType = ({
shareMode, mode,
blockIds, blockIds,
elementIds, elementIds,
}: { }: {
shareMode?: DocMode; mode?: DocMode;
blockIds?: string[]; blockIds?: string[];
elementIds?: string[]; elementIds?: string[];
}) => { }) => {
if (shareMode === 'page') { if (mode === 'page') {
return 'doc'; return 'doc';
} else if (shareMode === 'edgeless') { } else if (mode === 'edgeless') {
return 'whiteboard'; return 'whiteboard';
} else if (blockIds && blockIds.length > 0) { } else if (blockIds && blockIds.length > 0) {
return 'block'; return 'block';
@@ -131,17 +132,17 @@ export const useSharingUrl = ({ workspaceId, pageId }: UseSharingUrl) => {
const serverService = useService(ServerService); const serverService = useService(ServerService);
const onClickCopyLink = useCallback( const onClickCopyLink = useCallback(
(shareMode?: DocMode, blockIds?: string[], elementIds?: string[]) => { (mode?: DocMode, blockIds?: string[], elementIds?: string[]) => {
const sharingUrl = generateUrl({ const sharingUrl = generateUrl({
baseUrl: serverService.server.baseUrl, baseUrl: serverService.server.baseUrl,
workspaceId, workspaceId,
pageId, pageId,
blockIds, blockIds,
elementIds, elementIds,
shareMode, // if view is not provided, use the current view mode, // if view is not provided, use the current view
}); });
const type = getShareLinkType({ const type = getShareLinkType({
shareMode, mode,
blockIds, blockIds,
elementIds, elementIds,
}); });

View File

@@ -1,6 +1,7 @@
import { extractEmojiIcon } from '@affine/core/utils'; import { extractEmojiIcon } from '@affine/core/utils';
import { i18nTime } from '@affine/i18n'; import { i18nTime } from '@affine/i18n';
import { import {
AliasIcon as LitAliasIcon,
BlockLinkIcon as LitBlockLinkIcon, BlockLinkIcon as LitBlockLinkIcon,
EdgelessIcon as LitEdgelessIcon, EdgelessIcon as LitEdgelessIcon,
LinkedEdgelessIcon as LitLinkedEdgelessIcon, LinkedEdgelessIcon as LitLinkedEdgelessIcon,
@@ -11,6 +12,7 @@ import {
YesterdayIcon as LitYesterdayIcon, YesterdayIcon as LitYesterdayIcon,
} from '@blocksuite/icons/lit'; } from '@blocksuite/icons/lit';
import { import {
AliasIcon,
BlockLinkIcon, BlockLinkIcon,
EdgelessIcon, EdgelessIcon,
LinkedEdgelessIcon, LinkedEdgelessIcon,
@@ -42,6 +44,7 @@ interface DocDisplayIconOptions<T extends IconType> {
mode?: 'edgeless' | 'page'; mode?: 'edgeless' | 'page';
reference?: boolean; reference?: boolean;
referenceToNode?: boolean; referenceToNode?: boolean;
hasTitleAlias?: boolean;
/** /**
* @default true * @default true
*/ */
@@ -57,6 +60,7 @@ interface DocDisplayTitleOptions {
} }
const rcIcons = { const rcIcons = {
AliasIcon,
BlockLinkIcon, BlockLinkIcon,
EdgelessIcon, EdgelessIcon,
LinkedEdgelessIcon, LinkedEdgelessIcon,
@@ -67,6 +71,7 @@ const rcIcons = {
YesterdayIcon, YesterdayIcon,
}; };
const litIcons = { const litIcons = {
AliasIcon: LitAliasIcon,
BlockLinkIcon: LitBlockLinkIcon, BlockLinkIcon: LitBlockLinkIcon,
EdgelessIcon: LitEdgelessIcon, EdgelessIcon: LitEdgelessIcon,
LinkedEdgelessIcon: LitLinkedEdgelessIcon, LinkedEdgelessIcon: LitLinkedEdgelessIcon,
@@ -130,6 +135,12 @@ export class DocDisplayMetaService extends Service {
const mode = doc ? get(doc.primaryMode$) : undefined; const mode = doc ? get(doc.primaryMode$) : undefined;
const finalMode = options?.mode ?? mode ?? 'page'; const finalMode = options?.mode ?? mode ?? 'page';
const referenceToNode = !!(options?.reference && options.referenceToNode); const referenceToNode = !!(options?.reference && options.referenceToNode);
const hasTitleAlias = !!(options?.reference && options?.hasTitleAlias);
// increases block link priority with title alias
if (hasTitleAlias) {
return iconSet.AliasIcon;
}
// increases block link priority // increases block link priority
if (referenceToNode) { if (referenceToNode) {

View File

@@ -580,3 +580,305 @@ test('should show edgeless content when switching card view of linked mode doc i
edgelessText.y + edgelessText.height edgelessText.y + edgelessText.height
); );
}); });
// Aliases & Copy link
test.describe('Customize linked doc title and description', () => {
// Inline View
test('should set a custom title for inline link', async ({ page }) => {
await page.keyboard.press('Enter');
await createLinkedPage(page, 'Test Page');
const link = page.locator('affine-reference');
const title = link.locator('.affine-reference-title');
await link.hover();
const toolbar = page.locator('reference-popup');
// Copies link
await toolbar.getByRole('button', { name: 'Copy link' }).click();
const url0 = await link.locator('a').getAttribute('href');
const url1 = await (
await page.evaluateHandle(() => navigator.clipboard.readText())
).jsonValue();
expect(url0).not.toBeNull();
expect(new URL(url0!, coreUrl).pathname).toBe(new URL(url1).pathname);
// Edits title
await toolbar.getByRole('button', { name: 'Edit' }).click();
// Title alias
await page.keyboard.type('Test Page Alias');
await page.keyboard.press('Enter');
await expect(title).toHaveText('Test Page Alias');
// Original title
await link.hover();
const docTitle = toolbar.getByRole('button', { name: 'Doc title' });
await expect(docTitle).toHaveText('Test Page', { useInnerText: true });
// Edits title
await toolbar.getByRole('button', { name: 'Edit' }).click();
const aliasPopup = page.locator('reference-alias-popup');
// Input
await expect(aliasPopup.locator('input')).toHaveValue('Test Page Alias');
// Reset
await aliasPopup.getByRole('button', { name: 'Reset' }).click();
await expect(title).toHaveText('Test Page');
await link.hover();
await expect(docTitle).toBeHidden();
});
// Card View
test('should set a custom title and description for card link', async ({
page,
}) => {
await page.keyboard.press('Enter');
await createLinkedPage(page, 'Test Page');
const inlineLink = page.locator('affine-reference');
const inlineToolbar = page.locator('reference-popup');
await inlineLink.hover();
// Copies link
await inlineToolbar.getByRole('button', { name: 'Copy link' }).click();
const url0 = await (
await page.evaluateHandle(() => navigator.clipboard.readText())
).jsonValue();
// Edits title
await inlineToolbar.getByRole('button', { name: 'Edit' }).click();
// Title alias
await page.keyboard.type('Test Page Alias');
await page.keyboard.press('Enter');
await inlineLink.hover();
// Switches to card view
await inlineToolbar.getByRole('button', { name: 'Switch view' }).click();
await inlineToolbar.getByRole('button', { name: 'Card view' }).click();
const cardLink = page.locator('affine-embed-linked-doc-block');
const cardTitle = cardLink.locator(
'.affine-embed-linked-doc-content-title-text'
);
const cardDescription = cardLink.locator(
'.affine-embed-linked-doc-content-note.alias'
);
const cardToolbar = page.locator('affine-embed-card-toolbar');
await cardLink.click();
// Copies link
await cardToolbar.getByRole('button', { name: 'Copy link' }).click();
const url1 = await (
await page.evaluateHandle(() => navigator.clipboard.readText())
).jsonValue();
expect(url0).not.toBeNull();
expect(url1).not.toBeNull();
expect(url0).toBe(url1);
const docTitle = cardToolbar.getByRole('button', { name: 'Doc title' });
await expect(docTitle).toHaveText('Test Page', { useInnerText: true });
await expect(cardTitle).toHaveText('Test Page Alias');
// Edits title & description
await cardToolbar.getByRole('button', { name: 'Edit' }).click();
const cardEditPopup = page.locator('embed-card-edit-modal');
// Title alias
await page.keyboard.type('Test Page Alias Again');
await page.keyboard.press('Tab');
// Description alias
await page.keyboard.type('This is a new description');
// Saves aliases
await cardEditPopup.getByRole('button', { name: 'Save' }).click();
await expect(cardTitle).toHaveText('Test Page Alias Again');
await expect(cardDescription).toHaveText('This is a new description');
await cardLink.click();
// Switches to inline view
{
await cardToolbar.getByRole('button', { name: 'Switch view' }).click();
await cardToolbar.getByRole('button', { name: 'Inline view' }).click();
// Focuses inline editor
const bounds = (await inlineLink.boundingBox())!;
await page.mouse.click(
bounds.x + bounds.width + 30,
bounds.y + bounds.height / 2
);
await inlineLink.hover();
const title = inlineLink.locator('.affine-reference-title');
await expect(title).toHaveText('Test Page Alias Again');
// Switches to card view
await inlineToolbar.getByRole('button', { name: 'Switch view' }).click();
await inlineToolbar.getByRole('button', { name: 'Card view' }).click();
}
await cardLink.click();
await cardToolbar.getByRole('button', { name: 'Edit' }).click();
// Resets
await cardEditPopup.getByRole('button', { name: 'Reset' }).click();
await expect(cardTitle).toHaveText('Test Page');
await expect(cardDescription).toBeHidden();
});
// Embed View
test('should automatically switch to card view and set a custom title and description', async ({
page,
}) => {
await page.keyboard.press('Enter');
await createLinkedPage(page, 'Test Page');
const inlineLink = page.locator('affine-reference');
const inlineToolbar = page.locator('reference-popup');
await inlineLink.hover();
// Copies link
await inlineToolbar.getByRole('button', { name: 'Copy link' }).click();
const url0 = await (
await page.evaluateHandle(() => navigator.clipboard.readText())
).jsonValue();
// Switches to card view
await inlineToolbar.getByRole('button', { name: 'Switch view' }).click();
await inlineToolbar.getByRole('button', { name: 'Card view' }).click();
const cardLink = page.locator('affine-embed-linked-doc-block');
const cardTitle = cardLink.locator(
'.affine-embed-linked-doc-content-title-text'
);
const cardDescription = cardLink.locator(
'.affine-embed-linked-doc-content-note.alias'
);
const cardToolbar = page.locator('affine-embed-card-toolbar');
await cardLink.click();
// Copies link
await cardToolbar.getByRole('button', { name: 'Copy link' }).click();
const url1 = await (
await page.evaluateHandle(() => navigator.clipboard.readText())
).jsonValue();
// Switches to embed view
await cardToolbar.getByRole('button', { name: 'Switch view' }).click();
await cardToolbar.getByRole('button', { name: 'Embed view' }).click();
const embedLink = page.locator('affine-embed-synced-doc-block');
const embedTitle = embedLink.locator('.affine-embed-synced-doc-title');
const embedToolbar = page.locator('affine-embed-card-toolbar');
await embedLink.click();
// Copies link
await embedToolbar.getByRole('button', { name: 'Copy link' }).click();
const url2 = await (
await page.evaluateHandle(() => navigator.clipboard.readText())
).jsonValue();
expect(url0).not.toBeNull();
expect(url1).not.toBeNull();
expect(url2).not.toBeNull();
expect(url0).toBe(url1);
expect(url1).toBe(url2);
// Edits title & description
await embedToolbar.getByRole('button', { name: 'Edit' }).click();
const embedEditPopup = page.locator('embed-card-edit-modal');
// Title alias
await page.keyboard.type('Test Page Alias Again');
await page.keyboard.press('Tab');
// Description alias
await page.keyboard.type('This is a new description');
// Cancels
await embedEditPopup.getByRole('button', { name: 'Cancel' }).click();
await expect(embedEditPopup).toBeHidden();
await embedLink.click();
// Edits title & description
await embedToolbar.getByRole('button', { name: 'Edit' }).click();
// Title alias
await page.keyboard.type('Test Page Alias');
await page.keyboard.press('Tab');
// Description alias
await page.keyboard.type('This is a new description');
// Saves aliases
await embedEditPopup.getByRole('button', { name: 'Save' }).click();
// Automatically switch to card view
await expect(embedLink).toBeHidden();
await expect(cardTitle).toHaveText('Test Page Alias');
await expect(cardDescription).toHaveText('This is a new description');
await cardLink.click();
const docTitle = cardToolbar.getByRole('button', { name: 'Doc title' });
await expect(docTitle).toHaveText('Test Page', { useInnerText: true });
await expect(cardTitle).toHaveText('Test Page Alias');
// Switches to embed view
await cardToolbar.getByRole('button', { name: 'Switch view' }).click();
await cardToolbar.getByRole('button', { name: 'Embed view' }).click();
await expect(embedTitle).toHaveText('Test Page');
await embedLink.click();
// Switches to inline view
{
await embedToolbar.getByRole('button', { name: 'Switch view' }).click();
await embedToolbar.getByRole('button', { name: 'Inline view' }).click();
// Focuses inline editor
const bounds = (await inlineLink.boundingBox())!;
await page.mouse.click(
bounds.x + bounds.width + 30,
bounds.y + bounds.height / 2
);
await inlineLink.hover();
// Upstreams: https://github.com/toeverything/blocksuite/pull/8884
// const title = inlineLink.locator('.affine-reference-title');
// await expect(title).toHaveText('Test Page');
// Switches to embed view
await inlineToolbar.getByRole('button', { name: 'Switch view' }).click();
await inlineToolbar.getByRole('button', { name: 'Embed view' }).click();
}
await embedLink.click();
await expect(embedTitle).toHaveText('Test Page');
await expect(
embedToolbar.getByRole('button', { name: 'Doc title' })
).toBeHidden();
});
});