fix(#248): resolve parent UUID to name in TagParentPicker dropdown subtitle

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-17 07:42:13 +02:00
parent be7009f9ed
commit aff7afa7cb
3 changed files with 49 additions and 2 deletions

View File

@@ -7,14 +7,27 @@ import { createTypeahead } from '$lib/hooks/useTypeahead.svelte';
type Tag = components['schemas']['Tag'];
interface FlatTagRef {
id: string;
name: string;
parentId?: string;
}
interface Props {
name: string;
value?: string;
excludeIds?: string[];
initialName?: string;
allTags?: FlatTagRef[];
}
let { name, value = $bindable(''), excludeIds = [], initialName = '' }: Props = $props();
let {
name,
value = $bindable(''),
excludeIds = [],
initialName = '',
allTags = []
}: Props = $props();
// displayName must be both prop-derived AND locally writable (user typing), so $state +
// $effect is the correct pattern here — writable $derived is read-only and won't work.
@@ -135,7 +148,8 @@ function handleKeydown(e: KeyboardEvent) {
>
<span class="block truncate font-medium">{tag.name}</span>
{#if tag.parentId}
<span class="block truncate text-xs text-ink-3">{tag.parentId}</span>
{@const parentName = allTags.find((t) => t.id === tag.parentId)?.name ?? tag.parentId}
<span class="block truncate text-xs text-ink-3">{parentName}</span>
{/if}
</div>
{/each}

View File

@@ -162,3 +162,35 @@ describe('TagParentPicker ARIA combobox', () => {
expect(option.id).toBe('parentId-option-0');
});
});
// ─── Parent name resolution ───────────────────────────────────────────────────
describe('TagParentPicker parent name subtitle', () => {
it('shows parent name instead of UUID when allTags is provided', async () => {
mockFetchWithTags([{ id: 't2', name: 'Keller', parentId: 't1' }]);
const allTags = [
{ id: 't1', name: 'Haus', documentCount: 5 },
{ id: 't2', name: 'Keller', parentId: 't1', documentCount: 2 }
];
render(TagParentPicker, { name: 'parentId', allTags });
const input = page.getByRole('combobox');
await input.fill('K');
await vi.advanceTimersByTimeAsync(300);
await expect.element(page.getByText('Haus')).toBeInTheDocument();
});
it('shows nothing as subtitle when tag has no parentId', async () => {
mockFetchWithTags([{ id: 't1', name: 'Haus' }]);
const allTags = [{ id: 't1', name: 'Haus', documentCount: 5 }];
render(TagParentPicker, { name: 'parentId', allTags });
const input = page.getByRole('combobox');
await input.fill('H');
await vi.advanceTimersByTimeAsync(300);
// Only the tag name should appear (no subtitle)
await expect.element(page.getByRole('option', { name: 'Haus' })).toBeInTheDocument();
});
});

View File

@@ -131,6 +131,7 @@ const colors = [
bind:value={parentId}
excludeIds={[data.tag.id]}
initialName={parentName}
allTags={data.tags}
/>
</div>