From d9004809200eba24b9f506a823ac51be64cdfd8d Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 16 Apr 2026 16:39:02 +0200 Subject: [PATCH] feat(#221): add parent selector and color picker to admin tag edit form Tag edit form gains a parent + + {#each data.tags.filter((t) => t.id !== data.tag.id) as tag (tag.id)} + + {/each} + + + + + {#if parentId === ''} +
+

Farbe

+
+ {#each colors as colorName (colorName)} + + {/each} + +
+ +
+ {:else} + + {/if} diff --git a/frontend/src/routes/admin/tags/[id]/page.svelte.spec.ts b/frontend/src/routes/admin/tags/[id]/page.svelte.spec.ts index e327f6b8..e6909200 100644 --- a/frontend/src/routes/admin/tags/[id]/page.svelte.spec.ts +++ b/frontend/src/routes/admin/tags/[id]/page.svelte.spec.ts @@ -9,7 +9,10 @@ vi.mock('$app/navigation', () => ({ beforeNavigate: vi.fn(), goto: vi.fn() })); import { beforeNavigate, goto } from '$app/navigation'; const baseTag = { id: 't1', name: 'Familie' }; -const baseData = { tag: baseTag }; +const baseData = { + tag: baseTag, + tags: [] as { id: string; name: string; parentId?: string; color?: string }[] +}; afterEach(cleanup); @@ -91,3 +94,74 @@ describe('Admin edit tag page – unsaved-changes guard', () => { expect(vi.mocked(goto)).toHaveBeenCalledWith('http://localhost/admin/tags/t2'); }); }); + +// ─── Parent selector ────────────────────────────────────────────────────────── + +describe('Admin edit tag page – parent selector', () => { + it('renders a parent selector', async () => { + render(Page, { data: baseData, form: null }); + await expect.element(page.getByRole('combobox', { name: /übergeordnet/i })).toBeInTheDocument(); + }); + + it('shows other tags in the parent selector', async () => { + render(Page, { + data: { + tag: { id: 't1', name: 'Familie' }, + tags: [ + { id: 't1', name: 'Familie' }, + { id: 't2', name: 'Reise' } + ] + }, + form: null + }); + await expect.element(page.getByRole('option', { name: 'Reise' })).toBeInTheDocument(); + }); + + it('does not show self in the parent selector', async () => { + render(Page, { + data: { + tag: { id: 't1', name: 'Familie' }, + tags: [ + { id: 't1', name: 'Familie' }, + { id: 't2', name: 'Reise' } + ] + }, + form: null + }); + const options = document.querySelectorAll('select[name="parentId"] option'); + const values = Array.from(options).map((o) => o.value); + expect(values).not.toContain('t1'); + }); +}); + +// ─── Color picker ───────────────────────────────────────────────────────────── + +describe('Admin edit tag page – color picker', () => { + it('renders color picker when tag has no parent', async () => { + render(Page, { + data: { tag: { id: 't1', name: 'Familie', parentId: undefined }, tags: [] }, + form: null + }); + await expect.element(page.getByTestId('color-picker')).toBeInTheDocument(); + }); + + it('hides color picker when tag already has a parent', async () => { + render(Page, { + data: { + tag: { id: 't1', name: 'Familie', parentId: 't2' }, + tags: [{ id: 't2', name: 'Reise' }] + }, + form: null + }); + await expect.element(page.getByTestId('color-picker')).not.toBeInTheDocument(); + }); + + it('pre-selects the current tag color in the color picker', async () => { + render(Page, { + data: { tag: { id: 't1', name: 'Familie', color: 'sage' }, tags: [] }, + form: null + }); + const selected = page.getByTestId('color-swatch-sage'); + await expect.element(selected).toHaveAttribute('aria-pressed', 'true'); + }); +}); diff --git a/frontend/src/routes/page.svelte.spec.ts b/frontend/src/routes/page.svelte.spec.ts index a31a7e4b..9c1f8890 100644 --- a/frontend/src/routes/page.svelte.spec.ts +++ b/frontend/src/routes/page.svelte.spec.ts @@ -31,7 +31,8 @@ const emptyData = { tags: [], sort: 'DATE' as const, dir: 'desc' as const, - tagQ: '' + tagQ: '', + tagOp: 'AND' }, documents: [], total: 0,