diff --git a/argos/tests/showcase.spec.ts b/argos/tests/showcase.spec.ts new file mode 100644 index 000000000000..f1fcd952f90a --- /dev/null +++ b/argos/tests/showcase.spec.ts @@ -0,0 +1,69 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {test, expect} from '@playwright/test'; + +// cSpell:ignore zhong + +test.beforeEach(async ({page}) => { + await page.goto('http://localhost:3000/showcase?name=React&tags=opensource'); + await expect(page.locator('html')).toHaveAttribute( + 'data-has-hydrated', + 'true', + ); +}); + +test('showcase search commits input method text after composition ends', async ({ + page, +}) => { + const input = page.getByPlaceholder('Search for site name...'); + await expect(input).toHaveValue('React'); + await input.selectText(); + + const session = await page.context().newCDPSession(page); + try { + await session.send('Input.imeSetComposition', { + text: 'zhong', + selectionStart: 0, + selectionEnd: 5, + }); + + await expect(input).toHaveValue('zhong'); + await expect(page).toHaveURL(/\?name=React&tags=opensource$/); + await expect(input).toBeFocused(); + + await session.send('Input.insertText', {text: '中文'}); + + await expect(input).toHaveValue('中文'); + await expect(page).toHaveURL(/\?name=%E4%B8%AD%E6%96%87&tags=opensource$/); + await expect(input).toBeFocused(); + } finally { + await session.detach(); + } +}); + +test('showcase search updates ordinary input and clearing immediately', async ({ + page, +}) => { + const input = page.getByPlaceholder('Search for site name...'); + await input.fill('Docusaurus'); + await expect(page).toHaveURL(/\?name=Docusaurus&tags=opensource$/); + + await input.fill(''); + await expect(page).toHaveURL(/\?tags=opensource$/); + await expect(input).toHaveValue(''); +}); + +test('showcase search follows an external filter reset', async ({page}) => { + const input = page.getByPlaceholder('Search for site name...'); + await expect(input).toHaveValue('React'); + + await page.getByRole('button', {name: 'Clear All', exact: true}).click(); + + await expect(page).toHaveURL('http://localhost:3000/showcase'); + await expect(input).toHaveValue(''); +}); diff --git a/website/src/pages/showcase/_components/ShowcaseSearchBar/index.tsx b/website/src/pages/showcase/_components/ShowcaseSearchBar/index.tsx index 87b62b8ce39b..b0e4ddaf983b 100644 --- a/website/src/pages/showcase/_components/ShowcaseSearchBar/index.tsx +++ b/website/src/pages/showcase/_components/ShowcaseSearchBar/index.tsx @@ -5,13 +5,14 @@ * LICENSE file in the root directory of this source tree. */ -import {type ReactNode} from 'react'; +import {type ReactNode, useState} from 'react'; import {translate} from '@docusaurus/Translate'; import {useSearchName} from '@site/src/pages/showcase/_utils'; import styles from './styles.module.css'; export default function ShowcaseSearchBar(): ReactNode { const [searchName, setSearchName] = useSearchName(); + const [compositionValue, setCompositionValue] = useState(null); return (
{ + value={compositionValue ?? searchName} + onCompositionStart={(e) => { + setCompositionValue(e.currentTarget.value); + }} + onCompositionEnd={(e) => { + setCompositionValue(null); setSearchName(e.currentTarget.value); }} + onInput={(e) => { + if (compositionValue !== null) { + setCompositionValue(e.currentTarget.value); + } else { + setSearchName(e.currentTarget.value); + } + }} />
);