feat(timeline): add eventCardConfig accent matrix + DTO test factories
getAccentConfig(entry) maps each EVENT to its glyph (* / † / ⚭ / ★ / ◍), German redundant-cue label, and accent kind (REQ-007/008/018). test-factories build TimelineEntryDTO/TimelineDTO mirroring the real wire shape for component specs. Refs #779 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
53
frontend/src/lib/timeline/eventCardConfig.spec.ts
Normal file
53
frontend/src/lib/timeline/eventCardConfig.spec.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { getAccentConfig } from './eventCardConfig';
|
||||||
|
import type { components } from '$lib/generated/api';
|
||||||
|
|
||||||
|
type TimelineEntryDTO = components['schemas']['TimelineEntryDTO'];
|
||||||
|
|
||||||
|
function event(overrides: Partial<TimelineEntryDTO>): TimelineEntryDTO {
|
||||||
|
return {
|
||||||
|
kind: 'EVENT',
|
||||||
|
precision: 'YEAR',
|
||||||
|
derived: false,
|
||||||
|
senderName: '',
|
||||||
|
receiverName: '',
|
||||||
|
...overrides
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('getAccentConfig', () => {
|
||||||
|
it('maps a derived birth to the * glyph and "Geburt"', () => {
|
||||||
|
const cfg = getAccentConfig(event({ derived: true, derivedType: 'BIRTH' }));
|
||||||
|
expect(cfg.glyph).toBe('*');
|
||||||
|
expect(cfg.label).toBe('Geburt');
|
||||||
|
expect(cfg.accent).toBe('derived');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('maps a derived death to the † glyph and "Tod"', () => {
|
||||||
|
const cfg = getAccentConfig(event({ derived: true, derivedType: 'DEATH' }));
|
||||||
|
expect(cfg.glyph).toBe('†');
|
||||||
|
expect(cfg.label).toBe('Tod');
|
||||||
|
expect(cfg.accent).toBe('derived');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('maps a derived marriage to the ⚭ glyph and "Heirat"', () => {
|
||||||
|
const cfg = getAccentConfig(event({ derived: true, derivedType: 'MARRIAGE' }));
|
||||||
|
expect(cfg.glyph).toBe('⚭');
|
||||||
|
expect(cfg.label).toBe('Heirat');
|
||||||
|
expect(cfg.accent).toBe('derived');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('maps a HISTORICAL event to the world glyph and "Weltgeschehen"', () => {
|
||||||
|
const cfg = getAccentConfig(event({ type: 'HISTORICAL' }));
|
||||||
|
expect(cfg.glyph).toBe('◍');
|
||||||
|
expect(cfg.label).toBe('Weltgeschehen');
|
||||||
|
expect(cfg.accent).toBe('historical');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('maps a curated PERSONAL event to the ★ glyph and "Familie"', () => {
|
||||||
|
const cfg = getAccentConfig(event({ type: 'PERSONAL', eventId: 'e-1' }));
|
||||||
|
expect(cfg.glyph).toBe('★');
|
||||||
|
expect(cfg.label).toBe('Familie');
|
||||||
|
expect(cfg.accent).toBe('curated');
|
||||||
|
});
|
||||||
|
});
|
||||||
38
frontend/src/lib/timeline/eventCardConfig.ts
Normal file
38
frontend/src/lib/timeline/eventCardConfig.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import * as m from '$lib/paraglide/messages.js';
|
||||||
|
import type { components } from '$lib/generated/api';
|
||||||
|
|
||||||
|
type TimelineEntryDTO = components['schemas']['TimelineEntryDTO'];
|
||||||
|
|
||||||
|
/** Styling discriminant for an axis pill/band. */
|
||||||
|
export type TimelineAccent = 'derived' | 'curated' | 'historical';
|
||||||
|
|
||||||
|
export interface AccentConfig {
|
||||||
|
/** Visible Unicode glyph — render `aria-hidden`, paired with an sr-only label. */
|
||||||
|
glyph: string;
|
||||||
|
/** German layer/life-event label — used as the sr-only text and as visible chrome. */
|
||||||
|
label: string;
|
||||||
|
accent: TimelineAccent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a timeline EVENT entry to its glyph, redundant non-color label, and accent
|
||||||
|
* (REQ-007/008/018). Derived life-events use the * / † / ⚭ glyphs that match
|
||||||
|
* `personLifeDates.ts`; HISTORICAL events get the muted world band; everything
|
||||||
|
* else (curated PERSONAL) gets the mint family pill.
|
||||||
|
*/
|
||||||
|
export function getAccentConfig(entry: TimelineEntryDTO): AccentConfig {
|
||||||
|
if (entry.derived) {
|
||||||
|
switch (entry.derivedType) {
|
||||||
|
case 'BIRTH':
|
||||||
|
return { glyph: '*', label: m.timeline_derived_birth(), accent: 'derived' };
|
||||||
|
case 'DEATH':
|
||||||
|
return { glyph: '†', label: m.timeline_derived_death(), accent: 'derived' };
|
||||||
|
case 'MARRIAGE':
|
||||||
|
return { glyph: '⚭', label: m.timeline_derived_marriage(), accent: 'derived' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (entry.type === 'HISTORICAL') {
|
||||||
|
return { glyph: '◍', label: m.timeline_layer_world(), accent: 'historical' };
|
||||||
|
}
|
||||||
|
return { glyph: '★', label: m.timeline_layer_family(), accent: 'curated' };
|
||||||
|
}
|
||||||
34
frontend/src/lib/timeline/test-factories.ts
Normal file
34
frontend/src/lib/timeline/test-factories.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import type { components } from '$lib/generated/api';
|
||||||
|
|
||||||
|
type TimelineEntryDTO = components['schemas']['TimelineEntryDTO'];
|
||||||
|
type TimelineYearDTO = components['schemas']['TimelineYearDTO'];
|
||||||
|
type TimelineDTO = components['schemas']['TimelineDTO'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a `TimelineEntryDTO` mirroring the real wire shape (no `year`,
|
||||||
|
* `description`, or `snippet` fields). Defaults to a dated DAY-precision letter;
|
||||||
|
* override `kind`/`derived`/`type`/`derivedType` etc. for events.
|
||||||
|
*/
|
||||||
|
export function makeEntry(overrides: Partial<TimelineEntryDTO> = {}): TimelineEntryDTO {
|
||||||
|
return {
|
||||||
|
kind: 'LETTER',
|
||||||
|
precision: 'DAY',
|
||||||
|
derived: false,
|
||||||
|
senderName: 'Karl Raddatz',
|
||||||
|
receiverName: 'Elfriede Raddatz',
|
||||||
|
eventDate: '1915-06-15',
|
||||||
|
title: 'Brief aus dem Feld',
|
||||||
|
documentId: '11111111-1111-1111-1111-111111111111',
|
||||||
|
...overrides
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeYear(year: number, entries: TimelineEntryDTO[]): TimelineYearDTO {
|
||||||
|
return { year, entries };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeTimelineDTO(
|
||||||
|
opts: { years?: TimelineYearDTO[]; undated?: TimelineEntryDTO[] } = {}
|
||||||
|
): TimelineDTO {
|
||||||
|
return { years: opts.years ?? [], undated: opts.undated ?? [] };
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user