Re_Figma_Code/src/components/workflow/CreateRequest/WizardStepper.tsx

120 lines
4.6 KiB
TypeScript

import { Check } from 'lucide-react';
interface WizardStepperProps {
currentStep: number;
totalSteps: number;
stepNames: string[];
}
/**
* Component: WizardStepper
*
* Purpose: Displays progress indicator for multi-step wizard
*
* Features:
* - Shows current step and progress percentage
* - Mobile-optimized display
* - Test IDs for testing
*/
export function WizardStepper({ currentStep, totalSteps, stepNames }: WizardStepperProps) {
const progressPercentage = Math.round((currentStep / totalSteps) * 100);
// Use a narrower container for fewer steps to avoid excessive spacing
const containerMaxWidth = stepNames.length <= 3 ? 'max-w-xl' : 'max-w-6xl';
return (
<div
className="bg-white border-b border-gray-200 px-3 sm:px-6 py-2 sm:py-3 flex-shrink-0"
data-testid="wizard-stepper"
>
<div className={`${containerMaxWidth} mx-auto`}>
{/* Mobile: Current step indicator only */}
<div className="block sm:hidden" data-testid="wizard-stepper-mobile">
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2">
<div
className="w-8 h-8 rounded-full bg-green-600 text-white flex items-center justify-center text-xs font-semibold"
data-testid="wizard-stepper-mobile-current-step"
>
{currentStep}
</div>
<div>
<p className="text-xs font-semibold text-gray-900" data-testid="wizard-stepper-mobile-step-name">
{stepNames[currentStep - 1]}
</p>
<p className="text-xs text-gray-600" data-testid="wizard-stepper-mobile-step-info">
Step {currentStep} of {totalSteps}
</p>
</div>
</div>
<div className="text-right">
<p className="text-xs font-medium text-green-600" data-testid="wizard-stepper-mobile-progress">
{progressPercentage}%
</p>
</div>
</div>
{/* Progress bar */}
<div
className="w-full bg-gray-200 h-1.5 rounded-full overflow-hidden"
data-testid="wizard-stepper-mobile-progress-bar"
>
<div
className="bg-green-600 h-full transition-all duration-300"
style={{ width: `${progressPercentage}%` }}
data-testid="wizard-stepper-mobile-progress-fill"
/>
</div>
</div>
{/* Desktop: Full step indicator */}
<div className="hidden sm:block" data-testid="wizard-stepper-desktop">
<div className="flex items-center justify-center gap-4 mb-2" data-testid="wizard-stepper-desktop-steps">
{stepNames.map((_, index) => (
<div key={index} className="flex items-center flex-1 last:flex-none" data-testid={`wizard-stepper-desktop-step-${index + 1}`}>
<div
className={`w-8 h-8 rounded-full flex items-center justify-center text-xs font-semibold flex-shrink-0 ${index + 1 < currentStep
? 'bg-green-500 text-white'
: index + 1 === currentStep
? 'bg-green-500 text-white ring-2 ring-green-500/30 ring-offset-1'
: 'bg-gray-200 text-gray-600'
}`}
data-testid={`wizard-stepper-desktop-step-${index + 1}-indicator`}
>
{index + 1 < currentStep ? (
<Check className="w-4 h-4" />
) : (
index + 1
)}
</div>
{index < stepNames.length - 1 && (
<div
className={`flex-1 h-0.5 mx-2 ${index + 1 < currentStep ? 'bg-green-500' : 'bg-gray-200'
}`}
data-testid={`wizard-stepper-desktop-step-${index + 1}-connector`}
/>
)}
</div>
))}
</div>
<div
className="hidden lg:flex justify-between text-xs text-gray-600 mt-2 px-1"
data-testid="wizard-stepper-desktop-labels"
>
{stepNames.map((step, index) => (
<span
key={index}
className={`${index + 1 === currentStep ? 'font-semibold text-green-600' : ''
}`}
data-testid={`wizard-stepper-desktop-label-${index + 1}`}
>
{step}
</span>
))}
</div>
</div>
</div>
</div>
);
}