feat(staples): add StapleChip component with aria-pressed toggle and focus ring

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-02 20:06:32 +02:00
parent 7c66dcad3a
commit 7bdc049461
2 changed files with 65 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
<script lang="ts">
let { name, selected, onToggle }: {
name: string;
selected: boolean;
onToggle: (value: boolean) => void;
} = $props();
</script>
<button
type="button"
aria-pressed={selected}
onclick={() => onToggle(!selected)}
class="inline-flex text-[12px] font-medium px-[12px] py-[6px] rounded-full border cursor-pointer
focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--green-light)]
{selected
? 'bg-[var(--green-tint)] border-[var(--green-light)] text-[var(--green-dark)]'
: 'bg-[var(--color-surface)] border-[var(--color-border)] text-[var(--color-text-muted)]'}"
>
{name}
</button>

View File

@@ -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');
});
});