feat(timeline): show the root-tag chip on the letter card
LetterCard now renders a TagChip beneath the sender→receiver/date line whenever the entry carries a rootTagName, mapping rootTagColor to the chip (neutral when null). Because the chip lives on LetterCard it shows up wherever a LetterCard does — the global timeline and the expanded YearLetterStrip — with no per-surface special-casing; a tagless letter shows no chip. A long name truncates inline so the card never overflows at 320px. Refs #835 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
import * as m from '$lib/paraglide/messages.js';
|
import * as m from '$lib/paraglide/messages.js';
|
||||||
import { timelineDateLabel } from './dateLabel';
|
import { timelineDateLabel } from './dateLabel';
|
||||||
import GlyphLabel from './GlyphLabel.svelte';
|
import GlyphLabel from './GlyphLabel.svelte';
|
||||||
|
import TagChip from './TagChip.svelte';
|
||||||
import type { components } from '$lib/generated/api';
|
import type { components } from '$lib/generated/api';
|
||||||
|
|
||||||
type TimelineEntryDTO = components['schemas']['TimelineEntryDTO'];
|
type TimelineEntryDTO = components['schemas']['TimelineEntryDTO'];
|
||||||
@@ -46,4 +47,9 @@ const receiver = $derived(
|
|||||||
<span data-testid="letter-date"> · {dateLabel}</span>
|
<span data-testid="letter-date"> · {dateLabel}</span>
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
|
{#if entry.rootTagName}
|
||||||
|
<!-- The primary root-tag chip sits on its own line beneath the meta line
|
||||||
|
(#835 §3); absent when the letter has no tag (REQ-005). -->
|
||||||
|
<TagChip name={entry.rootTagName} color={entry.rootTagColor ?? null} />
|
||||||
|
{/if}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { describe, it, expect, afterEach } from 'vitest';
|
import { describe, it, expect, afterEach } from 'vitest';
|
||||||
import { cleanup, render } from 'vitest-browser-svelte';
|
import { cleanup, render } from 'vitest-browser-svelte';
|
||||||
|
import { tick } from 'svelte';
|
||||||
import * as m from '$lib/paraglide/messages.js';
|
import * as m from '$lib/paraglide/messages.js';
|
||||||
import LetterCard from './LetterCard.svelte';
|
import LetterCard from './LetterCard.svelte';
|
||||||
|
import YearLetterStrip from './YearLetterStrip.svelte';
|
||||||
import { timelineDateLabel } from './dateLabel';
|
import { timelineDateLabel } from './dateLabel';
|
||||||
import { makeEntry } from './test-factories';
|
import { makeEntry } from './test-factories';
|
||||||
|
|
||||||
@@ -86,4 +88,42 @@ describe('LetterCard', () => {
|
|||||||
expect(document.body.textContent).toContain(evil);
|
expect(document.body.textContent).toContain(evil);
|
||||||
expect(document.querySelector('a script')).toBeNull();
|
expect(document.querySelector('a script')).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('renders one root-tag chip beneath the meta line when rootTagName is present (REQ-008)', () => {
|
||||||
|
render(LetterCard, { entry: makeEntry({ rootTagName: 'Familie', rootTagColor: 'sage' }) });
|
||||||
|
const chips = document.querySelectorAll('[data-testid="tag-chip"]');
|
||||||
|
expect(chips).toHaveLength(1);
|
||||||
|
expect(chips[0].textContent).toContain('Familie');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders no chip when the letter has no root tag (REQ-005/006)', () => {
|
||||||
|
render(LetterCard, { entry: makeEntry({ rootTagName: undefined, rootTagColor: undefined }) });
|
||||||
|
expect(document.querySelector('[data-testid="tag-chip"]')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps a long tag name from overflowing the card at 320px, full name in the title (REQ-008a)', () => {
|
||||||
|
document.body.style.width = '320px';
|
||||||
|
render(LetterCard, {
|
||||||
|
entry: makeEntry({
|
||||||
|
rootTagName: 'Briefe von der Front und aus der Heimat',
|
||||||
|
rootTagColor: 'sienna'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const link = document.querySelector('a') as HTMLAnchorElement;
|
||||||
|
expect(link.scrollWidth).toBeLessThanOrEqual(link.clientWidth);
|
||||||
|
const chip = document.querySelector('[data-testid="tag-chip"]') as HTMLElement;
|
||||||
|
expect(chip.getAttribute('title')).toBe('Briefe von der Front und aus der Heimat');
|
||||||
|
document.body.style.width = '';
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the chip inside an expanded YearLetterStrip too (REQ-012)', async () => {
|
||||||
|
render(YearLetterStrip, {
|
||||||
|
letters: [makeEntry({ rootTagName: 'Familie', rootTagColor: 'sage', documentId: 'doc-1' })],
|
||||||
|
year: 1909
|
||||||
|
});
|
||||||
|
(document.querySelector('[data-testid="strip-expand"]') as HTMLButtonElement).click();
|
||||||
|
await tick();
|
||||||
|
const chip = document.querySelector('[data-testid="tag-chip"]');
|
||||||
|
expect(chip?.textContent).toContain('Familie');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user