mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-14 13:22:37 +00:00
Keep a plugin search term typed while the website page is still loading (#6293)
* test(website): reproduce plugin search dropping a term typed before hydration Typing in the plugin search box before the page's scripts run leaves the term in the box but not in React state, so Enter searches without it. * fix(website): keep a plugin search term typed before the page loads Text typed into the plugin search box before hydration stayed in the box but not in component state, so Enter searched without it and the clear button never appeared. Adopt the box's value once the page hydrates. Closes #6286 * test(website): cover live browse search typed before the page loads --------- Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
This commit is contained in:
@@ -113,6 +113,35 @@ test("keeps the directory's ranking window when searching", async ({ page }) =>
|
||||
);
|
||||
});
|
||||
|
||||
test("searches for a term typed before the page finished loading", async ({ page }) => {
|
||||
const loadScripts = await holdScripts(page);
|
||||
await page.goto("/plugins?window=month", { waitUntil: "domcontentloaded" });
|
||||
await searchPlugins(page, "graphite");
|
||||
await loadScripts();
|
||||
await expect(page.getByRole("button", { name: "Clear search" })).toBeVisible();
|
||||
await page.keyboard.press("Enter");
|
||||
await expect(page).toHaveURL(/\/plugins\/all\?q=graphite&window=month$/);
|
||||
});
|
||||
|
||||
test("filters browse results for a term typed before the page finished loading", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto("/");
|
||||
const loadScripts = await holdScripts(page);
|
||||
await page.goto("/plugins/all", { waitUntil: "domcontentloaded" });
|
||||
const entries = await historyLength(page);
|
||||
await searchPlugins(page, "gra");
|
||||
await loadScripts();
|
||||
await expect(page).toHaveURL(/\/plugins\/all\?q=gra$/);
|
||||
await expect(page.getByRole("heading", { level: 1, name: /^Results for “gra”/ })).toBeVisible();
|
||||
await expect(page.getByRole("link", { name: /Dracula/ })).toHaveCount(0);
|
||||
expect(await historyLength(page)).toBe(entries + 1);
|
||||
|
||||
await page.goBack();
|
||||
await expect(page).toHaveURL(/\/plugins\/all$/);
|
||||
await expect(page.getByRole("heading", { level: 1, name: /^All plugins/ })).toBeVisible();
|
||||
});
|
||||
|
||||
test("clears the search with the clear button", async ({ page }) => {
|
||||
await page.goto("/plugins/all");
|
||||
const searchbox = page.getByRole("searchbox", { name: "Search plugins" });
|
||||
@@ -304,6 +333,20 @@ test.describe("search engine visits without JavaScript", () => {
|
||||
});
|
||||
});
|
||||
|
||||
/** Holds the page's scripts so typing lands before hydration; the returned function loads them. */
|
||||
async function holdScripts(page: Page): Promise<() => Promise<void>> {
|
||||
let release!: () => void;
|
||||
const held = new Promise<void>((resolve) => (release = resolve));
|
||||
await page.route(/\.js($|\?)/, async (route) => {
|
||||
await held;
|
||||
await route.continue();
|
||||
});
|
||||
return async () => {
|
||||
release();
|
||||
await page.waitForLoadState("load");
|
||||
};
|
||||
}
|
||||
|
||||
async function searchPlugins(page: Page, term: string) {
|
||||
await page.getByRole("searchbox", { name: "Search plugins" }).fill(term);
|
||||
}
|
||||
|
||||
@@ -46,6 +46,12 @@ export function PluginSearch({
|
||||
},
|
||||
[live, show, term],
|
||||
);
|
||||
// A term typed before the page hydrated is in the box but not in state; adopt it. Once hydrated,
|
||||
// the box always matches state, so this does nothing.
|
||||
useEffect(() => {
|
||||
const typed = input.current?.value ?? "";
|
||||
if (typed !== term) edit(typed);
|
||||
}, [edit, term]);
|
||||
const handleChange = useCallback(
|
||||
(event: ChangeEvent<HTMLInputElement>) => edit(event.target.value),
|
||||
[edit],
|
||||
|
||||
Reference in New Issue
Block a user