From 53d60a13b268e0957e4513af77b0d8f6dac62f30 Mon Sep 17 00:00:00 2001 From: JimmFly Date: Thu, 9 Feb 2023 06:05:15 +0800 Subject: [PATCH] fix: retrieve lost cursor after closing search (#899) --- .../app/src/components/quick-search/Input.tsx | 48 ++++++++------ .../app/src/components/quick-search/index.tsx | 1 + tests/quick-search.spec.ts | 62 ++++++++++++++----- 3 files changed, 76 insertions(+), 35 deletions(-) diff --git a/packages/app/src/components/quick-search/Input.tsx b/packages/app/src/components/quick-search/Input.tsx index 2a8778da0..0ac61c54c 100644 --- a/packages/app/src/components/quick-search/Input.tsx +++ b/packages/app/src/components/quick-search/Input.tsx @@ -10,27 +10,39 @@ import { StyledInputContent, StyledLabel } from './style'; import { Command } from 'cmdk'; import { useTranslation } from '@affine/i18n'; export const Input = (props: { + open: boolean; query: string; setQuery: Dispatch>; setLoading: Dispatch>; isPublic: boolean; publishWorkspaceName: string | undefined; }) => { + const { open, query, setQuery, setLoading, isPublic, publishWorkspaceName } = + props; const [isComposition, setIsComposition] = useState(false); const [inputValue, setInputValue] = useState(''); const inputRef = useRef(null); const { t } = useTranslation(); useEffect(() => { - inputRef.current?.addEventListener( - 'blur', - () => { - inputRef.current?.focus(); - }, - true - ); - setInputValue(props.query); - return inputRef.current?.focus(); - }, [inputRef, props.query]); + if (open) { + const inputElement = inputRef.current; + return inputElement?.focus(); + } + }, [open]); + useEffect(() => { + const inputElement = inputRef.current; + if (!open) { + return; + } + const handleFocus = () => { + inputElement?.focus(); + }; + inputElement?.addEventListener('blur', handleFocus, true); + return () => inputElement?.removeEventListener('blur', handleFocus, true); + }, [inputRef, open]); + useEffect(() => { + setInputValue(query); + }, [query]); return ( @@ -43,18 +55,18 @@ export const Input = (props: { setIsComposition(true); }} onCompositionEnd={e => { - props.setQuery(e.data); + setQuery(e.data); setIsComposition(false); - if (!props.query) { - props.setLoading(true); + if (!query) { + setLoading(true); } }} onValueChange={str => { setInputValue(str); if (!isComposition) { - props.setQuery(str); - if (!props.query) { - props.setLoading(true); + setQuery(str); + if (!query) { + setLoading(true); } } }} @@ -75,9 +87,9 @@ export const Input = (props: { } }} placeholder={ - props.isPublic + isPublic ? t('Quick search placeholder2', { - workspace: props.publishWorkspaceName, + workspace: publishWorkspaceName, }) : t('Quick search placeholder') } diff --git a/packages/app/src/components/quick-search/index.tsx b/packages/app/src/components/quick-search/index.tsx index 65cfe22c6..97921efca 100644 --- a/packages/app/src/components/quick-search/index.tsx +++ b/packages/app/src/components/quick-search/index.tsx @@ -100,6 +100,7 @@ export const QuickSearch = ({ open, onClose }: TransitionsModalProps) => { > await withCtrlOrMeta(page, () => page.keyboard.press('k', { delay: 50 })); -async function assertTitleTexts(page: Page, texts: string, isSearch?: boolean) { - if (isSearch) { - const actual = await page.evaluate(() => { - const titleElement = ( - document.querySelector('.affine-default-page-block-title') - ); - return titleElement.value; - }); - expect(actual).toEqual(texts); - } else { - const actual = await page.title(); - expect(actual).toEqual(texts); - } +async function assertTitle(page: Page, text: string) { + const locator = page.locator('.affine-default-page-block-title').nth(0); + const actual = await locator.inputValue(); + expect(actual).toBe(text); } async function assertResultList(page: Page, texts: string[]) { const actual = await page.locator('[cmdk-item]').allInnerTexts(); expect(actual).toEqual(texts); } +async function titleIsFocused(page: Page) { + const title = page.locator('.affine-default-page-block-title'); + await expect(title).toBeVisible(); + await expect(title).toBeFocused(); +} test.describe('Open quick search', () => { test('Click slider bar button', async ({ page }) => { @@ -63,7 +59,7 @@ test.describe('Add new page in quick search', () => { const addNewPage = page.locator('[data-testid=quickSearch-addNewPage]'); await addNewPage.click(); await page.waitForTimeout(300); - await assertTitleTexts(page, 'Untitled'); + await assertTitle(page, ''); }); test('Create a new page with keyword', async ({ page }) => { @@ -73,11 +69,18 @@ test.describe('Add new page in quick search', () => { const addNewPage = page.locator('[data-testid=quickSearch-addNewPage]'); await addNewPage.click(); await page.waitForTimeout(300); - await assertTitleTexts(page, 'test123456'); + await assertTitle(page, 'test123456'); }); }); test.describe('Search and select', () => { + test('Enter a keyword to search for', async ({ page }) => { + await newPage(page); + await openQuickSearchByShortcut(page); + await page.keyboard.insertText('test123456'); + const actual = await page.locator('[cmdk-input]').inputValue(); + expect(actual).toBe('test123456'); + }); test('Create a new page and search this page', async ({ page }) => { await newPage(page); await openQuickSearchByShortcut(page); @@ -85,13 +88,13 @@ test.describe('Search and select', () => { const addNewPage = page.locator('[data-testid=quickSearch-addNewPage]'); await addNewPage.click(); await page.waitForTimeout(300); - await assertTitleTexts(page, 'test123456'); + await assertTitle(page, 'test123456'); await openQuickSearchByShortcut(page); await page.keyboard.insertText('test123456'); await assertResultList(page, ['test123456']); await page.keyboard.press('Enter'); await page.waitForTimeout(300); - await assertTitleTexts(page, 'test123456', true); + await assertTitle(page, 'test123456'); }); }); test.describe('Disable search on 404 page', () => { @@ -114,3 +117,28 @@ test.describe('Open quick search on the published page', () => { await expect(publishedSearchResults).toBeVisible({ visible: false }); }); }); + +test.describe('Focus event for quick search', () => { + test('Autofocus input after opening quick search', async ({ page }) => { + await newPage(page); + await openQuickSearchByShortcut(page); + const locator = page.locator('[cmdk-input]'); + await expect(locator).toBeVisible(); + await expect(locator).toBeFocused(); + }); + test('Autofocus input after select', async ({ page }) => { + await newPage(page); + await openQuickSearchByShortcut(page); + await page.keyboard.press('ArrowUp'); + const locator = page.locator('[cmdk-input]'); + await expect(locator).toBeVisible(); + await expect(locator).toBeFocused(); + }); + test('Focus title after creating a new page', async ({ page }) => { + await newPage(page); + await openQuickSearchByShortcut(page); + const addNewPage = page.locator('[data-testid=quickSearch-addNewPage]'); + await addNewPage.click(); + await titleIsFocused(page); + }); +});