feat(search): add onTextInput callback to TagInput for live tag filter

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-06 13:42:08 +02:00
parent aeed6e0dac
commit eeb78c98ec
2 changed files with 24 additions and 2 deletions

View File

@@ -6,9 +6,10 @@ import { clickOutside } from '$lib/actions/clickOutside';
interface Props {
tags?: string[];
allowCreation?: boolean;
onTextInput?: (text: string) => void;
}
let { tags = $bindable([]), allowCreation = true }: Props = $props();
let { tags = $bindable([]), allowCreation = true, onTextInput }: Props = $props();
let inputVal = $state('');
let suggestions: string[] = $state([]);
@@ -101,7 +102,7 @@ function handleKeydown(e: KeyboardEvent) {
<input
type="text"
bind:value={inputVal}
oninput={() => fetchSuggestions(inputVal)}
oninput={() => { fetchSuggestions(inputVal); onTextInput?.(inputVal); }}
onkeydown={handleKeydown}
onfocus={() => fetchSuggestions(inputVal)}
placeholder={tags.length === 0

View File

@@ -208,3 +208,24 @@ describe('TagInput autocomplete', () => {
await expect.element(page.getByRole('option', { name: 'Familie' })).not.toBeInTheDocument();
});
});
// ─── onTextInput callback ──────────────────────────────────────────────────────
describe('TagInput onTextInput callback', () => {
it('calls onTextInput with the current value on every input event', async () => {
mockFetchEmpty();
const onTextInput = vi.fn();
render(TagInput, { tags: [], allowCreation: false, onTextInput });
const input = page.getByRole('textbox');
await input.fill('fa');
await expect.poll(() => onTextInput.mock.calls.length).toBeGreaterThan(0);
expect(onTextInput).toHaveBeenCalledWith('fa');
});
it('does not throw when onTextInput is not provided', async () => {
mockFetchEmpty();
render(TagInput, { tags: [], allowCreation: false });
const input = page.getByRole('textbox');
await expect(input.fill('fa')).resolves.not.toThrow();
});
});