diff --git a/frontend/src/lib/shared/primitives/Sparkline.svelte b/frontend/src/lib/shared/primitives/Sparkline.svelte new file mode 100644 index 00000000..2876d3e1 --- /dev/null +++ b/frontend/src/lib/shared/primitives/Sparkline.svelte @@ -0,0 +1,38 @@ + + + diff --git a/frontend/src/lib/shared/primitives/Sparkline.svelte.spec.ts b/frontend/src/lib/shared/primitives/Sparkline.svelte.spec.ts new file mode 100644 index 00000000..228302b0 --- /dev/null +++ b/frontend/src/lib/shared/primitives/Sparkline.svelte.spec.ts @@ -0,0 +1,28 @@ +import { describe, it, expect, afterEach } from 'vitest'; +import { cleanup, render } from 'vitest-browser-svelte'; +import Sparkline from './Sparkline.svelte'; + +afterEach(() => cleanup()); + +describe('Sparkline', () => { + it('renders one bar per value', () => { + render(Sparkline, { values: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] }); + const bars = document.querySelectorAll('[data-testid="sparkline-bar"]'); + expect(bars).toHaveLength(12); + }); + + it('scales bar heights relative to the largest value', () => { + render(Sparkline, { values: [5, 10, 0] }); + const bars = document.querySelectorAll('[data-testid="sparkline-bar"]'); + const h = (i: number) => parseFloat(bars[i].style.height); + // 10 is the max → tallest; 5 is half of the max's height; 0 is the shortest. + expect(h(1)).toBeGreaterThan(h(0)); + expect(h(0)).toBeGreaterThan(h(2)); + }); + + it('exposes an accessible label when provided', () => { + render(Sparkline, { values: [1, 2, 3], label: 'Monatsdichte' }); + const img = document.querySelector('[role="img"]'); + expect(img?.getAttribute('aria-label')).toBe('Monatsdichte'); + }); +});