feat(timeline): add YearLetterStrip for dense years

Letter count + 12-month density sparkline + a >=44px keyboard-focusable expand
toggle that reveals that year's LetterCards (REQ-012). Sparkline values from the
shared monthHistogram.

Refs #779
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-06-13 19:36:57 +02:00
parent bea0e0d056
commit 5bff428954
2 changed files with 94 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
import { describe, it, expect, afterEach } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import { tick } from 'svelte';
import YearLetterStrip from './YearLetterStrip.svelte';
import { makeEntry } from './test-factories';
afterEach(() => cleanup());
function denseLetters(year: number, count: number) {
return Array.from({ length: count }, (_, i) =>
makeEntry({
eventDate: `${year}-${String((i % 12) + 1).padStart(2, '0')}-10`,
documentId: `doc-${i}`
})
);
}
describe('YearLetterStrip', () => {
it('shows the letter count and a 12-bar sparkline (REQ-012)', () => {
render(YearLetterStrip, { letters: denseLetters(1915, 30), year: 1915 });
expect(document.body.textContent).toContain('30');
const bars = document.querySelectorAll('[data-testid="sparkline-bar"]');
expect(bars).toHaveLength(12);
});
it('has a keyboard-focusable expand toggle of at least 44px (REQ-012)', () => {
render(YearLetterStrip, { letters: denseLetters(1915, 30), year: 1915 });
const toggle = document.querySelector('[data-testid="strip-expand"]') as HTMLButtonElement;
expect(toggle).not.toBeNull();
expect(toggle.tagName).toBe('BUTTON');
expect(toggle.getBoundingClientRect().height).toBeGreaterThanOrEqual(44);
});
it('reveals all letter cards when expanded (REQ-012)', async () => {
render(YearLetterStrip, { letters: denseLetters(1915, 30), year: 1915 });
expect(document.querySelectorAll('a').length).toBe(0);
const toggle = document.querySelector('[data-testid="strip-expand"]') as HTMLButtonElement;
toggle.click();
await tick();
expect(document.querySelectorAll('a').length).toBe(30);
});
});