feat(timeline): show curator event note on Zeitstrahl (#844) #883

Merged
marcel merged 6 commits from feat/issue-844-event-note into main 2026-06-16 16:27:54 +02:00
Owner

Summary

Threads the existing TimelineEvent.description field through the list DTO and renders it as an expandable note on /zeitstrahl for curated PERSONAL and HISTORICAL events.

  • REQ-001 description added as nullable 18th field on TimelineEntryDTO; TimelineService.mapEvent() populates it, letters and derived events get null; api.ts regenerated
  • REQ-002 XSS safety via Svelte {...} interpolation — no {@html} (confirmed by grep gate)
  • REQ-003 white-space: pre-line applied as inline style so newlines are preserved
  • REQ-004 EventNote wired into EventPill.svelte (PERSONAL) and WorldBand.svelte (HISTORICAL) below the title line
  • REQ-005 Notes longer than 3 lines are clamped with a "mehr anzeigen" toggle (aria-expanded=false)
  • REQ-006 Short notes render fully with no toggle
  • REQ-007 Toggle expands → collapses with correct aria state and i18n labels
  • REQ-008 null, empty, or blank-only descriptions render nothing

Test plan

  • TimelineServiceTest — 3 new unit tests (REQ-001): description populated, null when absent, null for letters
  • TimelineControllerTest — 1 new slice test: JSON includes description field (REQ-001)
  • event-note.svelte.spec.ts — 8 vitest-browser component tests (REQ-002–REQ-008): all pass
  • e2e/zeitstrahl-note.spec.ts — 2 Playwright tests: seed PERSONAL + HISTORICAL events with description via API, assert note appears under title on /zeitstrahl
  • RTM rows REQ-001–REQ-008 for #844 added, all Done

Closes #844

🤖 Generated with Claude Code

## Summary Threads the existing `TimelineEvent.description` field through the list DTO and renders it as an expandable note on `/zeitstrahl` for curated PERSONAL and HISTORICAL events. - **REQ-001** `description` added as nullable 18th field on `TimelineEntryDTO`; `TimelineService.mapEvent()` populates it, letters and derived events get `null`; `api.ts` regenerated - **REQ-002** XSS safety via Svelte `{...}` interpolation — no `{@html}` (confirmed by grep gate) - **REQ-003** `white-space: pre-line` applied as inline style so newlines are preserved - **REQ-004** `EventNote` wired into `EventPill.svelte` (PERSONAL) and `WorldBand.svelte` (HISTORICAL) below the title line - **REQ-005** Notes longer than 3 lines are clamped with a "mehr anzeigen" toggle (`aria-expanded=false`) - **REQ-006** Short notes render fully with no toggle - **REQ-007** Toggle expands → collapses with correct aria state and i18n labels - **REQ-008** `null`, empty, or blank-only descriptions render nothing ## Test plan - [ ] `TimelineServiceTest` — 3 new unit tests (REQ-001): description populated, null when absent, null for letters - [ ] `TimelineControllerTest` — 1 new slice test: JSON includes `description` field (REQ-001) - [ ] `event-note.svelte.spec.ts` — 8 vitest-browser component tests (REQ-002–REQ-008): all pass - [ ] `e2e/zeitstrahl-note.spec.ts` — 2 Playwright tests: seed PERSONAL + HISTORICAL events with description via API, assert note appears under title on `/zeitstrahl` - [ ] RTM rows REQ-001–REQ-008 for #844 added, all `Done` Closes #844 🤖 Generated with [Claude Code](https://claude.com/claude-code)
marcel added 5 commits 2026-06-16 15:10:16 +02:00
Surfaces the existing TimelineEvent.description through the list DTO so
curated event notes reach the read view. Null for letters and derived
entries; populated from ev.getDescription() in mapEvent() only.

Refs #844
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds timeline_note_show_more / _show_less Paraglide keys (de/en/es) for
the expand/collapse affordance on long event notes. Regenerates api.ts
to include description?: string on TimelineEntryDTO.

Refs #844
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Handles XSS escaping, whitespace-pre-line, 3-line clamp via inline style,
and a toggle button that is only shown when content actually overflows.

Refs #844
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
EventPill (PERSONAL curated events) and WorldBand (HISTORICAL events) now
render the curator note immediately below the title line. Derived events
and letters carry null description so EventNote renders nothing.

Refs #844
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
test(timeline): add Playwright e2e + RTM rows for event note (#844)
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 3m58s
CI / OCR Service Tests (pull_request) Successful in 25s
CI / Backend Unit Tests (pull_request) Successful in 6m21s
CI / fail2ban Regex (pull_request) Failing after 46s
CI / Semgrep Security Scan (pull_request) Successful in 24s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m10s
SDD Gate / RTM Check (pull_request) Successful in 15s
SDD Gate / Contract Validate (pull_request) Successful in 23s
SDD Gate / Constitution Impact (pull_request) Successful in 20s
82979be705
zeitstrahl-note.spec.ts seeds PERSONAL and HISTORICAL events with
descriptions via API and asserts the note appears in DOM (REQ-001, REQ-004).
RTM rows REQ-001–REQ-008 for #844 all marked Done.

Closes #844
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
marcel added 1 commit 2026-06-16 15:42:23 +02:00
fix(timeline): restore justify-center on EventPill outer wrapper
All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 4m9s
CI / OCR Service Tests (pull_request) Successful in 23s
CI / Backend Unit Tests (pull_request) Successful in 5m59s
CI / fail2ban Regex (pull_request) Successful in 47s
CI / Semgrep Security Scan (pull_request) Successful in 23s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m11s
SDD Gate / RTM Check (pull_request) Successful in 17s
SDD Gate / Contract Validate (pull_request) Successful in 22s
SDD Gate / Constitution Impact (pull_request) Successful in 19s
CI / Unit & Component Tests (push) Successful in 5m33s
CI / OCR Service Tests (push) Successful in 28s
CI / Backend Unit Tests (push) Successful in 6m27s
CI / fail2ban Regex (push) Successful in 48s
CI / Semgrep Security Scan (push) Successful in 26s
CI / Compose Bucket Idempotency (push) Successful in 1m6s
3ba5ae982b
YearBand.svelte.spec.ts queries '.justify-center .rounded-full' to assert
the pill is present. Replacing justify-center with items-center broke that
selector; adding both preserves the test contract while keeping flex-col
for the note below the pill.

Refs #844
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
marcel merged commit 3ba5ae982b into main 2026-06-16 16:27:54 +02:00
marcel deleted branch feat/issue-844-event-note 2026-06-16 16:27:54 +02:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#883