From 35ed6ca878caefe7544a9a672e27954b1ccbe7f1 Mon Sep 17 00:00:00 2001 From: Marcel Raddatz Date: Fri, 3 Apr 2026 09:43:06 +0200 Subject: [PATCH] feat(recipes): add FilterChipRow with effort filter chips Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/lib/recipes/FilterChipRow.svelte | 20 ++++++++ .../src/lib/recipes/FilterChipRow.test.ts | 51 +++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 frontend/src/lib/recipes/FilterChipRow.svelte create mode 100644 frontend/src/lib/recipes/FilterChipRow.test.ts diff --git a/frontend/src/lib/recipes/FilterChipRow.svelte b/frontend/src/lib/recipes/FilterChipRow.svelte new file mode 100644 index 0000000..571e48b --- /dev/null +++ b/frontend/src/lib/recipes/FilterChipRow.svelte @@ -0,0 +1,20 @@ + + +
+ {#each chips as label (label)} + + {/each} +
diff --git a/frontend/src/lib/recipes/FilterChipRow.test.ts b/frontend/src/lib/recipes/FilterChipRow.test.ts new file mode 100644 index 0000000..ae28811 --- /dev/null +++ b/frontend/src/lib/recipes/FilterChipRow.test.ts @@ -0,0 +1,51 @@ +import { describe, it, expect, vi } from 'vitest'; +import { render, screen } from '@testing-library/svelte'; +import { userEvent } from '@testing-library/user-event'; +import FilterChipRow from './FilterChipRow.svelte'; + +describe('FilterChipRow', () => { + it('renders all effort filter chips', () => { + render(FilterChipRow, { props: { activeFilter: 'Alle', onFilter: vi.fn() } }); + expect(screen.getByRole('button', { name: 'Alle' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Leicht' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Mittel' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Schwer' })).toBeInTheDocument(); + }); + + it('marks active chip with aria-pressed=true', () => { + render(FilterChipRow, { props: { activeFilter: 'Leicht', onFilter: vi.fn() } }); + expect(screen.getByRole('button', { name: 'Leicht' })).toHaveAttribute('aria-pressed', 'true'); + expect(screen.getByRole('button', { name: 'Alle' })).toHaveAttribute('aria-pressed', 'false'); + }); + + it('marks inactive chips with aria-pressed=false', () => { + render(FilterChipRow, { props: { activeFilter: 'Alle', onFilter: vi.fn() } }); + expect(screen.getByRole('button', { name: 'Leicht' })).toHaveAttribute('aria-pressed', 'false'); + expect(screen.getByRole('button', { name: 'Mittel' })).toHaveAttribute('aria-pressed', 'false'); + expect(screen.getByRole('button', { name: 'Schwer' })).toHaveAttribute('aria-pressed', 'false'); + }); + + it('calls onFilter with the chip label when clicked', async () => { + const user = userEvent.setup(); + const onFilter = vi.fn(); + render(FilterChipRow, { props: { activeFilter: 'Alle', onFilter } }); + + await user.click(screen.getByRole('button', { name: 'Leicht' })); + expect(onFilter).toHaveBeenCalledWith('Leicht'); + }); + + it('calls onFilter with Alle when reset chip clicked', async () => { + const user = userEvent.setup(); + const onFilter = vi.fn(); + render(FilterChipRow, { props: { activeFilter: 'Leicht', onFilter } }); + + await user.click(screen.getByRole('button', { name: 'Alle' })); + expect(onFilter).toHaveBeenCalledWith('Alle'); + }); + + it('active chip has green-tint styling', () => { + render(FilterChipRow, { props: { activeFilter: 'Mittel', onFilter: vi.fn() } }); + const btn = screen.getByRole('button', { name: 'Mittel' }); + expect(btn.className).toContain('bg-[var(--green-tint)]'); + }); +});