learning_ai_common_plat/packages/auth-ui/src/OnboardingShell.tsx
saravanakumardb1 43439e9c85 feat(auth-ui): complete Auth UI Kit (3.2) — 7 new components, 54 tests
New components:
- RegisterForm — name, email, password, confirm, terms, password strength
- ForgotPasswordForm — email input with success/error states, back link
- ResetPasswordForm — new password + confirm with strength indicator
- VerifyEmailForm — 6-digit code input with resend, numeric-only filter
- OnboardingShell — step indicator, progress bar, back/next/complete nav
- AuthPageLayout — full-page centered card with product branding
- PasswordStrengthBar — visual bar + label (weak/fair/good/strong)

Existing components preserved: LoginForm, MfaChallenge, SocialButtons
All styled via --bl-* CSS custom properties for product theming
54 tests (13 existing + 41 new) — all passing
2026-03-19 20:25:57 -07:00

149 lines
4.1 KiB
TypeScript

import type { OnboardingShellProps } from './types.js';
/**
* Onboarding shell — step indicator, navigation, progress bar.
* Wraps arbitrary step content provided via children.
* Styled via CSS custom properties (inherits --bl-* from host app).
*/
export function OnboardingShell({
steps,
currentStep,
onNext,
onBack,
onComplete,
children,
className,
}: OnboardingShellProps) {
const isFirst = currentStep === 0;
const isLast = currentStep === steps.length - 1;
const progress = steps.length > 1 ? ((currentStep + 1) / steps.length) * 100 : 100;
return (
<div className={className} data-testid="bl-onboarding-shell">
{/* Progress bar */}
<div
style={{
height: '4px',
borderRadius: '2px',
background: 'var(--bl-border, #e5e7eb)',
marginBottom: '24px',
overflow: 'hidden',
}}
>
<div
data-testid="bl-onboarding-progress"
style={{
height: '100%',
width: `${progress}%`,
background: 'var(--bl-primary, #0066ff)',
transition: 'width 0.3s ease',
borderRadius: '2px',
}}
/>
</div>
{/* Step indicator */}
<div
data-testid="bl-onboarding-steps"
style={{
display: 'flex',
gap: '8px',
marginBottom: '24px',
justifyContent: 'center',
flexWrap: 'wrap',
}}
>
{steps.map((step, i) => (
<div
key={step.key}
data-testid={`bl-onboarding-step-${step.key}`}
style={{
display: 'flex',
alignItems: 'center',
gap: '6px',
fontSize: '13px',
color:
i === currentStep
? 'var(--bl-primary, #0066ff)'
: i < currentStep
? 'var(--bl-success, #22c55e)'
: 'var(--bl-muted, #999)',
fontWeight: i === currentStep ? 600 : 400,
}}
>
<span
style={{
width: '24px',
height: '24px',
borderRadius: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '12px',
fontWeight: 600,
background:
i <= currentStep ? 'var(--bl-primary, #0066ff)' : 'var(--bl-border, #e5e7eb)',
color: i <= currentStep ? '#fff' : 'var(--bl-muted, #999)',
}}
>
{i < currentStep ? '✓' : i + 1}
</span>
{step.label}
</div>
))}
</div>
{/* Step content */}
<div data-testid="bl-onboarding-content" style={{ marginBottom: '24px' }}>
{children}
</div>
{/* Navigation */}
<div
style={{
display: 'flex',
justifyContent: 'space-between',
gap: '12px',
}}
>
<button
type="button"
onClick={onBack}
disabled={isFirst}
data-testid="bl-onboarding-back"
style={{
padding: '10px 20px',
border: '1px solid var(--bl-border, #ccc)',
borderRadius: 'var(--bl-radius, 6px)',
background: 'var(--bl-surface, #fff)',
color: 'var(--bl-text, #333)',
cursor: isFirst ? 'not-allowed' : 'pointer',
fontSize: '14px',
opacity: isFirst ? 0.4 : 1,
}}
>
Back
</button>
<button
type="button"
onClick={isLast ? onComplete : onNext}
data-testid={isLast ? 'bl-onboarding-complete' : 'bl-onboarding-next'}
style={{
padding: '10px 20px',
border: 'none',
borderRadius: 'var(--bl-radius, 6px)',
background: 'var(--bl-primary, #0066ff)',
color: '#fff',
cursor: 'pointer',
fontSize: '14px',
fontWeight: 600,
}}
>
{isLast ? 'Complete' : 'Next'}
</button>
</div>
</div>
);
}