fix(search): prevent stale navigation from clobbering focused search input #41

Merged
marcel merged 1 commits from fix/34-url-synced-input-keystroke-loss into main 2026-03-20 21:06:28 +01:00
2 changed files with 27 additions and 2 deletions

View File

@@ -11,6 +11,7 @@ import { formatDate } from '$lib/utils/date';
let { data } = $props();
let q = $state(untrack(() => data.filters?.q || ''));
let qFocused = $state(false);
let from = $state(untrack(() => data.filters?.from || ''));
let to = $state(untrack(() => data.filters?.to || ''));
let senderId = $state(untrack(() => data.filters?.senderId || ''));
@@ -61,9 +62,10 @@ $effect(() => {
}
});
// Sync local state with server data after navigation
// Sync local state with server data after navigation.
// Guard q: skip overwrite while the user is actively typing in the search field.
$effect(() => {
q = data.filters?.q || '';
if (!qFocused) q = data.filters?.q || '';
from = data.filters?.from || '';
to = data.filters?.to || '';
senderId = data.filters?.senderId || '';
@@ -85,6 +87,8 @@ $effect(() => {
type="text"
bind:value={q}
oninput={handleTextSearch}
onfocus={() => (qFocused = true)}
onblur={() => (qFocused = false)}
placeholder={m.docs_search_placeholder()}
class="block w-full border-gray-300 py-2.5 pr-10 pl-3 placeholder-gray-400 shadow-sm focus:border-brand-navy focus:ring-brand-navy"
/>

View File

@@ -164,6 +164,27 @@ describe('Home page document list', () => {
});
});
// ─── Keystroke preservation (issue #34) ──────────────────────────────────────
describe('Home page search input keystroke preservation', () => {
it('does not overwrite the search input while the user is focused and stale data arrives', async () => {
const { rerender } = render(Page, { data: emptyData });
const input = page.getByPlaceholder('Suche in Titel, Inhalt, Ort...');
// User types "abc" — input is focused
await input.click();
await input.fill('abc');
// Simulate a navigation completing with stale data (q='a') while the user is still typing
await rerender({ data: { ...emptyData, filters: { ...emptyData.filters, q: 'a' } } });
await tick();
// Input must still show what the user typed, not the stale URL value
await expect.element(input).toHaveValue('abc');
});
});
// ─── Error state ──────────────────────────────────────────────────────────────
describe('Home page error state', () => {