Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: focus the create page item when query returns no result in at menu #10060

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions blocksuite/blocks/src/root-block/widgets/linked-doc/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,23 @@ export interface LinkedWidgetConfig {
abortSignal: AbortSignal
) => Promise<LinkedMenuGroup[]> | LinkedMenuGroup[];

/**
* Auto focused item
*
* Will be called when the menu is
* - opened
* - query changed
* - menu group or its items changed
*
* If the return value is not null, no action will be taken.
*/
autoFocusedItem?: (
menus: LinkedMenuGroup[],
query: string,
editorHost: EditorHost,
inlineEditor: AffineInlineEditor
) => LinkedMenuItem | null;

mobile: {
useScreenHeight?: boolean;
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
throttle,
WithDisposable,
} from '@blocksuite/global/utils';
import { effect } from '@preact/signals-core';
import { css, html, LitElement, nothing } from 'lit';
import { property, query, queryAll, state } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js';
Expand Down Expand Up @@ -47,6 +48,8 @@ export class LinkedDocPopover extends SignalWatcher(

private readonly _expanded = new Map<string, boolean>();

private _menusItemsEffectCleanup: () => void = () => {};

private readonly _updateLinkedDocGroup = async () => {
const query = this._query;
if (this._updateLinkedDocGroupAbortController) {
Expand All @@ -65,6 +68,30 @@ export class LinkedDocPopover extends SignalWatcher(
this.context.inlineEditor,
this._updateLinkedDocGroupAbortController.signal
);

this._menusItemsEffectCleanup();

// need to rebind the effect because this._linkedDocGroup has changed.
this._menusItemsEffectCleanup = effect(() => {
this._updateAutoFocusedItem();
});
};

private readonly _updateAutoFocusedItem = () => {
if (!this._query) {
return;
}
const autoFocusedItem = this.context.config.autoFocusedItem?.(
this._linkedDocGroup,
this._query,
this.context.std.host,
this.context.inlineEditor
);
if (autoFocusedItem) {
this._activatedItemIndex = this._flattenActionList.findIndex(
item => item.key === autoFocusedItem.key
);
}
};

private _updateLinkedDocGroupAbortController: AbortController | null = null;
Expand Down Expand Up @@ -217,6 +244,11 @@ export class LinkedDocPopover extends SignalWatcher(
});
}

override disconnectedCallback() {
super.disconnectedCallback();
this._menusItemsEffectCleanup();
}

override render() {
const MAX_HEIGHT = 380;
const style = this._position
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
NewXxxEdgelessIcon,
NewXxxPageIcon,
} from '@blocksuite/icons/lit';
import { computed } from '@preact/signals-core';
import { computed, Signal } from '@preact/signals-core';
import { Service } from '@toeverything/infra';
import { cssVarV2 } from '@toeverything/theme/v2';
import { html } from 'lit';
Expand All @@ -28,6 +28,10 @@ import type { DocSearchMenuService } from '../../doc-search-menu/services';
import type { EditorSettingService } from '../../editor-setting';
import { type JournalService, suggestJournalDate } from '../../journal';

function resolveSignal<T>(data: T | Signal<T>): T {
return data instanceof Signal ? data.value : data;
}

export class AtMenuConfigService extends Service {
constructor(
private readonly journalService: JournalService,
Expand All @@ -46,6 +50,7 @@ export class AtMenuConfigService extends Service {
return {
getMenus: this.getMenusFn(),
mobile: this.getMobileConfig(),
autoFocusedItem: this.autoFocusedItem,
};
}

Expand All @@ -56,6 +61,22 @@ export class AtMenuConfigService extends Service {
});
}

private readonly autoFocusedItem = (
menus: LinkedMenuGroup[],
query: string
): LinkedMenuItem | null => {
if (query.trim().length === 0) {
return null;
}
// if the second group (linkToDocGroup) is EMPTY,
// if the query is NOT empty && the second group (linkToDocGroup) is EMPTY,
// we will focus on the first item of the third group (create), which is the "New Doc" item.
if (resolveSignal(menus[1].items).length === 0) {
return resolveSignal(menus[2].items)[0];
}
return null;
};

private newDocMenuGroup(
query: string,
close: () => void,
Expand Down
15 changes: 15 additions & 0 deletions tests/affine-local/e2e/links.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@

// refocus
await cardLink.click();
await cardToolbar.getByLabel('Switch view').click();

Check failure on line 320 in tests/affine-local/e2e/links.spec.ts

View workflow job for this annotation

GitHub Actions / E2E Test (3)

e2e/links.spec.ts:279:1 › allow switching to embed view when linking to the other document with mode

1) e2e/links.spec.ts:279:1 › allow switching to embed view when linking to the other document with mode TimeoutError: locator.click: Timeout 5000ms exceeded. Call log: - waiting for locator('affine-embed-card-toolbar').getByLabel('Switch view') 318 | // refocus 319 | await cardLink.click(); > 320 | await cardToolbar.getByLabel('Switch view').click(); | ^ 321 | 322 | await clickable(cardToolbar.getByLabel('Inline view')); 323 | await notClickable(cardToolbar.getByLabel('Card view')); at /home/runner/work/AFFiNE/AFFiNE/tests/affine-local/e2e/links.spec.ts:320:47

Check failure on line 320 in tests/affine-local/e2e/links.spec.ts

View workflow job for this annotation

GitHub Actions / E2E Test (3)

e2e/links.spec.ts:279:1 › allow switching to embed view when linking to the other document with mode

1) e2e/links.spec.ts:279:1 › allow switching to embed view when linking to the other document with mode TimeoutError: locator.click: Timeout 5000ms exceeded. Call log: - waiting for locator('affine-embed-card-toolbar').getByLabel('Switch view') 318 | // refocus 319 | await cardLink.click(); > 320 | await cardToolbar.getByLabel('Switch view').click(); | ^ 321 | 322 | await clickable(cardToolbar.getByLabel('Inline view')); 323 | await notClickable(cardToolbar.getByLabel('Card view')); at /home/runner/work/AFFiNE/AFFiNE/tests/affine-local/e2e/links.spec.ts:320:47

await clickable(cardToolbar.getByLabel('Inline view'));
await notClickable(cardToolbar.getByLabel('Card view'));
Expand Down Expand Up @@ -473,6 +473,21 @@
).toBeVisible();
});

test('@ popover can auto focus on the "New Doc" item when query returns no items', async ({
page,
}) => {
await page.keyboard.press('Enter');
await waitForEmptyEditor(page);
await page.keyboard.press('@');
await page.keyboard.type('nawowenni');
await expect(page.locator('.linked-doc-popover')).toBeVisible();
const newDocMenuItem = page
.locator('.linked-doc-popover')
.locator('[data-id="create-page"]');
await expect(newDocMenuItem).toBeVisible();
await expect(newDocMenuItem).toHaveAttribute('hover', 'true');
});

test('linked doc should show markdown preview in the backlink section', async ({
page,
}) => {
Expand Down Expand Up @@ -607,7 +622,7 @@
.locator('affine-embed-edgeless-linked-doc-block')
.waitFor({ state: 'visible' });
await page.mouse.click(x - 50, y - 50);
await page.getByLabel('Switch view').click();

Check failure on line 625 in tests/affine-local/e2e/links.spec.ts

View workflow job for this annotation

GitHub Actions / E2E Test (3)

e2e/links.spec.ts:583:1 › should show edgeless content when switching card view of linked mode doc in edgeless

2) e2e/links.spec.ts:583:1 › should show edgeless content when switching card view of linked mode doc in edgeless TimeoutError: locator.click: Timeout 5000ms exceeded. Call log: - waiting for getByLabel('Switch view') 623 | .waitFor({ state: 'visible' }); 624 | await page.mouse.click(x - 50, y - 50); > 625 | await page.getByLabel('Switch view').click(); | ^ 626 | await page.getByTestId('link-to-embed').click(); 627 | 628 | const viewport = await page at /home/runner/work/AFFiNE/AFFiNE/tests/affine-local/e2e/links.spec.ts:625:40
await page.getByTestId('link-to-embed').click();

const viewport = await page
Expand Down
Loading