polish(web): search pill aria-pressed, keepPreviousData, plural result count, URL-hydration test

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-04 12:47:38 +02:00
parent 358d793e44
commit 4dd00362b8
5 changed files with 21 additions and 4 deletions
+2 -1
View File
@@ -1,4 +1,4 @@
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { keepPreviousData, useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { api } from "./client"; import { api } from "./client";
import type { components } from "./schema"; import type { components } from "./schema";
@@ -306,6 +306,7 @@ export function useSearch(q: string, visibility: string | null) {
return data; return data;
}, },
placeholderData: keepPreviousData,
getNextPageParam: (lastPage, allPages) => { getNextPageParam: (lastPage, allPages) => {
const loaded = allPages.reduce((n, page) => n + page.hits.length, 0); const loaded = allPages.reduce((n, page) => n + page.hits.length, 0);
+2 -1
View File
@@ -25,7 +25,8 @@
"empty": "No results", "empty": "No results",
"loadError": "Search is unavailable", "loadError": "Search is unavailable",
"loadMore": "Load more", "loadMore": "Load more",
"resultCount": "{{count}} results", "resultCount_one": "{{count}} result",
"resultCount_other": "{{count}} results",
"selectPrompt": "Select a result to see the full record" "selectPrompt": "Select a result to see the full record"
}, },
"publish": { "publish": {
+2 -1
View File
@@ -25,7 +25,8 @@
"empty": "Inga träffar", "empty": "Inga träffar",
"loadError": "Sök är inte tillgängligt", "loadError": "Sök är inte tillgängligt",
"loadMore": "Visa fler", "loadMore": "Visa fler",
"resultCount": "{{count}} träffar", "resultCount_one": "{{count}} träff",
"resultCount_other": "{{count}} träffar",
"selectPrompt": "Välj en träff för att se hela posten" "selectPrompt": "Välj en träff för att se hela posten"
}, },
"publish": { "publish": {
+1
View File
@@ -69,6 +69,7 @@ export function SearchPanel() {
<button <button
key={value} key={value}
type="button" type="button"
aria-pressed={active}
onClick={() => setVisibility(value)} onClick={() => setVisibility(value)}
className={`rounded px-2 py-0.5 ${active ? "bg-indigo-600 text-white" : "border"}`} className={`rounded px-2 py-0.5 ${active ? "bg-indigo-600 text-white" : "border"}`}
> >
+14 -1
View File
@@ -1,5 +1,5 @@
import { expect, test } from "vitest"; import { expect, test } from "vitest";
import { screen, waitFor } from "@testing-library/react"; import { screen, waitFor, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event"; import userEvent from "@testing-library/user-event";
import { http, HttpResponse } from "msw"; import { http, HttpResponse } from "msw";
import { Route, Routes } from "react-router-dom"; import { Route, Routes } from "react-router-dom";
@@ -75,3 +75,16 @@ test("clicking a result shows the object in the detail pane", async () => {
expect(await screen.findByText(amphora.object_name)).toBeInTheDocument(); expect(await screen.findByText(amphora.object_name)).toBeInTheDocument();
}); });
test("hydrates query and visibility from the initial URL", async () => {
renderApp(tree(), { route: "/search?q=bronze" });
expect(screen.getByLabelText(/search the collection/i)).toHaveValue("bronze");
expect(await screen.findByText("Bronze figurine")).toBeInTheDocument();
const { container } = renderApp(tree(), { route: "/search?q=bronze&visibility=internal" });
expect(
within(container).getByRole("button", { name: /^internal$/i }),
).toHaveAttribute("aria-pressed", "true");
});