diff --git a/frontend/src/lib/shared/hooks/useTypeahead.svelte.test.ts b/frontend/src/lib/shared/hooks/useTypeahead.svelte.test.ts index 89794289..b0b47afa 100644 --- a/frontend/src/lib/shared/hooks/useTypeahead.svelte.test.ts +++ b/frontend/src/lib/shared/hooks/useTypeahead.svelte.test.ts @@ -117,6 +117,17 @@ describe('createTypeahead', () => { expect(ta.loading).toBe(false); }); + it('close() cancels the pending debounce — no stale fetch fires, loading resets', async () => { + const fetchUrl = vi.fn().mockResolvedValue([{ id: '1' }]); + const ta = createTypeahead({ fetchUrl, debounceMs: 300 }); + ta.setQuery('foo'); + expect(ta.loading).toBe(true); + ta.close(); + expect(ta.loading).toBe(false); + await vi.advanceTimersByTimeAsync(300); + expect(fetchUrl).not.toHaveBeenCalled(); + }); + it('setActiveIndex updates activeIndex', () => { const ta = createTypeahead({ fetchUrl: vi.fn().mockResolvedValue([]) }); expect(ta.activeIndex).toBe(-1); diff --git a/frontend/src/lib/shared/hooks/useTypeahead.svelte.ts b/frontend/src/lib/shared/hooks/useTypeahead.svelte.ts index 4f51abbe..f970cd29 100644 --- a/frontend/src/lib/shared/hooks/useTypeahead.svelte.ts +++ b/frontend/src/lib/shared/hooks/useTypeahead.svelte.ts @@ -40,6 +40,10 @@ export function createTypeahead(options: Options) { function close() { isOpen = false; activeIndex = -1; + // Cancel the pending debounce — an abandoned query must not fire a stale + // fetch after the dropdown closed, nor leave loading stuck on true. + clearTimeout(debounceTimer); + loading = false; } function setActiveIndex(idx: number) {