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
- OnboardingEmployeeSidebar - Sidebar navigation for employee onboarding
- EmployeeOnboardingNavbar - Navigation bar for onboarding progress
- AdminOnboardingNav - Admin-specific onboarding navigation
- OnboardingWorkflowSidebar - Workflow management sidebar
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>
);
}
Sidebar with Step Configuration
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>
);
}
Navbar with Custom Actions
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
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| pathname | string | ✓ | - | Current page path |
| isDisabled | boolean | - | false | Disable navigation actions |
| isLoadingNext | boolean | - | false | Loading state for next button |
| handleNext | function | ✓ | - | Next step handler |
| isLoadingFinishLater | boolean | - | false | Loading state for save button |
| handleSaveFinishLater | function | ✓ | - | 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 (
<Flex>
<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}
/>
) : (
<Flex>
<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
-
Progressive Disclosure
- Break complex forms into manageable steps
- Show progress indicators clearly
- Provide step-by-step guidance
-
Error Handling
- Validate steps before allowing progression
- Provide clear error messages
- Allow users to save drafts and continue later
-
Accessibility
- Ensure proper tab order for navigation
- Provide clear focus indicators
- Include proper ARIA labels
State Management
-
Data Persistence
- Save step data as users progress
- Implement auto-save functionality
- Handle browser refresh gracefully
-
Progress Tracking
- Track completion status for each step
- Show overall progress percentage
- Highlight required vs optional steps
-
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
-
Update Import Paths
// Old
import OnboardingNav from 'components/OldOnboarding/Nav';
// New
import AdminOnboardingNav from 'components/EmployeeOnboarding/AdminOnboardingNav'; -
Update Props Structure
// Old
<OnboardingNav
step={2}
onNext={handleNext}
onSave={handleSave}
/>
// New
<AdminOnboardingNav
pathname="/admin-onboarding/employment-details"
handleNext={handleNext}
handleSaveFinishLater={handleSave}
/> -
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()}
/>
);
}