feat(staples): A3/D3 — Pantry staples toggle UI #35
20
frontend/src/lib/onboarding/StapleChip.svelte
Normal file
20
frontend/src/lib/onboarding/StapleChip.svelte
Normal 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>
|
||||
45
frontend/src/lib/onboarding/StapleChip.test.ts
Normal file
45
frontend/src/lib/onboarding/StapleChip.test.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user