fix(persons): prevent stale navigation from clobbering focused search input
All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 2m12s
CI / Backend Unit Tests (pull_request) Successful in 1m58s
CI / E2E Tests (pull_request) Successful in 17m40s
CI / Unit & Component Tests (push) Successful in 1m58s
CI / Backend Unit Tests (push) Successful in 1m59s
CI / E2E Tests (push) Successful in 14m56s

The persons list search input used value={data.q || ''} bound directly to
server data, so every navigation completion would reset it to the URL value
mid-typing, dropping keystrokes just like issue #34 on the home page.

Apply the same focus-guard fix: introduce local `q` state, a `qFocused`
flag, and a guarded $effect that only syncs URL → state when the input is
not focused. Adds a regression test matching the home-page pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit was merged in pull request #44.
This commit is contained in:
Marcel
2026-03-20 21:54:56 +01:00
parent 513a7290b0
commit da0d5495d0
2 changed files with 84 additions and 4 deletions

View File

@@ -1,16 +1,24 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { untrack } from 'svelte';
import { m } from '$lib/paraglide/messages.js';
let { data } = $props();
let q = $state(untrack(() => data.q || ''));
let qFocused = $state(false);
// Sync URL → local state after navigation, but not while the user is typing.
$effect(() => {
if (!qFocused) q = data.q || '';
});
let searchTimeout: ReturnType<typeof setTimeout>;
function handleSearch(e: Event) {
const value = (e.target as HTMLInputElement).value;
function handleSearch() {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => {
goto(`/persons?q=${value}`, { keepFocus: true });
goto(`/persons?q=${q}`, { keepFocus: true });
}, 300);
}
</script>
@@ -49,8 +57,10 @@ function handleSearch(e: Event) {
id="search"
type="text"
placeholder={m.persons_search_placeholder()}
value={data.q || ''}
bind:value={q}
oninput={handleSearch}
onfocus={() => (qFocused = true)}
onblur={() => (qFocused = false)}
class="block w-full rounded-sm border border-gray-300 bg-white py-2.5 pr-10 pl-4 font-sans text-sm text-brand-navy placeholder-gray-400 shadow-sm focus:border-brand-navy focus:ring-1 focus:ring-brand-navy focus:outline-none"
/>
<div