feat(recipes): add StepList component with numbered circles

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-03 09:58:39 +02:00
parent c7e56a173d
commit b39d04acce
2 changed files with 82 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
<script lang="ts">
type Step = { stepNumber?: number; instruction?: string };
let { steps }: { steps: Step[] } = $props();
const sortedSteps = $derived(
[...steps].sort((a, b) => (a.stepNumber ?? 0) - (b.stepNumber ?? 0))
);
</script>
<section>
<h2
class="text-[12px] font-medium font-sans tracking-[0.08em] uppercase text-[var(--color-text-muted)] mb-[16px]"
>
Zubereitung
</h2>
<ol>
{#each sortedSteps as step (step.stepNumber)}
<li class="flex gap-[16px] items-start mb-[20px]">
<div
data-testid="step-circle"
class="w-[28px] h-[28px] rounded-full bg-[var(--green-dark)] text-white flex items-center justify-center shrink-0 font-sans text-[13px] font-medium"
>
{step.stepNumber}
</div>
<p class="text-[14px] text-[var(--color-text)] leading-[1.6] pt-[4px]">
{step.instruction}
</p>
</li>
{/each}
</ol>
</section>

View File

@@ -0,0 +1,50 @@
import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/svelte';
import StepList from './StepList.svelte';
const mockSteps = [
{ stepNumber: 1, instruction: 'Wasser zum Kochen bringen' },
{ stepNumber: 2, instruction: 'Spaghetti al dente kochen' },
{ stepNumber: 3, instruction: 'Sauce bereiten' }
];
describe('StepList', () => {
it('renders the section heading', () => {
render(StepList, { props: { steps: mockSteps } });
expect(screen.getByText(/zubereitung/i)).toBeInTheDocument();
});
it('renders each step instruction', () => {
render(StepList, { props: { steps: mockSteps } });
expect(screen.getByText('Wasser zum Kochen bringen')).toBeInTheDocument();
expect(screen.getByText('Spaghetti al dente kochen')).toBeInTheDocument();
expect(screen.getByText('Sauce bereiten')).toBeInTheDocument();
});
it('renders step numbers', () => {
render(StepList, { props: { steps: mockSteps } });
expect(screen.getByText('1')).toBeInTheDocument();
expect(screen.getByText('2')).toBeInTheDocument();
expect(screen.getByText('3')).toBeInTheDocument();
});
it('renders numbered circles with step numbers', () => {
render(StepList, { props: { steps: mockSteps } });
const circles = document.querySelectorAll('[data-testid="step-circle"]');
expect(circles).toHaveLength(3);
});
it('renders steps in stepNumber order', () => {
const shuffled = [mockSteps[2], mockSteps[0], mockSteps[1]];
render(StepList, { props: { steps: shuffled } });
const circles = document.querySelectorAll('[data-testid="step-circle"]');
expect(circles[0].textContent).toBe('1');
expect(circles[1].textContent).toBe('2');
expect(circles[2].textContent).toBe('3');
});
it('renders empty state when no steps', () => {
render(StepList, { props: { steps: [] } });
expect(screen.getByText(/zubereitung/i)).toBeInTheDocument();
});
});