fix(timeline): skip empty-title events in the cluster lookup

A titleless or whitespace-only event stored `''` in the lookup, so its
letters still clustered and rendered a label-less `✉` mystery card. The
lookup now skips events whose trimmed title is empty — those letters stay
loose. Fixes review finding #8.

Refs #850
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-06-15 22:11:23 +02:00
committed by marcel
parent 5870d244fc
commit c23ff6026e
2 changed files with 22 additions and 2 deletions

View File

@@ -59,6 +59,22 @@ describe('eventClustering — buildEventLookup', () => {
expect(lookup.has(EV_A)).toBe(false); expect(lookup.has(EV_A)).toBe(false);
expect(lookup.size).toBe(0); expect(lookup.size).toBe(0);
}); });
it('skips an event with an empty or whitespace title — no bare ✉ card (#8)', () => {
const timeline: TimelineDTO = {
years: [
{
year: 1916,
entries: [
makeEvent({ eventId: EV_A, title: '' }),
makeEvent({ eventId: EV_B, title: ' ' })
]
}
],
undated: []
};
expect(buildEventLookup(timeline).size).toBe(0);
});
}); });
describe('eventClustering — splitYearLetters', () => { describe('eventClustering — splitYearLetters', () => {

View File

@@ -33,13 +33,17 @@ export interface SplitLetters {
* Only year-band events are collected: an undated event renders as a plain pill in the undated * Only year-band events are collected: an undated event renders as a plain pill in the undated
* bucket (out of clustering scope), so including it would scatter its dated letters into orphaned * bucket (out of clustering scope), so including it would scatter its dated letters into orphaned
* cross-year cards detached from that pill (#7). * cross-year cards detached from that pill (#7).
*
* An event with an empty/whitespace title is skipped too — clustering under it would render a
* label-less `✉` mystery card; its letters stay loose instead (#8).
*/ */
export function buildEventLookup(timeline: TimelineDTO): Map<string, string> { export function buildEventLookup(timeline: TimelineDTO): Map<string, string> {
const lookup = new Map<string, string>(); const lookup = new Map<string, string>();
const collect = (entries: TimelineEntryDTO[]) => { const collect = (entries: TimelineEntryDTO[]) => {
for (const entry of entries) { for (const entry of entries) {
if (entry.kind === 'EVENT' && entry.eventId && entry.type !== 'HISTORICAL') { const title = entry.title?.trim();
lookup.set(entry.eventId, entry.title ?? ''); if (entry.kind === 'EVENT' && entry.eventId && entry.type !== 'HISTORICAL' && title) {
lookup.set(entry.eventId, title);
} }
} }
}; };