From 3b3b551d841049dc3e58df2964fe76e9fc6b7712 Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 23 Apr 2026 20:43:04 +0200 Subject: [PATCH] refactor(briefwechsel): extract TagChipList from ThumbnailRow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lifts the three-chip-plus-"+N" tag row out of ThumbnailRow into a standalone TagChipList component so the chip cap + overflow policy lives in one place and can be reused on other surfaces (document detail header is a candidate). ThumbnailRow drops from 110 to ~90 lines and no longer owns tag-slicing logic — it just asks for the list with max=3. Behavior is byte-identical: same data-testid, same max cap, same "+N" overflow indicator. All ThumbnailRow row-level tag tests continue to pass against the new composition. Refs #305 Fixes @felixbrandt suggestion 1 from PR review Co-Authored-By: Claude Sonnet 4.6 --- .../src/lib/components/TagChipList.svelte | 23 +++++++++++++ .../lib/components/TagChipList.svelte.spec.ts | 34 +++++++++++++++++++ .../src/lib/components/ThumbnailRow.svelte | 18 ++-------- 3 files changed, 59 insertions(+), 16 deletions(-) create mode 100644 frontend/src/lib/components/TagChipList.svelte create mode 100644 frontend/src/lib/components/TagChipList.svelte.spec.ts diff --git a/frontend/src/lib/components/TagChipList.svelte b/frontend/src/lib/components/TagChipList.svelte new file mode 100644 index 00000000..05851250 --- /dev/null +++ b/frontend/src/lib/components/TagChipList.svelte @@ -0,0 +1,23 @@ + + +{#if tags.length > 0} +
+ {#each displayedTags as tag (tag.id)} + {tag.name} + {/each} + {#if hiddenTagCount > 0} + +{hiddenTagCount} + {/if} +
+{/if} diff --git a/frontend/src/lib/components/TagChipList.svelte.spec.ts b/frontend/src/lib/components/TagChipList.svelte.spec.ts new file mode 100644 index 00000000..14eb2a14 --- /dev/null +++ b/frontend/src/lib/components/TagChipList.svelte.spec.ts @@ -0,0 +1,34 @@ +import { describe, it, expect, afterEach } from 'vitest'; +import { cleanup, render } from 'vitest-browser-svelte'; + +import TagChipList from './TagChipList.svelte'; + +afterEach(() => { + cleanup(); +}); + +const makeTags = (n: number) => + Array.from({ length: n }, (_, i) => ({ id: `t${i}`, name: `Tag${i}` })); + +describe('TagChipList', () => { + it('renders all tags as chips when under the cap', () => { + render(TagChipList, { tags: makeTags(2), max: 3 }); + const chips = document.querySelectorAll('[data-testid="thumb-row-tag"]'); + expect(chips).toHaveLength(2); + expect(document.body.textContent).not.toMatch(/\+/); + }); + + it('caps visible chips at max and renders +N for the remainder', () => { + render(TagChipList, { tags: makeTags(5), max: 3 }); + const chips = document.querySelectorAll('[data-testid="thumb-row-tag"]'); + expect(chips).toHaveLength(3); + expect(document.body.textContent).toMatch(/\+2/); + }); + + it('renders nothing when tags is empty', () => { + render(TagChipList, { tags: [], max: 3 }); + const chips = document.querySelectorAll('[data-testid="thumb-row-tag"]'); + expect(chips).toHaveLength(0); + expect(document.body.textContent).not.toMatch(/\+/); + }); +}); diff --git a/frontend/src/lib/components/ThumbnailRow.svelte b/frontend/src/lib/components/ThumbnailRow.svelte index 700550a7..f605b0c4 100644 --- a/frontend/src/lib/components/ThumbnailRow.svelte +++ b/frontend/src/lib/components/ThumbnailRow.svelte @@ -1,5 +1,6 @@