From 334b624063c494284c9891e5c965400cce3e43b8 Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 23 Apr 2026 21:52:23 +0200 Subject: [PATCH] feat(briefwechsel): bump row typography and drop relative-year chip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 168px-tall thumbnail tile was dominating rows where the text column only rendered at text-xs / text-sm — visually the right column sat half-empty. Three changes: - Title: text-sm → text-lg - Summary: text-sm → text-base - Meta + tag chips: text-xs → text-sm And remove the "vor N Jahren" chip entirely. The documentDate in the meta row already carries the temporal context and the chip was adding visual noise without new information. Co-Authored-By: Claude Opus 4.7 --- .../src/lib/components/TagChipList.svelte | 4 +- .../src/lib/components/ThumbnailRow.svelte | 23 ++----- .../components/ThumbnailRow.svelte.spec.ts | 68 ++++++++----------- 3 files changed, 37 insertions(+), 58 deletions(-) diff --git a/frontend/src/lib/components/TagChipList.svelte b/frontend/src/lib/components/TagChipList.svelte index 92a5df9c..9c974cea 100644 --- a/frontend/src/lib/components/TagChipList.svelte +++ b/frontend/src/lib/components/TagChipList.svelte @@ -12,12 +12,12 @@ const hiddenTagCount = $derived(Math.max(0, tags.length - max)); {#each displayedTags as tag (tag.id)} {tag.name} {/each} {#if hiddenTagCount > 0} - +{hiddenTagCount} + +{hiddenTagCount} {/if} {/if} diff --git a/frontend/src/lib/components/ThumbnailRow.svelte b/frontend/src/lib/components/ThumbnailRow.svelte index f5770211..f7eccac4 100644 --- a/frontend/src/lib/components/ThumbnailRow.svelte +++ b/frontend/src/lib/components/ThumbnailRow.svelte @@ -2,7 +2,6 @@ import ConversationThumbnail from '$lib/components/ConversationThumbnail.svelte'; import TagChipList from '$lib/components/TagChipList.svelte'; import { formatDate } from '$lib/utils/date'; -import { relativeYearsDe } from '$lib/relativeTime'; import * as m from '$lib/paraglide/messages.js'; type Person = { id: string; firstName?: string | null; lastName: string; displayName: string }; @@ -28,13 +27,11 @@ type Doc = { let { doc, isOut, - showOtherParty, - now = new Date() + showOtherParty }: { doc: Doc; isOut: boolean; showOtherParty: boolean; - now?: Date; } = $props(); const title = $derived(doc.title || doc.originalFilename); @@ -45,9 +42,6 @@ const otherPartyName = $derived( : (doc.sender?.displayName ?? '') : '' ); -const relativeYearLabel = $derived( - doc.documentDate ? relativeYearsDe(new Date(doc.documentDate + 'T12:00:00'), now) : '' -); const directionLabel = $derived(isOut ? m.row_direction_sent() : m.row_direction_received()); const ariaLabel = $derived( `${directionLabel}: ${title}${doc.documentDate ? `, ${formatDate(doc.documentDate)}` : ''}` @@ -63,23 +57,18 @@ const ariaLabel = $derived( > -
-
-
- {title} -
- {#if relativeYearLabel} -
{relativeYearLabel}
- {/if} +
+
+ {title}
{#if doc.summary} -
+
“{doc.summary}”
{/if} -
+
{doc.documentDate ? formatDate(doc.documentDate) : '—'} {#if doc.location} · diff --git a/frontend/src/lib/components/ThumbnailRow.svelte.spec.ts b/frontend/src/lib/components/ThumbnailRow.svelte.spec.ts index e62b45ea..97130ad6 100644 --- a/frontend/src/lib/components/ThumbnailRow.svelte.spec.ts +++ b/frontend/src/lib/components/ThumbnailRow.svelte.spec.ts @@ -36,8 +36,7 @@ describe('ThumbnailRow', () => { render(ThumbnailRow, { doc: baseDoc, isOut: true, - showOtherParty: false, - now: new Date('2026-06-01T00:00:00Z') + showOtherParty: false }); expect(document.body.textContent).toContain('Liebe Anna'); @@ -49,8 +48,7 @@ describe('ThumbnailRow', () => { render(ThumbnailRow, { doc: { ...baseDoc, title: '' }, isOut: true, - showOtherParty: false, - now: new Date('2026-06-01T00:00:00Z') + showOtherParty: false }); expect(document.body.textContent).toContain('liebe_anna.pdf'); @@ -60,8 +58,7 @@ describe('ThumbnailRow', () => { render(ThumbnailRow, { doc: baseDoc, isOut: true, - showOtherParty: true, - now: new Date('2026-06-01T00:00:00Z') + showOtherParty: true }); // Out-going from Hans, other party is first receiver (Anna Schmidt) @@ -72,8 +69,7 @@ describe('ThumbnailRow', () => { render(ThumbnailRow, { doc: baseDoc, isOut: false, - showOtherParty: false, - now: new Date('2026-06-01T00:00:00Z') + showOtherParty: false }); // Anna is the receiver; in a bilateral list we suppress party names. @@ -84,8 +80,7 @@ describe('ThumbnailRow', () => { render(ThumbnailRow, { doc: baseDoc, isOut: true, - showOtherParty: false, - now: new Date('2026-06-01T00:00:00Z') + showOtherParty: false }); const chips = document.querySelectorAll('[data-testid="thumb-row-tag"]'); @@ -93,38 +88,38 @@ describe('ThumbnailRow', () => { expect(document.body.textContent).toMatch(/\+2/); }); - it('renders relative-year label derived from documentDate', () => { + it('does not render a relative-year label', () => { + // Document date is historical; we deliberately omit the "vor N Jahren" + // chip so the row can give vertical space to the title + summary. render(ThumbnailRow, { doc: baseDoc, isOut: true, - showOtherParty: false, - now: new Date('2026-06-01T00:00:00Z') - }); - - // 1950-06-01 → 2026-06-01 = 76 years - expect(document.body.textContent).toContain('vor 76 Jahren'); - }); - - it('hides the relative-year label when documentDate is in the future', () => { - // relativeYearsDe returns "" for future/invalid dates; the row must not - // then render an empty chip or print "vor 0 Jahren". - render(ThumbnailRow, { - doc: { ...baseDoc, documentDate: '2030-01-01' }, - isOut: true, - showOtherParty: false, - now: new Date('2026-06-01T00:00:00Z') + showOtherParty: false }); expect(document.body.textContent).not.toMatch(/vor \d+ Jahr/); expect(document.body.textContent).not.toMatch(/vor weniger/); }); + it('renders the title at text-lg so the row uses its full vertical space', () => { + render(ThumbnailRow, { + doc: baseDoc, + isOut: true, + showOtherParty: false + }); + + const titleEl = [...document.querySelectorAll('div')].find( + (el) => el.textContent?.trim() === 'Liebe Anna' + ) as HTMLElement | undefined; + expect(titleEl, 'title element not found').toBeDefined(); + expect(titleEl!.className).toContain('text-lg'); + }); + it('sets border-l class based on isOut', () => { const { unmount } = render(ThumbnailRow, { doc: baseDoc, isOut: true, - showOtherParty: false, - now: new Date('2026-06-01T00:00:00Z') + showOtherParty: false }); let link = document.querySelector('a[href="/documents/d1"]') as HTMLElement; @@ -135,8 +130,7 @@ describe('ThumbnailRow', () => { render(ThumbnailRow, { doc: baseDoc, isOut: false, - showOtherParty: false, - now: new Date('2026-06-01T00:00:00Z') + showOtherParty: false }); link = document.querySelector('a[href="/documents/d1"]') as HTMLElement; expect(link.className).toContain('border-l-accent'); @@ -146,8 +140,7 @@ describe('ThumbnailRow', () => { render(ThumbnailRow, { doc: baseDoc, isOut: true, - showOtherParty: false, - now: new Date('2026-06-01T00:00:00Z') + showOtherParty: false }); const link = document.querySelector('a[href="/documents/d1"]') as HTMLElement; @@ -163,8 +156,7 @@ describe('ThumbnailRow', () => { render(ThumbnailRow, { doc: baseDoc, isOut: false, - showOtherParty: false, - now: new Date('2026-06-01T00:00:00Z') + showOtherParty: false }); const link = document.querySelector('a[href="/documents/d1"]') as HTMLElement; @@ -179,8 +171,7 @@ describe('ThumbnailRow', () => { summary: 'safe text' }, isOut: true, - showOtherParty: false, - now: new Date('2026-06-01T00:00:00Z') + showOtherParty: false }); // No real img tag from the summary, the ConversationThumbnail img is fine. @@ -199,8 +190,7 @@ describe('ThumbnailRow', () => { thumbnailAspect: 'PORTRAIT' }, isOut: true, - showOtherParty: false, - now: new Date('2026-06-01T00:00:00Z') + showOtherParty: false }); expect(document.body.textContent).toContain('Ohne Datum');