diff --git a/frontend/src/lib/timeline/EventPill.svelte b/frontend/src/lib/timeline/EventPill.svelte new file mode 100644 index 00000000..9b125cb7 --- /dev/null +++ b/frontend/src/lib/timeline/EventPill.svelte @@ -0,0 +1,59 @@ + + +
+
+ + + {config.label} + + + {#if entry.title} + {entry.title} + {/if} + {#if dateLabel} + {dateLabel} + {/if} + + {#if canEdit} + + + {m.btn_edit()} + + {/if} +
+
diff --git a/frontend/src/lib/timeline/EventPill.svelte.spec.ts b/frontend/src/lib/timeline/EventPill.svelte.spec.ts new file mode 100644 index 00000000..945ea65f --- /dev/null +++ b/frontend/src/lib/timeline/EventPill.svelte.spec.ts @@ -0,0 +1,90 @@ +import { describe, it, expect, afterEach } from 'vitest'; +import { cleanup, render } from 'vitest-browser-svelte'; +import EventPill from './EventPill.svelte'; +import { makeEntry } from './test-factories'; + +afterEach(() => cleanup()); + +const EVENT_ID = '33333333-3333-3333-3333-333333333333'; + +function derived(derivedType: 'BIRTH' | 'DEATH' | 'MARRIAGE', title: string) { + return makeEntry({ + kind: 'EVENT', + derived: true, + derivedType, + title, + senderName: '', + receiverName: '', + precision: 'YEAR', + eventDate: '1914-01-01', + documentId: undefined + }); +} + +describe('EventPill', () => { + it('renders a derived marriage as ⚭ + "Heirat" + title (REQ-007)', () => { + render(EventPill, { entry: derived('MARRIAGE', 'Heirat: Karl & Elfriede') }); + expect(document.body.textContent).toContain('⚭'); + expect(document.body.textContent).toContain('Heirat'); + expect(document.body.textContent).toContain('Heirat: Karl & Elfriede'); + }); + + it('renders a derived birth as * + "Geburt" (REQ-007)', () => { + render(EventPill, { entry: derived('BIRTH', 'Geburt: Hans') }); + expect(document.body.textContent).toContain('*'); + expect(document.body.textContent).toContain('Geburt'); + }); + + it('renders a derived death as † + "Tod" (REQ-007)', () => { + render(EventPill, { entry: derived('DEATH', 'Tod: Karl') }); + expect(document.body.textContent).toContain('†'); + expect(document.body.textContent).toContain('Tod'); + }); + + it('wraps the glyph aria-hidden with an sr-only label sibling (REQ-018)', () => { + render(EventPill, { entry: derived('BIRTH', 'Geburt: Hans') }); + const hidden = document.querySelector('[aria-hidden="true"]'); + expect(hidden?.textContent).toBe('*'); + const srOnly = document.querySelector('.sr-only'); + expect(srOnly?.textContent).toBe('Geburt'); + }); + + it('shows an edit affordance for a curated PERSONAL event with an eventId (REQ-008)', () => { + render(EventPill, { + entry: makeEntry({ + kind: 'EVENT', + derived: false, + type: 'PERSONAL', + eventId: EVENT_ID, + title: 'Auswanderung', + senderName: '', + receiverName: '', + documentId: undefined + }) + }); + const edit = document.querySelector('[data-testid="event-edit"]') as HTMLAnchorElement | null; + expect(edit).not.toBeNull(); + expect(edit?.getAttribute('href')).toContain(EVENT_ID); + }); + + it('shows no edit affordance when eventId is null (REQ-008)', () => { + render(EventPill, { + entry: makeEntry({ + kind: 'EVENT', + derived: false, + type: 'PERSONAL', + eventId: undefined, + title: 'Auswanderung', + senderName: '', + receiverName: '', + documentId: undefined + }) + }); + expect(document.querySelector('[data-testid="event-edit"]')).toBeNull(); + }); + + it('shows no edit affordance for a derived event (REQ-008)', () => { + render(EventPill, { entry: derived('MARRIAGE', 'Heirat') }); + expect(document.querySelector('[data-testid="event-edit"]')).toBeNull(); + }); +});