From 7bdc049461f4d08417a5015ec1c19bec91ea4179 Mon Sep 17 00:00:00 2001 From: Marcel Raddatz Date: Thu, 2 Apr 2026 20:06:32 +0200 Subject: [PATCH] feat(staples): add StapleChip component with aria-pressed toggle and focus ring Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/lib/onboarding/StapleChip.svelte | 20 +++++++++ .../src/lib/onboarding/StapleChip.test.ts | 45 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 frontend/src/lib/onboarding/StapleChip.svelte create mode 100644 frontend/src/lib/onboarding/StapleChip.test.ts diff --git a/frontend/src/lib/onboarding/StapleChip.svelte b/frontend/src/lib/onboarding/StapleChip.svelte new file mode 100644 index 0000000..00d5848 --- /dev/null +++ b/frontend/src/lib/onboarding/StapleChip.svelte @@ -0,0 +1,20 @@ + + + diff --git a/frontend/src/lib/onboarding/StapleChip.test.ts b/frontend/src/lib/onboarding/StapleChip.test.ts new file mode 100644 index 0000000..cc47d93 --- /dev/null +++ b/frontend/src/lib/onboarding/StapleChip.test.ts @@ -0,0 +1,45 @@ +import { describe, it, expect, vi } from 'vitest'; +import { render, screen } from '@testing-library/svelte'; +import { userEvent } from '@testing-library/user-event'; +import StapleChip from './StapleChip.svelte'; + +describe('StapleChip', () => { + it('renders a button with the ingredient name', () => { + render(StapleChip, { props: { name: 'Olivenöl', selected: false, onToggle: vi.fn() } }); + expect(screen.getByRole('button', { name: 'Olivenöl' })).toBeInTheDocument(); + }); + + it('is aria-pressed="false" when unselected', () => { + render(StapleChip, { props: { name: 'Olivenöl', selected: false, onToggle: vi.fn() } }); + expect(screen.getByRole('button', { name: 'Olivenöl' })).toHaveAttribute('aria-pressed', 'false'); + }); + + it('is aria-pressed="true" when selected', () => { + render(StapleChip, { props: { name: 'Olivenöl', selected: true, onToggle: vi.fn() } }); + expect(screen.getByRole('button', { name: 'Olivenöl' })).toHaveAttribute('aria-pressed', 'true'); + }); + + it('calls onToggle with true when unselected chip is clicked', async () => { + const user = userEvent.setup(); + const onToggle = vi.fn(); + render(StapleChip, { props: { name: 'Olivenöl', selected: false, onToggle } }); + + await user.click(screen.getByRole('button', { name: 'Olivenöl' })); + expect(onToggle).toHaveBeenCalledWith(true); + }); + + it('calls onToggle with false when selected chip is clicked', async () => { + const user = userEvent.setup(); + const onToggle = vi.fn(); + render(StapleChip, { props: { name: 'Olivenöl', selected: true, onToggle } }); + + await user.click(screen.getByRole('button', { name: 'Olivenöl' })); + expect(onToggle).toHaveBeenCalledWith(false); + }); + + it('has a visible focus ring class for keyboard accessibility', () => { + render(StapleChip, { props: { name: 'Olivenöl', selected: false, onToggle: vi.fn() } }); + const btn = screen.getByRole('button', { name: 'Olivenöl' }); + expect(btn.className).toContain('focus-visible:outline'); + }); +});