From 8a8205ad8d23ebbac131ee92573c9bba4c9228e9 Mon Sep 17 00:00:00 2001 From: Marcel Date: Sat, 9 May 2026 10:27:30 +0200 Subject: [PATCH] fix(person-typeahead): add resetKey prop to clear term on navigation reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the user types in the sender/receiver typeahead without selecting a person and then clicks ×-reset (navigating back to /documents), the manually-typed term was not cleared because initialName stayed '' between navigations — the existing $effect tracking initialName never fired. Adding `resetKey` (incremented by the page on every navigation) forces the effect to re-run via `void resetKey`, clearing searchTerm=initialName even when initialName is unchanged. Co-Authored-By: Claude Sonnet 4.6 --- .../src/lib/person/PersonTypeahead.svelte | 7 +++++- .../lib/person/PersonTypeahead.svelte.spec.ts | 24 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/frontend/src/lib/person/PersonTypeahead.svelte b/frontend/src/lib/person/PersonTypeahead.svelte index bff2386c..2114fe38 100644 --- a/frontend/src/lib/person/PersonTypeahead.svelte +++ b/frontend/src/lib/person/PersonTypeahead.svelte @@ -21,6 +21,7 @@ interface Props { restrictToCorrespondentsOf?: string; excludePersonId?: string; badge?: 'additive' | 'replace'; + resetKey?: number; onchange?: (value: string) => void; onfocused?: () => void; } @@ -39,6 +40,7 @@ let { restrictToCorrespondentsOf, excludePersonId, badge, + resetKey = 0, onchange, onfocused }: Props = $props(); @@ -48,8 +50,11 @@ let { // eslint-disable-next-line svelte/prefer-writable-derived let searchTerm = $state(initialName); -// Sync display text when the selected person changes externally (e.g. swap, navigation). +// Sync display text when initialName changes OR when resetKey increments (navigation reset). +// resetKey is incremented by the page on every SvelteKit navigation so that a manually-typed +// term that was never committed (no person selected) gets cleared even if initialName stays ''. $effect(() => { + void resetKey; searchTerm = initialName; }); diff --git a/frontend/src/lib/person/PersonTypeahead.svelte.spec.ts b/frontend/src/lib/person/PersonTypeahead.svelte.spec.ts index 060d0fbd..4df51713 100644 --- a/frontend/src/lib/person/PersonTypeahead.svelte.spec.ts +++ b/frontend/src/lib/person/PersonTypeahead.svelte.spec.ts @@ -270,6 +270,30 @@ describe('PersonTypeahead – correspondent mode', () => { }); }); +// ─── resetKey ───────────────────────────────────────────────────────────────── + +describe('PersonTypeahead – resetKey', () => { + it('clears a manually-typed term when resetKey changes even if initialName stays empty', async () => { + mockFetchWithPersons([]); + const { rerender } = render(PersonTypeahead, { + name: 'senderId', + label: 'Absender', + initialName: '', + resetKey: 0 + }); + const input = page.getByPlaceholder('Namen tippen...'); + + // User types something without selecting a person + await input.fill('Max'); + await waitForDebounce(); + await expect.element(input).toHaveValue('Max'); + + // Navigation resets: initialName stays '', but resetKey increments + await rerender({ name: 'senderId', label: 'Absender', initialName: '', resetKey: 1 }); + await expect.element(input).toHaveValue(''); + }); +}); + // ─── Click outside ──────────────────────────────────────────────────────────── describe('PersonTypeahead – click outside', () => {