diff --git a/frontend/src/lib/shared/discussion/MentionDropdown.svelte b/frontend/src/lib/shared/discussion/MentionDropdown.svelte index 4932eda4..176b144b 100644 --- a/frontend/src/lib/shared/discussion/MentionDropdown.svelte +++ b/frontend/src/lib/shared/discussion/MentionDropdown.svelte @@ -2,6 +2,7 @@ import type { components } from '$lib/generated/api'; // eslint-disable-next-line boundaries/dependencies -- mention dropdown needs person date formatting; extract to shared if it becomes reusable import { formatLifeDateRange } from '$lib/person/personLifeDates'; +import { untrack } from 'svelte'; import { m } from '$lib/paraglide/messages.js'; type Person = components['schemas']['Person']; @@ -17,7 +18,19 @@ type DropdownState = { clientRect: (() => DOMRect | null) | null; }; -let { model }: { model: DropdownState } = $props(); +let { + model, + initialQuery = '', + onSearch = () => {} +}: { + model: DropdownState; + initialQuery?: string; + onSearch?: (query: string) => void; +} = $props(); + +// initialQuery is a one-shot prop — PersonMentionEditor mounts a fresh dropdown +// with the typed text on each Tiptap onStart, so we deliberately snapshot here. +let searchQuery = $state(untrack(() => initialQuery)); // highlightedIndex must be both writable (keyboard handler mutates it) and // reset when `items` changes (so it never points past the end of a new list). @@ -119,6 +132,31 @@ function selectItem(item: Person) { style:bottom={position.bottom} style:left={position.left} > +
{m.person_mention_popup_empty()} diff --git a/frontend/src/lib/shared/discussion/MentionDropdown.svelte.spec.ts b/frontend/src/lib/shared/discussion/MentionDropdown.svelte.spec.ts new file mode 100644 index 00000000..907741f7 --- /dev/null +++ b/frontend/src/lib/shared/discussion/MentionDropdown.svelte.spec.ts @@ -0,0 +1,44 @@ +/** + * MentionDropdown — direct component tests. + * + * These tests render the dropdown in isolation, passing the `model` proxy + * (matching what PersonMentionEditor would pass). They cover the dropdown's + * own surface: the search input, the empty-query prompt, and the existing + * "no results" / "create new" behaviors. Wiring tests against Tiptap live + * in PersonMentionEditor.svelte.spec.ts. + */ +import { describe, it, expect, afterEach } from 'vitest'; +import { cleanup, render } from 'vitest-browser-svelte'; +import { page } from 'vitest/browser'; +import MentionDropdown from './MentionDropdown.svelte'; +import type { components } from '$lib/generated/api'; + +type Person = components['schemas']['Person']; + +type DropdownState = { + items: Person[]; + command: (item: Person) => void; + clientRect: (() => DOMRect | null) | null; +}; + +function makeModel(items: Person[] = []): DropdownState { + return { + items, + command: () => {}, + clientRect: () => new DOMRect(0, 0, 0, 0) + }; +} + +afterEach(() => cleanup()); + +describe('MentionDropdown — search input', () => { + it('renders a search input pre-filled with the initialQuery prop', async () => { + render(MentionDropdown, { + model: makeModel(), + initialQuery: 'WdG', + onSearch: () => {} + }); + + await expect.element(page.getByRole('searchbox')).toHaveValue('WdG'); + }); +});