From e75448ba1483a3c7a4d7b5a22dd4bb6f1c3183ce Mon Sep 17 00:00:00 2001 From: Marcel Date: Sat, 13 Jun 2026 19:36:21 +0200 Subject: [PATCH] feat(timeline): add WorldBand for HISTORICAL context bands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Full-width muted band; RANGE renders a span pill (1914–1918) with a Zeitraum aria-label (REQ-009); a RANGE with no end degrades to the start year, no pill, no crash (REQ-010). World glyph is a redundant non-color cue with sr-only label (REQ-018); text uses text-ink-2 to hold AA in both themes (REQ-019). Refs #779 Co-Authored-By: Claude Opus 4.8 --- frontend/src/lib/timeline/WorldBand.svelte | 43 +++++++++++++++++ .../src/lib/timeline/WorldBand.svelte.spec.ts | 48 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 frontend/src/lib/timeline/WorldBand.svelte create mode 100644 frontend/src/lib/timeline/WorldBand.svelte.spec.ts diff --git a/frontend/src/lib/timeline/WorldBand.svelte b/frontend/src/lib/timeline/WorldBand.svelte new file mode 100644 index 00000000..92519535 --- /dev/null +++ b/frontend/src/lib/timeline/WorldBand.svelte @@ -0,0 +1,43 @@ + + +
+ + + {config.label} + {entry.title} + + {#if showSpan && fromYear && toYear} + + {fromYear}–{toYear} + + {:else if dateText} + {dateText} + {/if} +
diff --git a/frontend/src/lib/timeline/WorldBand.svelte.spec.ts b/frontend/src/lib/timeline/WorldBand.svelte.spec.ts new file mode 100644 index 00000000..497f51b3 --- /dev/null +++ b/frontend/src/lib/timeline/WorldBand.svelte.spec.ts @@ -0,0 +1,48 @@ +import { describe, it, expect, afterEach } from 'vitest'; +import { cleanup, render } from 'vitest-browser-svelte'; +import WorldBand from './WorldBand.svelte'; +import { makeEntry } from './test-factories'; + +afterEach(() => cleanup()); + +function historical(overrides = {}) { + return makeEntry({ + kind: 'EVENT', + derived: false, + type: 'HISTORICAL', + title: 'Erster Weltkrieg', + senderName: '', + receiverName: '', + precision: 'RANGE', + eventDate: '1914-01-01', + eventDateEnd: '1918-12-31', + documentId: undefined, + ...overrides + }); +} + +describe('WorldBand', () => { + it('renders the historical title with the world glyph + "Weltgeschehen" cue (REQ-018)', () => { + render(WorldBand, { entry: historical() }); + expect(document.body.textContent).toContain('Erster Weltkrieg'); + const hidden = document.querySelector('[aria-hidden="true"]'); + expect(hidden?.textContent).toBe('◍'); + const srOnly = document.querySelector('.sr-only'); + expect(srOnly?.textContent).toBe('Weltgeschehen'); + }); + + it('renders a RANGE span pill 1914–1918 with a Zeitraum aria-label (REQ-009)', () => { + render(WorldBand, { entry: historical() }); + const pill = document.querySelector('[data-testid="world-range"]'); + expect(pill).not.toBeNull(); + expect(pill?.textContent).toContain('1914–1918'); + expect(pill?.getAttribute('aria-label')).toBe('Zeitraum: 1914 bis 1918'); + }); + + it('degrades a RANGE with no end to the start year, no span pill, no crash (REQ-010)', () => { + render(WorldBand, { entry: historical({ eventDateEnd: undefined }) }); + expect(document.querySelector('[data-testid="world-range"]')).toBeNull(); + expect(document.body.textContent).toContain('Erster Weltkrieg'); + expect(document.body.textContent).toContain('1914'); + }); +});