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:
@@ -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}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -131,6 +131,7 @@ const colors = [
|
||||
bind:value={parentId}
|
||||
excludeIds={[data.tag.id]}
|
||||
initialName={parentName}
|
||||
allTags={data.tags}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user