From 6adec8bd32276bf68a8e4bb0866b9b6ac61b4f83 Mon Sep 17 00:00:00 2001 From: "dahlia.elbanhawy" Date: Sun, 28 Sep 2025 20:35:40 +0200 Subject: [PATCH 1/3] create-stepper-component --- components/stepper/stepper.tsx | 96 +++++++++++++++++++++++++++++----- components/stepper/steps.ts | 11 +++- pages/index.tsx | 30 +++++------ 3 files changed, 107 insertions(+), 30 deletions(-) diff --git a/components/stepper/stepper.tsx b/components/stepper/stepper.tsx index fe3602e..e3507a7 100644 --- a/components/stepper/stepper.tsx +++ b/components/stepper/stepper.tsx @@ -1,14 +1,84 @@ -interface StepperProps {} - -export default function Stepper() { - /*TODO: Replace this with the actual Stepper implementation*/ - return ( -
- {""} -
- ); +import useStepper from "../../hooks/useStepper"; +interface Steps { + id: number + title: string + completed?: boolean +} + +interface StepperProps { + steps: Steps[] + currentStep: number + onStepClick?: (stepNumber: number) => void + onNextStep?: () => void +} + +export default function Stepper({ steps, onStepClick }: Omit) { + const { currentStep, handleNextStep } = useStepper(); + if (!steps || steps.length === 0) { + return ( +
+ No steps provided +
+ ); + } + + return ( +
+
+ {steps.map((step, index) => { + const stepNumber = index; + const isActive = stepNumber === currentStep; + const isCompleted = stepNumber < currentStep || step.completed; + const isClickable = onStepClick && stepNumber <= currentStep; + + return ( +
+ {index < steps.length - 1 && ( +
+ )} + + + + + {step.title} + +
+ ); + })} +
+
+ ); } diff --git a/components/stepper/steps.ts b/components/stepper/steps.ts index d840d60..a75c557 100644 --- a/components/stepper/steps.ts +++ b/components/stepper/steps.ts @@ -1,5 +1,12 @@ interface Step { - title: string; + id: number; + title: string; + completed?: boolean; } -export const steps: Step[] = []; +export const steps: Step[] = [ + { id: 1, title: 'Fahrzeug' }, + { id: 2, title: 'Termin' }, + { id: 3, title: 'Fahrzeug' }, + { id: 4, title: 'Kontakt' } +]; diff --git a/pages/index.tsx b/pages/index.tsx index f1ad62d..53077fb 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -5,21 +5,21 @@ import MainWrapper from "../components/repareo/mainWrapper"; import StepperWrapper from "../components/repareo/stepperWrapper"; import Stepper from "../components/stepper/stepper"; import useStepper from "../hooks/useStepper"; +import {steps} from "../components/stepper/steps"; export default function Home() { - const { currentStep, handleNextStep } = useStepper(); - return ( - <> -
- - - {/*TODO: Make sure the Stepper handles clicks on the button*/} - - - - - - - - ); + const { currentStep, handleNextStep } = useStepper(); + return ( + <> +
+ + + + + + + + + + ); } From d4fef850391bd77d96d826b7c5921a16e99582ae Mon Sep 17 00:00:00 2001 From: "dahlia.elbanhawy" Date: Sun, 28 Sep 2025 22:33:17 +0200 Subject: [PATCH 2/3] refactor: replace stepper buttons with non-interactive circles --- components/stepper/stepper.tsx | 46 +++++++++++----------------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/components/stepper/stepper.tsx b/components/stepper/stepper.tsx index e3507a7..bf0df38 100644 --- a/components/stepper/stepper.tsx +++ b/components/stepper/stepper.tsx @@ -1,4 +1,5 @@ import useStepper from "../../hooks/useStepper"; + interface Steps { id: number title: string @@ -7,13 +8,11 @@ interface Steps { interface StepperProps { steps: Steps[] - currentStep: number - onStepClick?: (stepNumber: number) => void - onNextStep?: () => void } -export default function Stepper({ steps, onStepClick }: Omit) { - const { currentStep, handleNextStep } = useStepper(); +export default function Stepper({ steps }: StepperProps) { + const { currentStep } = useStepper(); + if (!steps || steps.length === 0) { return (
@@ -22,55 +21,40 @@ export default function Stepper({ steps, onStepClick }: Omit
{steps.map((step, index) => { const stepNumber = index; const isActive = stepNumber === currentStep; - const isCompleted = stepNumber < currentStep || step.completed; - const isClickable = onStepClick && stepNumber <= currentStep; + const isCompleted = stepNumber < currentStep; return (
{index < steps.length - 1 && ( -
+
)} - + `}> + { index + 1} +
{step.title} From 13056a38a2997a3677cb6d15cd5c1c5716170877 Mon Sep 17 00:00:00 2001 From: "dahlia.elbanhawy" Date: Sun, 28 Sep 2025 22:33:38 +0200 Subject: [PATCH 3/3] Add unit test --- __tests__/components/stepper/stepper.test.tsx | 85 +++++++++++++++++-- 1 file changed, 80 insertions(+), 5 deletions(-) diff --git a/__tests__/components/stepper/stepper.test.tsx b/__tests__/components/stepper/stepper.test.tsx index 9a9c705..b9ced90 100644 --- a/__tests__/components/stepper/stepper.test.tsx +++ b/__tests__/components/stepper/stepper.test.tsx @@ -1,6 +1,81 @@ -// TODO: Implement your tests here -describe("Stepper", () => { - it("TODO", () => { - expect(true).toBe(false); - }); +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import Stepper from '../../../components/stepper/stepper'; +import useStepper from '../../../hooks/useStepper'; + +// Mock the useStepper hook +jest.mock('../../../hooks/useStepper'); + +const mockUseStepper = useStepper as jest.MockedFunction; + +describe('Stepper Component', () => { + const mockSteps = [ + { id: 1, title: 'Service' }, + { id: 2, title: 'Termin' }, + { id: 3, title: 'Fahrzeug' }, + { id: 4, title: 'Kontakt' }, + ]; + + const createMockStepper = (currentStep: number) => ({ + currentStep, + handleNextStep: jest.fn(), + }); + + beforeEach(() => { + mockUseStepper.mockReturnValue(createMockStepper(1)); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('renders without crashing', () => { + render(); + expect(screen.getByText('Service')).toBeInTheDocument(); + expect(screen.getByText('Termin')).toBeInTheDocument(); + expect(screen.getByText('Fahrzeug')).toBeInTheDocument(); + expect(screen.getByText('Kontakt')).toBeInTheDocument(); + }); + + it('displays "No steps provided" when steps array is empty', () => { + render(); + expect(screen.getByText('No steps provided')).toBeInTheDocument(); + }); + + it('displays "No steps provided" when steps is null', () => { + render(); + expect(screen.getByText('No steps provided')).toBeInTheDocument(); + }); + + it('renders step titles correctly', () => { + render(); + expect(screen.getByText('Service')).toBeInTheDocument(); + expect(screen.getByText('Termin')).toBeInTheDocument(); + expect(screen.getByText('Fahrzeug')).toBeInTheDocument(); + expect(screen.getByText('Kontakt')).toBeInTheDocument(); + }); + + it('applies correct styles for completed steps', () => { + mockUseStepper.mockReturnValue(createMockStepper(2)); + render(); + const stepCircles = document.querySelectorAll('.rounded-full'); + expect(stepCircles[0].className).toContain('bg-blue-500'); + expect(stepCircles[1].className).toContain('bg-blue-500'); + }); + + it('applies correct styles for inactive steps', () => { + mockUseStepper.mockReturnValue(createMockStepper(0)); + render(); + const stepCircles = document.querySelectorAll('.rounded-full'); + expect(stepCircles[1].className).toContain('bg-gray-200'); + expect(stepCircles[2].className).toContain('bg-gray-200'); + expect(stepCircles[3].className).toContain('bg-gray-200'); + }); + + it('renders connector lines between steps', () => { + render(); + const connectorLines = document.querySelectorAll('.absolute.top-6'); + expect(connectorLines).toHaveLength(3); + }); });