From 023b6ddb49a9a8acf3cefd9ec9f005079ba75102 Mon Sep 17 00:00:00 2001 From: Marcel Date: Mon, 6 Apr 2026 15:46:54 +0200 Subject: [PATCH] fix(search): tagQ alone now triggers search mode; selecting chip clears tagQ - isDashboard was ignoring tagQ so typing in tag filter showed dashboard - addTag now calls onTextInput('') to clear tagQ when a chip is selected Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/lib/components/TagInput.svelte | 1 + .../lib/components/TagInput.svelte.spec.ts | 12 +++++++++++ frontend/src/routes/+page.server.ts | 2 +- frontend/src/routes/page.server.spec.ts | 21 +++++++++++++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/frontend/src/lib/components/TagInput.svelte b/frontend/src/lib/components/TagInput.svelte index 79aef870..78eaeca8 100644 --- a/frontend/src/lib/components/TagInput.svelte +++ b/frontend/src/lib/components/TagInput.svelte @@ -44,6 +44,7 @@ function addTag(tag: string) { suggestions = []; showSuggestions = false; activeIndex = -1; + onTextInput?.(''); } function removeTag(index: number) { diff --git a/frontend/src/lib/components/TagInput.svelte.spec.ts b/frontend/src/lib/components/TagInput.svelte.spec.ts index 9456d06e..9e00915d 100644 --- a/frontend/src/lib/components/TagInput.svelte.spec.ts +++ b/frontend/src/lib/components/TagInput.svelte.spec.ts @@ -228,4 +228,16 @@ describe('TagInput – onTextInput callback', () => { const input = page.getByRole('textbox'); await expect(input.fill('fa')).resolves.not.toThrow(); }); + + it('calls onTextInput with empty string when a tag chip is added', async () => { + mockFetchWithTags(['Kaufvertrag']); + const onTextInput = vi.fn(); + render(TagInput, { tags: [], allowCreation: false, onTextInput }); + const input = page.getByRole('textbox'); + await input.fill('Ka'); + await waitForDebounce(); + const option = page.getByRole('option', { name: 'Kaufvertrag' }); + await option.click(); + await expect.poll(() => onTextInput.mock.calls.at(-1)).toEqual(['']); + }); }); diff --git a/frontend/src/routes/+page.server.ts b/frontend/src/routes/+page.server.ts index 7aa08827..380c7e3b 100644 --- a/frontend/src/routes/+page.server.ts +++ b/frontend/src/routes/+page.server.ts @@ -17,7 +17,7 @@ export async function load({ url, fetch }) { const dir = url.searchParams.get('dir') || 'desc'; const tagQ = url.searchParams.get('tagQ') || ''; - const isDashboard = !q && !from && !to && !senderId && !receiverId && !tags.length; + const isDashboard = !q && !from && !to && !senderId && !receiverId && !tags.length && !tagQ; const api = createApiClient(fetch); diff --git a/frontend/src/routes/page.server.spec.ts b/frontend/src/routes/page.server.spec.ts index f8af2b92..3e8f2dcb 100644 --- a/frontend/src/routes/page.server.spec.ts +++ b/frontend/src/routes/page.server.spec.ts @@ -167,6 +167,27 @@ describe('home page load — search mode', () => { expect(firstCall[1].params.query.q).toBe('Urlaub'); expect(firstCall[1].params.query.from).toBe('2020-01-01'); }); + + it('sets isDashboard false when only tagQ is set', async () => { + const mockGet = vi + .fn() + .mockResolvedValueOnce({ + response: { ok: true, status: 200 }, + data: { documents: [{ id: 'd1' }], total: 1 } + }) // search docs + .mockResolvedValueOnce({ response: { ok: true, status: 200 }, data: [] }); // persons + vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType< + typeof createApiClient + >); + + const result = await load({ + url: makeUrl({ tagQ: 'fam' }), + fetch: vi.fn() as unknown as typeof fetch + }); + + expect(result.isDashboard).toBe(false); + expect(result.documents).toHaveLength(1); + }); }); it('passes sort, dir, and tagQ params to the documents API', async () => {