feat(timeline): frame /zeitstrahl in a canvas with a meta line (REQ-001/002)

The timeline now sits inside a bordered, rounded bg-canvas sheet. Below the
heading a sub-line composes the year range, the letter and event counts
(from timelineMeta), and the static "Gruppierung: Datum" — joined by " · "
so the range drops out when there are no year bands and the whole line is
absent for an empty timeline. Semantic tokens only; AA-legible text-xs.

Refs #833
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-06-14 11:00:54 +02:00
parent a1e57ff8cf
commit e4da28d795
2 changed files with 94 additions and 2 deletions

View File

@@ -0,0 +1,66 @@
import { describe, it, expect, afterEach } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import * as m from '$lib/paraglide/messages.js';
import Page from './+page.svelte';
import { makeEntry, makeYear, makeTimelineDTO } from '$lib/timeline/test-factories';
afterEach(() => cleanup());
const event = (title: string) =>
makeEntry({
kind: 'EVENT',
derived: true,
derivedType: 'BIRTH',
title,
senderName: '',
receiverName: '',
documentId: undefined
});
describe('/zeitstrahl page', () => {
it('wraps the timeline in a bordered, rounded canvas frame (REQ-001)', () => {
render(Page, {
data: { timeline: makeTimelineDTO({ years: [makeYear(1909, [makeEntry()])] }) }
});
const canvas = document.querySelector('[data-testid="timeline-canvas"]');
expect(canvas).not.toBeNull();
expect(canvas?.classList.contains('bg-canvas')).toBe(true);
expect(canvas?.classList.contains('border')).toBe(true);
// the timeline renders inside the frame
expect(canvas?.querySelector('ol')).not.toBeNull();
});
it('renders the meta sub-line with range, counts, and grouping (REQ-002)', () => {
const dto = makeTimelineDTO({
years: [
makeYear(1909, [
makeEntry({ documentId: 'a' }),
makeEntry({ documentId: 'b' }),
event('Geburt')
]),
makeYear(1924, [makeEntry({ documentId: 'c' }), event('Tod')])
]
});
render(Page, { data: { timeline: dto } });
const sub = document.querySelector('[data-testid="timeline-meta"]');
expect(sub?.textContent).toContain('19091924');
expect(sub?.textContent).toContain(m.timeline_letters_count({ count: 3 }));
expect(sub?.textContent).toContain(m.timeline_events_count({ count: 2 }));
expect(sub?.textContent).toContain(m.timeline_grouping_date());
});
it('omits the range segment when there are no year bands (REQ-002)', () => {
render(Page, {
data: { timeline: makeTimelineDTO({ undated: [makeEntry({ documentId: 'u1' })] }) }
});
const sub = document.querySelector('[data-testid="timeline-meta"]');
expect(sub).not.toBeNull();
expect(sub?.textContent).not.toContain('');
expect(sub?.textContent).toContain(m.timeline_letters_count({ count: 1 }));
});
it('omits the entire sub-line for an empty timeline (REQ-002)', () => {
render(Page, { data: { timeline: makeTimelineDTO() } });
expect(document.querySelector('[data-testid="timeline-meta"]')).toBeNull();
});
});