diff --git a/frontend/src/lib/timeline/BucketHeaderChip.svelte b/frontend/src/lib/timeline/BucketHeaderChip.svelte new file mode 100644 index 00000000..9d12cab4 --- /dev/null +++ b/frontend/src/lib/timeline/BucketHeaderChip.svelte @@ -0,0 +1,59 @@ + + + + {m.timeline_tag_chip_label()}: + + {name} + diff --git a/frontend/src/lib/timeline/BucketHeaderChip.svelte.spec.ts b/frontend/src/lib/timeline/BucketHeaderChip.svelte.spec.ts new file mode 100644 index 00000000..61c926ad --- /dev/null +++ b/frontend/src/lib/timeline/BucketHeaderChip.svelte.spec.ts @@ -0,0 +1,44 @@ +import { describe, it, expect, afterEach } from 'vitest'; +import { cleanup, render } from 'vitest-browser-svelte'; +import * as m from '$lib/paraglide/messages.js'; +import BucketHeaderChip from './BucketHeaderChip.svelte'; + +afterEach(() => cleanup()); + +describe('BucketHeaderChip (REQ-015/009)', () => { + it('renders the root-tag name', () => { + render(BucketHeaderChip, { name: 'Krieg', color: 'sienna' }); + expect(document.body.textContent).toContain('Krieg'); + }); + + it('tints the chip with var(--c-tag-{token}) for a known colour token (REQ-015)', () => { + render(BucketHeaderChip, { name: 'Krieg', color: 'sienna' }); + const chip = document.querySelector('[data-testid="bucket-header-chip"]') as HTMLElement; + expect(chip.getAttribute('style')).toContain('var(--c-tag-sienna)'); + }); + + it('renders a neutral chip with no --c-tag- binding when colour is null (REQ-015)', () => { + render(BucketHeaderChip, { name: 'Ohne Thema', color: null }); + expect(document.body.textContent).toContain('Ohne Thema'); + expect(document.body.innerHTML).not.toContain('var(--c-tag-'); + }); + + it('falls back to neutral for an unknown colour token, never a broken var (REQ-015)', () => { + // "krieg" is a §2 demo class name, not a real --c-tag-* token. + render(BucketHeaderChip, { name: 'Krieg', color: 'krieg' }); + expect(document.body.innerHTML).not.toContain('var(--c-tag-'); + }); + + it('prefixes the name with an sr-only theme label so colour is never the only cue', () => { + render(BucketHeaderChip, { name: 'Krieg', color: 'sienna' }); + const srOnly = document.querySelector('.sr-only'); + expect(srOnly?.textContent).toContain(m.timeline_tag_chip_label()); + }); + + it('renders an HTML-bearing name as inert text, never markup (REQ-009)', () => { + const evil = ''; + render(BucketHeaderChip, { name: evil, color: null }); + expect(document.body.textContent).toContain(evil); + expect(document.querySelector('img')).toBeNull(); + }); +});