fix(typeahead): close() cancels the pending debounce

An abandoned query no longer fires a stale fetch after the dropdown
closed, and loading can't stay stuck on true. Test pins both.

Review round 3: Felix suggestion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-06-11 08:29:25 +02:00
parent b4fcbd7efc
commit 07725cfa39
2 changed files with 15 additions and 0 deletions

View File

@@ -117,6 +117,17 @@ describe('createTypeahead', () => {
expect(ta.loading).toBe(false); 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', () => { it('setActiveIndex updates activeIndex', () => {
const ta = createTypeahead({ fetchUrl: vi.fn().mockResolvedValue([]) }); const ta = createTypeahead({ fetchUrl: vi.fn().mockResolvedValue([]) });
expect(ta.activeIndex).toBe(-1); expect(ta.activeIndex).toBe(-1);

View File

@@ -40,6 +40,10 @@ export function createTypeahead<T>(options: Options<T>) {
function close() { function close() {
isOpen = false; isOpen = false;
activeIndex = -1; 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) { function setActiveIndex(idx: number) {