Skip to main content

Employee Onboarding Components

The Employee Onboarding Components provide comprehensive onboarding workflow functionality for the WorkPayCore Frontend application. These components handle new employee setup, workflow management, progress tracking, and multi-step onboarding processes with consistent navigation and state management.

Overview

This document covers onboarding-related components that enable structured employee onboarding workflows, both for employee self-onboarding and admin-managed onboarding processes, with step-by-step navigation and progress tracking.

Components Overview

Core Onboarding Components

Onboarding Flow Components

  • OnboardingPageWrapper - Page wrapper for onboarding flows
  • CompleteOnboardingModal - Modal for completing onboarding
  • OnboardingStepNavigation - Step-by-step navigation component

Onboarding Patterns

  • Employee Self-Onboarding - Employee-driven onboarding pattern
  • Admin-Managed Onboarding - HR-driven onboarding pattern
  • Workflow Management - Onboarding workflow configuration

OnboardingEmployeeSidebar

Sidebar navigation component for employee onboarding with step tracking and progress visualization.

Component Location

import OnboardingEmployeeSidebar from 'components/OnboardingEmployeeSidebar';

Features

  • Step Progress Tracking: Visual progress indicators for each onboarding step
  • Current Step Highlighting: Highlights the current active step
  • Navigation Links: Clickable navigation to different onboarding steps
  • Responsive Design: Adapts to different screen sizes
  • Workflow Integration: Integrates with workflow management system

Usage Examples

Basic Onboarding Sidebar

import OnboardingEmployeeSidebar from 'components/OnboardingEmployeeSidebar';

function OnboardingLayout() {
return (
<Flex minHeight='100vh'>
<OnboardingEmployeeSidebar
width={{ base: '0%', lg: '20%' }}
position='fixed'
h='100vh'
maxH='100vh'
zIndex='10000'
/>

<Stack
width={{ base: '100%', lg: '76%' }}
ml={{ base: '0%', lg: '21%' }}
spacing={0}
>
<Box mt='80px'>
<OnboardingContent />
</Box>
</Stack>
</Flex>
);
}
function CustomOnboardingSidebar() {
const onboardingSteps = [
{
id: 0,
name: 'Personal Details',
completed: true,
active: false,
url: '/employee-onboarding/personal-details',
},
{
id: 1,
name: 'Employment Details',
completed: false,
active: true,
url: '/employee-onboarding/employment-details',
},
{
id: 2,
name: 'Documents',
completed: false,
active: false,
url: '/employee-onboarding/documents',
},
];

return (
<OnboardingEmployeeSidebar
steps={onboardingSteps}
currentStep={1}
onStepClick={step => {
if (step.completed || step.active) {
navigateToStep(step.url);
}
}}
/>
);
}

EmployeeOnboardingNavbar

Navigation bar component for onboarding progress with breadcrumb navigation and step indicators.

Component Location

import EmployeeOnboardingNavbar from 'components/EmployeeOnboarding';

Features

  • Progress Visualization: Shows current step and overall progress
  • Breadcrumb Navigation: Hierarchical navigation structure
  • Step Indicators: Visual indicators for completed, current, and upcoming steps
  • Responsive Layout: Adapts to different screen sizes
  • Action Buttons: Save, continue, and finish later actions

Usage Examples

Basic Onboarding Navbar

import EmployeeOnboardingNavbar from 'components/EmployeeOnboarding';

function OnboardingHeader() {
return (
<Box
position='fixed'
left={{ base: '0', lg: '20%' }}
right={0}
top={0}
zIndex='banner'
>
<EmployeeOnboardingNavbar
currentStep={2}
totalSteps={8}
stepName='Employment Details'
onSave={handleSave}
onContinue={handleContinue}
onFinishLater={handleFinishLater}
/>
</Box>
);
}
function CustomOnboardingNavbar() {
const [saving, setSaving] = useState(false);
const [continuing, setContinuing] = useState(false);

const handleSaveAndContinue = async () => {
setContinuing(true);
try {
await saveCurrentStep();
await navigateToNextStep();
} finally {
setContinuing(false);
}
};

const handleSaveAndFinishLater = async () => {
setSaving(true);
try {
await saveCurrentStep();
await navigateToOnboardingDashboard();
} finally {
setSaving(false);
}
};

return (
<EmployeeOnboardingNavbar
currentStep={3}
totalSteps={8}
stepName='Document Upload'
isLoading={saving || continuing}
onSave={handleSaveAndContinue}
onFinishLater={handleSaveAndFinishLater}
customActions={
<Button size='sm' variant='outline'>
Preview Application
</Button>
}
/>
);
}

AdminOnboardingNav

Admin-specific navigation component for managing employee onboarding processes.

Component Location

import AdminOnboardingNav from 'components/EmployeeOnboarding/AdminOnboardingNav';

Props

PropTypeRequiredDefaultDescription
pathnamestring-Current page path
isDisabledboolean-falseDisable navigation actions
isLoadingNextboolean-falseLoading state for next button
handleNextfunction-Next step handler
isLoadingFinishLaterboolean-falseLoading state for save button
handleSaveFinishLaterfunction-Save and finish later handler

Features

  • Admin-Specific Actions: Admin-oriented navigation controls
  • Employee Context: Shows current employee being onboarded
  • Step Management: Handles step progression and validation
  • Complete Onboarding: Provides completion workflow
  • Workflow Integration: Integrates with onboarding workflow system

Usage Examples

Admin Onboarding Navigation

import AdminOnboardingNav from 'components/EmployeeOnboarding/AdminOnboardingNav';

function AdminOnboardingPage() {
const [isLoading, setIsLoading] = useState(false);
const [isSaving, setIsSaving] = useState(false);
const location = useLocation();

const handleNext = async () => {
setIsLoading(true);
try {
await validateCurrentStep();
await navigateToNextStep();
} catch (error) {
showErrorToast(error.message);
} finally {
setIsLoading(false);
}
};

const handleSaveFinishLater = async () => {
setIsSaving(true);
try {
await saveCurrentProgress();
await navigateToOnboardingDashboard();
} finally {
setIsSaving(false);
}
};

return (
<AdminOnboardingNav
pathname={location.pathname}
isDisabled={!isFormValid}
isLoadingNext={isLoading}
handleNext={handleNext}
isLoadingFinishLater={isSaving}
handleSaveFinishLater={handleSaveFinishLater}
/>
);
}

Admin Navigation with Validation

function AdminOnboardingWithValidation() {
const [formData, setFormData] = useState({});
const [validationErrors, setValidationErrors] = useState({});
const [currentStep, setCurrentStep] = useState(0);

const validateStep = async stepIndex => {
const errors = {};

switch (stepIndex) {
case 0: // Personal Details
if (!formData.firstName) errors.firstName = 'First name is required';
if (!formData.lastName) errors.lastName = 'Last name is required';
if (!formData.email) errors.email = 'Email is required';
break;

case 1: // Employment Details
if (!formData.department) errors.department = 'Department is required';
if (!formData.position) errors.position = 'Position is required';
break;

default:
break;
}

setValidationErrors(errors);
return Object.keys(errors).length === 0;
};

const handleNext = async () => {
const isValid = await validateStep(currentStep);
if (!isValid) {
toast.error('Please fix the validation errors before continuing');
return;
}

await saveStepData(currentStep, formData);
setCurrentStep(prev => prev + 1);
};

const isFormValid = Object.keys(validationErrors).length === 0;

return (
<VStack spacing={4}>
<OnboardingStepForm
step={currentStep}
data={formData}
onChange={setFormData}
errors={validationErrors}
/>

<AdminOnboardingNav
pathname={`/admin-onboarding/step-${currentStep}`}
isDisabled={!isFormValid}
handleNext={handleNext}
handleSaveFinishLater={() => saveCurrentProgress()}
/>
</VStack>
);
}

OnboardingWorkflowSidebar

Workflow management sidebar component for configuring onboarding workflows.

Component Location

import OnboardingWorkflowSidebar from 'components/EmployeeOnboarding/OnboardingWorkflowSidebar';

Features

  • Workflow Step Configuration: Configure which steps are included in workflow
  • Step Reordering: Drag and drop step reordering
  • Conditional Steps: Configure conditional step visibility
  • Workflow Templates: Pre-configured workflow templates
  • Progress Tracking: Visual progress tracking for workflow creation

Usage Examples

Basic Workflow Sidebar

import OnboardingWorkflowSidebar from 'components/EmployeeOnboarding/OnboardingWorkflowSidebar';

function WorkflowBuilder() {
const [workflowSteps, setWorkflowSteps] = useState([
{ id: 0, name: 'Personal Details', enabled: true, required: true },
{ id: 1, name: 'Employment Details', enabled: true, required: true },
{ id: 2, name: 'Documents', enabled: false, required: false },
{ id: 3, name: 'Next of Kin', enabled: true, required: false },
]);

const handleStepToggle = (stepId, enabled) => {
setWorkflowSteps(prev =>
prev.map(step => (step.id === stepId ? { ...step, enabled } : step)),
);
};

return (
&lt;Flex&gt;
<OnboardingWorkflowSidebar
steps={workflowSteps}
onStepToggle={handleStepToggle}
onWorkflowSave={handleWorkflowSave}
/>

<Box flex={1} ml={4}>
<WorkflowPreview steps={workflowSteps} />
</Box>
</Flex>
);
}

Advanced Workflow Configuration

function AdvancedWorkflowBuilder() {
const [workflows, setWorkflows] = useState([]);
const [selectedWorkflow, setSelectedWorkflow] = useState(null);
const [isEditing, setIsEditing] = useState(false);

const workflowTemplates = [
{
id: 'basic',
name: 'Basic Onboarding',
steps: [0, 1, 2, 3], // Personal, Employment, Documents, Next of Kin
},
{
id: 'comprehensive',
name: 'Comprehensive Onboarding',
steps: [0, 1, 2, 3, 4, 5, 6, 7], // All steps
},
{
id: 'minimal',
name: 'Minimal Onboarding',
steps: [0, 1], // Personal and Employment only
},
];

const handleTemplateSelect = template => {
const newWorkflow = {
id: Date.now(),
name: template.name,
steps: template.steps.map(stepId => ({
id: stepId,
enabled: true,
required: stepId <= 1, // First two steps are required
})),
};

setSelectedWorkflow(newWorkflow);
setIsEditing(true);
};

const handleWorkflowSave = async workflowData => {
try {
const savedWorkflow = await saveWorkflow(workflowData);
setWorkflows(prev => [...prev, savedWorkflow]);
setIsEditing(false);
toast.success('Workflow saved successfully');
} catch (error) {
toast.error('Failed to save workflow');
}
};

return (
<VStack spacing={4} align='stretch'>
<HStack spacing={4}>
<Text fontSize='lg' fontWeight='bold'>
Workflow Templates
</Text>
<Spacer />
<Button
size='sm'
onClick={() => setIsEditing(true)}
leftIcon={<PlusIcon />}
>
Create Custom Workflow
</Button>
</HStack>

<SimpleGrid columns={3} spacing={4}>
{workflowTemplates.map(template => (
<Box
key={template.id}
p={4}
borderWidth='1px'
borderRadius='md'
cursor='pointer'
onClick={() => handleTemplateSelect(template)}
_hover={{ borderColor: 'blue.300' }}
>
<Text fontWeight='bold'>{template.name}</Text>
<Text fontSize='sm' color='gray.600'>
{template.steps.length} steps
</Text>
</Box>
))}
</SimpleGrid>

{isEditing && (
<OnboardingWorkflowSidebar
workflow={selectedWorkflow}
onWorkflowSave={handleWorkflowSave}
onCancel={() => setIsEditing(false)}
/>
)}
</VStack>
);
}

Onboarding Patterns

Employee Self-Onboarding Pattern

function EmployeeSelfOnboardingFlow() {
const [currentStep, setCurrentStep] = useState(0);
const [onboardingData, setOnboardingData] = useState({});
const [completedSteps, setCompletedSteps] = useState(new Set());

const onboardingSteps = [
{
id: 0,
title: 'Personal Details',
component: PersonalDetailsForm,
url: '/employee-onboarding/personal-details',
required: true,
},
{
id: 1,
title: 'Statutory Details',
component: StatutoryDetailsForm,
url: '/employee-onboarding/statutory-details',
required: true,
},
{
id: 2,
title: 'Payment Details',
component: PaymentDetailsForm,
url: '/employee-onboarding/payment-details',
required: true,
},
{
id: 3,
title: 'Documents',
component: DocumentsForm,
url: '/employee-onboarding/documents',
required: false,
},
{
id: 4,
title: 'Next of Kin',
component: NextOfKinForm,
url: '/employee-onboarding/next-of-kin',
required: false,
},
{
id: 5,
title: 'Emergency Contact',
component: EmergencyContactForm,
url: '/employee-onboarding/emergency-contact',
required: false,
},
{
id: 6,
title: 'Legal',
component: LegalForm,
url: '/employee-onboarding/legal',
required: true,
},
];

const handleStepComplete = (stepId, data) => {
setOnboardingData(prev => ({
...prev,
[stepId]: data,
}));

setCompletedSteps(prev => new Set([...prev, stepId]));

// Auto-advance to next step
if (stepId === currentStep) {
setCurrentStep(prev => prev + 1);
}
};

const handleStepNavigation = stepId => {
// Only allow navigation to completed steps or current step
if (completedSteps.has(stepId) || stepId === currentStep) {
setCurrentStep(stepId);
}
};

const isOnboardingComplete = () => {
const requiredSteps = onboardingSteps.filter(step => step.required);
return requiredSteps.every(step => completedSteps.has(step.id));
};

const handleOnboardingSubmit = async () => {
if (!isOnboardingComplete()) {
toast.error('Please complete all required steps');
return;
}

try {
await submitOnboardingData(onboardingData);
toast.success('Onboarding completed successfully!');
// Redirect to dashboard
navigate('/dashboard');
} catch (error) {
toast.error('Failed to complete onboarding');
}
};

const CurrentStepComponent = onboardingSteps[currentStep]?.component;

return (
<Flex minHeight='100vh'>
<OnboardingEmployeeSidebar
steps={onboardingSteps}
currentStep={currentStep}
completedSteps={completedSteps}
onStepClick={handleStepNavigation}
/>

<Box flex={1} ml='20%'>
<EmployeeOnboardingNavbar
currentStep={currentStep}
totalSteps={onboardingSteps.length}
stepName={onboardingSteps[currentStep]?.title}
onSave={() => handleStepComplete(currentStep, getCurrentStepData())}
onFinishLater={() => saveDraftAndExit()}
/>

<Box p={8} mt='80px'>
{CurrentStepComponent && (
<CurrentStepComponent
data={onboardingData[currentStep]}
onComplete={data => handleStepComplete(currentStep, data)}
onNext={() => setCurrentStep(prev => prev + 1)}
onBack={() => setCurrentStep(prev => prev - 1)}
/>
)}

{currentStep === onboardingSteps.length && (
<OnboardingCompletionPage
onboardingData={onboardingData}
onSubmit={handleOnboardingSubmit}
isComplete={isOnboardingComplete()}
/>
)}
</Box>
</Box>
</Flex>
);
}

Admin-Managed Onboarding Pattern

function AdminManagedOnboardingFlow() {
const [employees, setEmployees] = useState([]);
const [selectedEmployee, setSelectedEmployee] = useState(null);
const [currentStep, setCurrentStep] = useState(0);
const [workflowId, setWorkflowId] = useState(null);

const adminOnboardingSteps = [
{
id: 0,
title: 'Personal Details',
component: AdminPersonalDetailsForm,
url: '/admin-onboarding/personal-details',
},
{
id: 1,
title: 'Employment Details',
component: AdminEmploymentDetailsForm,
url: '/admin-onboarding/employment-details',
},
{
id: 2,
title: 'Salary Details',
component: AdminSalaryDetailsForm,
url: '/admin-onboarding/salary-details',
},
{
id: 3,
title: 'Payment Details',
component: AdminPaymentDetailsForm,
url: '/admin-onboarding/payment-details',
},
{
id: 4,
title: 'Documents',
component: AdminDocumentsForm,
url: '/admin-onboarding/documents',
},
{
id: 5,
title: 'Next of Kin',
component: AdminNextOfKinForm,
url: '/admin-onboarding/next-of-kin',
},
{
id: 6,
title: 'Emergency Contact',
component: AdminEmergencyContactForm,
url: '/admin-onboarding/emergency-contact',
},
{
id: 7,
title: 'Issue Assets',
component: AdminIssueAssetsForm,
url: '/admin-onboarding/issue-assets',
},
{
id: 8,
title: 'Legal',
component: AdminLegalForm,
url: '/admin-onboarding/legal',
},
];

const handleEmployeeSelect = employee => {
setSelectedEmployee(employee);
setCurrentStep(0);
setWorkflowId(employee.workflow_id);
};

const handleStepSave = async stepData => {
try {
await saveOnboardingStep({
employeeId: selectedEmployee.id,
workflowId: workflowId,
step: currentStep,
data: stepData,
});

toast.success('Step saved successfully');
} catch (error) {
toast.error('Failed to save step');
}
};

const handleNext = async () => {
const isValid = await validateCurrentStep();
if (!isValid) return;

await handleStepSave(getCurrentStepData());
setCurrentStep(prev => prev + 1);
};

const handleCompleteOnboarding = async () => {
try {
await completeEmployeeOnboarding({
employeeId: selectedEmployee.id,
workflowId: workflowId,
});

toast.success('Employee onboarding completed successfully');

// Update employee status
setEmployees(prev =>
prev.map(emp =>
emp.id === selectedEmployee.id
? { ...emp, onboarding_status: 'completed' }
: emp,
),
);

setSelectedEmployee(null);
} catch (error) {
toast.error('Failed to complete onboarding');
}
};

const CurrentStepComponent = adminOnboardingSteps[currentStep]?.component;

return (
<VStack spacing={4} align='stretch'>
<HStack spacing={4}>
<Text fontSize='xl' fontWeight='bold'>
Employee Onboarding Management
</Text>
<Spacer />
<Button
leftIcon={<PlusIcon />}
onClick={() => setIsAddingEmployee(true)}
>
Add New Employee
</Button>
</HStack>

{!selectedEmployee ? (
<EmployeeSelectionGrid
employees={employees}
onEmployeeSelect={handleEmployeeSelect}
/>
) : (
&lt;Flex&gt;
<OnboardingWorkflowSidebar
steps={adminOnboardingSteps}
currentStep={currentStep}
workflowId={workflowId}
employee={selectedEmployee}
/>

<Box flex={1} ml={4}>
<AdminOnboardingNav
pathname={adminOnboardingSteps[currentStep]?.url}
isDisabled={!isCurrentStepValid()}
handleNext={handleNext}
handleSaveFinishLater={() => handleStepSave(getCurrentStepData())}
/>

<Box p={6}>
<Text fontSize='lg' fontWeight='bold' mb={4}>
{selectedEmployee.name} -{' '}
{adminOnboardingSteps[currentStep]?.title}
</Text>

{CurrentStepComponent && (
<CurrentStepComponent
employee={selectedEmployee}
workflowId={workflowId}
onSave={handleStepSave}
onComplete={handleCompleteOnboarding}
/>
)}
</Box>
</Box>
</Flex>
)}
</VStack>
);
}

Workflow Management Pattern

function WorkflowManagementSystem() {
const [workflows, setWorkflows] = useState([]);
const [selectedWorkflow, setSelectedWorkflow] = useState(null);
const [isCreating, setIsCreating] = useState(false);
const [isEditing, setIsEditing] = useState(false);

const { data: workflowsData, isLoading } = useQuery(
['onboarding-workflows'],
fetchOnboardingWorkflows,
);

useEffect(() => {
if (workflowsData) {
setWorkflows(workflowsData);
}
}, [workflowsData]);

const handleCreateWorkflow = async workflowData => {
try {
const newWorkflow = await createOnboardingWorkflow(workflowData);
setWorkflows(prev => [...prev, newWorkflow]);
setIsCreating(false);
toast.success('Workflow created successfully');
} catch (error) {
toast.error('Failed to create workflow');
}
};

const handleEditWorkflow = async workflowData => {
try {
const updatedWorkflow = await updateOnboardingWorkflow(workflowData);
setWorkflows(prev =>
prev.map(wf => (wf.id === updatedWorkflow.id ? updatedWorkflow : wf)),
);
setIsEditing(false);
toast.success('Workflow updated successfully');
} catch (error) {
toast.error('Failed to update workflow');
}
};

const handleDeleteWorkflow = async workflowId => {
try {
await deleteOnboardingWorkflow(workflowId);
setWorkflows(prev => prev.filter(wf => wf.id !== workflowId));
toast.success('Workflow deleted successfully');
} catch (error) {
toast.error('Failed to delete workflow');
}
};

const workflowStepOptions = [
{ id: 0, name: 'Personal Details', required: true },
{ id: 1, name: 'Employment Details', required: true },
{ id: 2, name: 'Salary Details', required: false },
{ id: 3, name: 'Payment Details', required: false },
{ id: 4, name: 'Documents', required: false },
{ id: 5, name: 'Next of Kin', required: false },
{ id: 6, name: 'Emergency Contact', required: false },
{ id: 7, name: 'Assets', required: false },
{ id: 8, name: 'Legal', required: true },
];

if (isLoading) {
return <LoadingSpinner />;
}

return (
<VStack spacing={6} align='stretch'>
<HStack justify='space-between'>
<Text fontSize='2xl' fontWeight='bold'>
Onboarding Workflows
</Text>
<Button leftIcon={<PlusIcon />} onClick={() => setIsCreating(true)}>
Create New Workflow
</Button>
</HStack>

<SimpleGrid columns={3} spacing={4}>
{workflows.map(workflow => (
<WorkflowCard
key={workflow.id}
workflow={workflow}
onEdit={() => {
setSelectedWorkflow(workflow);
setIsEditing(true);
}}
onDelete={() => handleDeleteWorkflow(workflow.id)}
onSelect={() => setSelectedWorkflow(workflow)}
/>
))}
</SimpleGrid>

{isCreating && (
<WorkflowBuilder
stepOptions={workflowStepOptions}
onSave={handleCreateWorkflow}
onCancel={() => setIsCreating(false)}
/>
)}

{isEditing && selectedWorkflow && (
<WorkflowBuilder
workflow={selectedWorkflow}
stepOptions={workflowStepOptions}
onSave={handleEditWorkflow}
onCancel={() => setIsEditing(false)}
/>
)}

{selectedWorkflow && !isEditing && (
<WorkflowPreview
workflow={selectedWorkflow}
onEdit={() => setIsEditing(true)}
onClose={() => setSelectedWorkflow(null)}
/>
)}
</VStack>
);
}

Best Practices

Onboarding Flow Design

  1. Progressive Disclosure

    • Break complex forms into manageable steps
    • Show progress indicators clearly
    • Provide step-by-step guidance
  2. Error Handling

    • Validate steps before allowing progression
    • Provide clear error messages
    • Allow users to save drafts and continue later
  3. Accessibility

    • Ensure proper tab order for navigation
    • Provide clear focus indicators
    • Include proper ARIA labels

State Management

  1. Data Persistence

    • Save step data as users progress
    • Implement auto-save functionality
    • Handle browser refresh gracefully
  2. Progress Tracking

    • Track completion status for each step
    • Show overall progress percentage
    • Highlight required vs optional steps
  3. Workflow Configuration

    • Make workflows configurable by admins
    • Support different onboarding flows for different roles
    • Allow step reordering and customization

Testing

Unit Tests

import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import AdminOnboardingNav from 'components/EmployeeOnboarding/AdminOnboardingNav';

describe('AdminOnboardingNav', () => {
const mockHandleNext = jest.fn();
const mockHandleSaveFinishLater = jest.fn();

beforeEach(() => {
jest.clearAllMocks();
});

it('renders navigation with correct step information', () => {
render(
<AdminOnboardingNav
pathname='/admin-onboarding/personal-details'
handleNext={mockHandleNext}
handleSaveFinishLater={mockHandleSaveFinishLater}
/>,
);

expect(screen.getByText('Personal Details')).toBeInTheDocument();
expect(screen.getByText('Next')).toBeInTheDocument();
expect(screen.getByText('Save & Finish Later')).toBeInTheDocument();
});

it('calls handleNext when Next button is clicked', async () => {
const { user } = render(
<AdminOnboardingNav
pathname='/admin-onboarding/personal-details'
handleNext={mockHandleNext}
handleSaveFinishLater={mockHandleSaveFinishLater}
/>,
);

const nextButton = screen.getByText('Next');
await user.click(nextButton);

expect(mockHandleNext).toHaveBeenCalledTimes(1);
});

it('disables Next button when isDisabled is true', () => {
render(
<AdminOnboardingNav
pathname='/admin-onboarding/personal-details'
isDisabled={true}
handleNext={mockHandleNext}
handleSaveFinishLater={mockHandleSaveFinishLater}
/>,
);

const nextButton = screen.getByText('Next');
expect(nextButton).toBeDisabled();
});
});

Integration Tests

describe('Onboarding Flow Integration', () => {
it('completes employee self-onboarding flow', async () => {
const mockOnboardingData = {
personalDetails: { firstName: 'John', lastName: 'Doe' },
employmentDetails: { department: 'IT', position: 'Developer' },
};

const { user } = render(<EmployeeSelfOnboardingFlow />);

// Fill personal details
await user.type(screen.getByLabelText('First Name'), 'John');
await user.type(screen.getByLabelText('Last Name'), 'Doe');
await user.click(screen.getByText('Next'));

// Fill employment details
await user.selectOptions(screen.getByLabelText('Department'), 'IT');
await user.type(screen.getByLabelText('Position'), 'Developer');
await user.click(screen.getByText('Next'));

// Complete onboarding
await user.click(screen.getByText('Complete Onboarding'));

await waitFor(() => {
expect(
screen.getByText('Onboarding completed successfully!'),
).toBeInTheDocument();
});
});
});

Migration Guide

From Legacy Onboarding Components

  1. Update Import Paths

    // Old
    import OnboardingNav from 'components/OldOnboarding/Nav';

    // New
    import AdminOnboardingNav from 'components/EmployeeOnboarding/AdminOnboardingNav';
  2. Update Props Structure

    // Old
    <OnboardingNav
    step={2}
    onNext={handleNext}
    onSave={handleSave}
    />

    // New
    <AdminOnboardingNav
    pathname="/admin-onboarding/employment-details"
    handleNext={handleNext}
    handleSaveFinishLater={handleSave}
    />
  3. Update State Management

    // Old
    const [currentStep, setCurrentStep] = useState(0);

    // New
    // Use URL-based routing for step management
    const location = useLocation();
    const currentStep = getCurrentStepFromPath(location.pathname);

Advanced Usage

Custom Onboarding Workflow

function CustomOnboardingWorkflow() {
const [workflowConfig, setWorkflowConfig] = useState({
steps: [],
conditionalSteps: {},
validationRules: {},
});

const addConditionalStep = (stepId, condition) => {
setWorkflowConfig(prev => ({
...prev,
conditionalSteps: {
...prev.conditionalSteps,
[stepId]: condition,
},
}));
};

const addValidationRule = (stepId, rule) => {
setWorkflowConfig(prev => ({
...prev,
validationRules: {
...prev.validationRules,
[stepId]: rule,
},
}));
};

return (
<WorkflowBuilder
config={workflowConfig}
onConfigChange={setWorkflowConfig}
onAddConditionalStep={addConditionalStep}
onAddValidationRule={addValidationRule}
/>
);
}

Onboarding Analytics

function OnboardingAnalytics() {
const [analyticsData, setAnalyticsData] = useState({
completionRates: {},
averageTime: {},
dropoffPoints: {},
});

useEffect(() => {
// Track onboarding step completion
const trackStepCompletion = (stepId, timeSpent) => {
analyticsTrackEvent('onboarding_step_completed', {
step_id: stepId,
time_spent: timeSpent,
workflow_id: workflowId,
});
};

// Track onboarding completion
const trackOnboardingCompletion = totalTime => {
analyticsTrackEvent('onboarding_completed', {
total_time: totalTime,
workflow_id: workflowId,
completion_rate: calculateCompletionRate(),
});
};

return () => {
// Cleanup event listeners
};
}, []);

return (
<OnboardingAnalyticsDashboard
data={analyticsData}
onExport={() => exportAnalyticsData()}
/>
);
}