Skip to main content

Upload Components

The Upload Components provide comprehensive file upload functionality for the WorkPayCore Frontend application. The system includes multiple upload component variants designed for different use cases, from simple file uploads to complex multi-file management with previews and validation.

Overview

This document covers all upload-related components that handle file selection, drag-and-drop functionality, file previews, and upload validation within the WorkPayCore Frontend application. All upload components are built using react-dropzone for consistent drag-and-drop behavior.

Components Overview

Core Upload Components

Specialized Upload Components

Legacy Upload Components

  • FileUpload - Original themed file upload
  • FileUploadLeaves - Leave-specific file upload
  • FileUploadLeavesV2 - Enhanced leave file upload

WPFileUploaderV1

Legacy file uploader component with basic drag-and-drop functionality.

Component Location

import WPFileUploaderV1 from 'components/Upload/WPFileUploaderV1';

Features

Basic Upload Functionality

  • Drag and drop file selection
  • Browse files button
  • Single file upload
  • File name display
  • Remove file capability

Form Integration

  • React Hook Form integration
  • Form validation support
  • Error display

Usage Examples

Basic V1 Upload

import WPFileUploaderV1 from 'components/Upload/WPFileUploaderV1';

function BasicUploadV1() {
return (
<Box p={4}>
<WPFileUploaderV1 />
</Box>
);
}

Form Integrated V1 Upload

import WPFileUploaderV1 from 'components/Upload/WPFileUploaderV1';
import { useForm } from 'react-hook-form';

function FormUploadV1() {
const { handleSubmit, watch } = useForm();
const uploadedFile = watch('file');

const onSubmit = data => {
console.log('Uploaded file:', data.file);
};

return (
<form onSubmit={handleSubmit(onSubmit)}>
<WPFileUploaderV1 />
<Button type='submit' disabled={!uploadedFile}>
Submit
</Button>
</form>
);
}

WPFileUploaderV2

Enhanced file uploader with modern UI, multiple file support, and advanced features.

Component Location

import WPFileUploaderV2 from 'components/Upload/WPFileUploaderV2';

Props

PropTypeRequiredDefaultDescription
namestring-'file-upload-v2'Input name attribute
multipleboolean-falseAllow multiple file selection
maxSizeInBytesnumber-200MBMaximum file size
fileTypesarray-['.gif', '.jpg', '.jpeg', '.png', '.pdf']Accepted file types
defaultFilesFile[]-nullPre-loaded files
handleSelectedFilefunction-File selection handler
handleRemoveFilefunction-File removal handler
isInvalidboolean-falseValidation error state
showFileTypesboolean-trueShow accepted file types

TypeScript Interface

interface WPFileUploaderV2Props {
name?: string;
multiple?: boolean;
maxSizeInBytes?: number;
fileTypes?: string[];
defaultFiles?: File[] | null;
handleSelectedFile: (files: File | File[]) => void;
handleRemoveFile: () => void;
isInvalid?: boolean;
showFileTypes: boolean;
}

Enhanced Features

Multi-File Support

  • Multiple file selection
  • File list management
  • Individual file removal
  • File preview with metadata

Modern UI/UX

  • Drag-and-drop zone with visual feedback
  • File preview cards with thumbnails
  • Open/preview functionality
  • Responsive design

File Management

  • File size validation
  • File type restrictions
  • File replacement
  • Batch file operations

Usage Examples

Basic V2 Upload

import WPFileUploaderV2 from 'components/Upload/WPFileUploaderV2';

function BasicUploadV2() {
const [files, setFiles] = useState([]);

const handleFileSelect = selectedFiles => {
setFiles(Array.isArray(selectedFiles) ? selectedFiles : [selectedFiles]);
};

const handleFileRemove = () => {
setFiles([]);
};

return (
<WPFileUploaderV2
name='document-upload'
multiple={true}
maxSizeInBytes={50 * 1024 * 1024} // 50MB
fileTypes={['.pdf', '.doc', '.docx']}
handleSelectedFile={handleFileSelect}
handleRemoveFile={handleFileRemove}
showFileTypes={true}
/>
);
}

Multiple File Upload with Validation

import WPFileUploaderV2 from 'components/Upload/WPFileUploaderV2';

function ValidatedMultiUpload() {
const [files, setFiles] = useState([]);
const [errors, setErrors] = useState([]);

const validateFiles = selectedFiles => {
const newErrors = [];
selectedFiles.forEach((file, index) => {
if (file.size > 10 * 1024 * 1024) {
// 10MB limit
newErrors.push(`File ${index + 1} exceeds size limit`);
}
if (!['image/jpeg', 'image/png', 'application/pdf'].includes(file.type)) {
newErrors.push(`File ${index + 1} has invalid type`);
}
});

setErrors(newErrors);
return newErrors.length === 0;
};

const handleFileSelect = selectedFiles => {
const fileArray = Array.isArray(selectedFiles)
? selectedFiles
: [selectedFiles];

if (validateFiles(fileArray)) {
setFiles(fileArray);
}
};

return (
<VStack spacing={4}>
<WPFileUploaderV2
name='validated-upload'
multiple={true}
maxSizeInBytes={10 * 1024 * 1024}
fileTypes={['.jpg', '.jpeg', '.png', '.pdf']}
handleSelectedFile={handleFileSelect}
handleRemoveFile={() => setFiles([])}
isInvalid={errors.length > 0}
showFileTypes={true}
/>

{errors.length > 0 && (
<Alert status='error'>
<AlertIcon />
<VStack align='start'>
{errors.map((error, index) => (
<Text key={index}>{error}</Text>
))}
</VStack>
</Alert>
)}
</VStack>
);
}

Upload with Default Files

import WPFileUploaderV2 from 'components/Upload/WPFileUploaderV2';

function UploadWithDefaults() {
const [files, setFiles] = useState([]);

// Load existing files from API
useEffect(() => {
const loadExistingFiles = async () => {
const existingFiles = await fetchUserDocuments();
setFiles(existingFiles);
};
loadExistingFiles();
}, []);

return (
<WPFileUploaderV2
name='existing-files-upload'
multiple={true}
defaultFiles={files}
handleSelectedFile={newFiles => {
const fileArray = Array.isArray(newFiles) ? newFiles : [newFiles];
setFiles([...files, ...fileArray]);
}}
handleRemoveFile={() => {
// Custom removal logic
setFiles(files.slice(0, -1));
}}
/>
);
}

FileUploadV2

Advanced upload component with document count control and enhanced file management.

Component Location

import FileUploadV2 from 'components/Upload/FileUploadV2';

Props

PropTypeRequiredDefaultDescription
namestring-'file-upload-v2'Input name
multipleboolean-falseMultiple file support
maxSizeInBytesnumber-200MBMax file size
fileTypesarray-['.gif', '.jpg', '.jpeg', '.png', '.pdf']Accepted types
defaultFilesFile[]-nullDefault files
handleSelectedFilefunction-File selection handler
handleRemoveFilefunction--File removal handler
isInvalidboolean-falseError state
showFileTypesboolean-trueShow file types
showOpenButtonboolean-trueShow open/preview button
requiredDocumentsCountnumber-1Required document count

Enhanced Features

Document Count Management

  • Required document count validation
  • Auto-hide upload area when limit reached
  • Document count-based UI behavior

Advanced File Operations

  • File preview/open functionality
  • Individual file removal
  • File metadata display

Smart UI Behavior

  • Conditional upload area visibility
  • Dynamic file type display
  • Enhanced user feedback

Usage Examples

Document Upload with Count Limit

import FileUploadV2 from 'components/Upload/FileUploadV2';

function DocumentUploadWithLimit() {
const [documents, setDocuments] = useState([]);
const requiredDocs = 3;

const handleFileSelect = files => {
const fileArray = Array.isArray(files) ? files : [files];
setDocuments(prev => [...prev, ...fileArray]);
};

const handleFileRemove = () => {
setDocuments(prev => prev.slice(0, -1));
};

return (
<VStack spacing={4}>
<Text fontWeight='bold'>
Upload Required Documents ({documents.length}/{requiredDocs})
</Text>

<FileUploadV2
name='required-documents'
multiple={true}
fileTypes={['.pdf', '.doc', '.docx']}
maxSizeInBytes={20 * 1024 * 1024} // 20MB
requiredDocumentsCount={requiredDocs}
handleSelectedFile={handleFileSelect}
handleRemoveFile={handleFileRemove}
showOpenButton={true}
showFileTypes={true}
/>

<Progress
value={(documents.length / requiredDocs) * 100}
colorScheme='green'
width='100%'
/>
</VStack>
);
}

Leave Application Upload

import FileUploadV2 from 'components/Upload/FileUploadV2';

function LeaveApplicationUpload() {
const [supportingDocs, setSupportingDocs] = useState([]);

return (
&lt;FormControl&gt;
&lt;FormLabel&gt;Supporting Documents</FormLabel>
<FormHelperText mb={4}>
Upload medical certificates or other supporting documents
</FormHelperText>

<FileUploadV2
name='leave-supporting-docs'
multiple={true}
fileTypes={['.pdf', '.jpg', '.jpeg', '.png']}
maxSizeInBytes={10 * 1024 * 1024}
requiredDocumentsCount={2}
handleSelectedFile={files => {
const fileArray = Array.isArray(files) ? files : [files];
setSupportingDocs(prev => [...prev, ...fileArray]);
}}
handleRemoveFile={() => {
setSupportingDocs(prev => prev.slice(0, -1));
}}
showFileTypes={true}
showOpenButton={true}
/>
</FormControl>
);
}

StableFormFileUpload

Form-integrated upload component with comprehensive validation and error handling.

Component Location

import { StableFormFileUpload } from 'components/Upload/StableFormFileUpload';

Props

PropTypeRequiredDefaultDescription
namestring-Form field name
labelstring-Field label
isRequiredboolean-falseRequired field
isDisabledboolean-falseDisabled state
isInvalidboolean-falseInvalid state
isReadOnlyboolean-falseRead-only state
acceptPropsobject--Accept configuration
multipleboolean-falseMultiple files
valueFile[]-[]Current files
errorstring--Error message
onChangefunction--Change handler
onRemovefunction--Remove handler
maxSizenumber-10MBMax file size
maxFilesnumber-5Max file count

TypeScript Interface

interface StableFormFileUploadProps {
name: string;
label: string;
isRequired?: boolean;
isDisabled?: boolean;
isInvalid?: boolean;
isReadOnly?: boolean;
acceptProps?: {
mimeTypes: readonly string[];
displayText?: string;
};
multiple?: boolean;
value?: File[];
error?: string;
onChange?: (files: File[]) => void;
onRemove?: (file: File) => void;
maxSize?: number;
maxFiles?: number;
gridGap?: number;
justify?: string;
padding?: string;
minHeight?: number;
flexDir?: 'row' | 'column';
}

Form Integration Features

Chakra UI Form Integration

  • FormControl integration
  • FormLabel and validation
  • FormErrorMessage display
  • Accessibility support

Advanced Validation

  • File size validation
  • File type validation
  • File count limits
  • Custom error messages

File Preview System

  • File preview cards
  • File metadata display
  • Individual file removal
  • File type icons

Usage Examples

Basic Form Upload

import { StableFormFileUpload } from 'components/Upload/StableFormFileUpload';

function FormWithUpload() {
const [files, setFiles] = useState([]);
const [error, setError] = useState('');

const handleFileChange = selectedFiles => {
setFiles(selectedFiles);
setError(''); // Clear errors on successful selection
};

const handleFileRemove = fileToRemove => {
setFiles(files.filter(file => file !== fileToRemove));
};

return (
<VStack spacing={6}>
<StableFormFileUpload
name='documents'
label='Upload Documents'
isRequired={true}
multiple={true}
value={files}
error={error}
onChange={handleFileChange}
onRemove={handleFileRemove}
acceptProps={{
mimeTypes: ['application/pdf', 'image/jpeg', 'image/png'],
displayText: 'Accepts PDF, JPEG, and PNG files (10MB max)',
}}
maxSize={10 * 1024 * 1024} // 10MB
maxFiles={3}
/>
</VStack>
);
}

Employee Onboarding Upload

import { StableFormFileUpload } from 'components/Upload/StableFormFileUpload';
import { useForm, Controller } from 'react-hook-form';

function EmployeeOnboardingForm() {
const {
control,
handleSubmit,
formState: { errors },
} = useForm();

const onSubmit = data => {
console.log('Form data:', data);
};

return (
<form onSubmit={handleSubmit(onSubmit)}>
<VStack spacing={6}>
<Controller
name='resume'
control={control}
rules={{ required: 'Resume is required' }}
render={({ field, fieldState }) => (
<StableFormFileUpload
name='resume'
label='Resume/CV'
isRequired={true}
value={field.value || []}
error={fieldState.error?.message}
onChange={field.onChange}
onRemove={file => {
const newFiles = field.value?.filter(f => f !== file) || [];
field.onChange(newFiles);
}}
acceptProps={{
mimeTypes: ['application/pdf', 'application/msword'],
displayText: 'PDF or Word documents only',
}}
maxSize={5 * 1024 * 1024} // 5MB
maxFiles={1}
/>
)}
/>

<Controller
name='certificates'
control={control}
render={({ field, fieldState }) => (
<StableFormFileUpload
name='certificates'
label='Certificates (Optional)'
multiple={true}
value={field.value || []}
error={fieldState.error?.message}
onChange={field.onChange}
onRemove={file => {
const newFiles = field.value?.filter(f => f !== file) || [];
field.onChange(newFiles);
}}
acceptProps={{
mimeTypes: ['application/pdf', 'image/jpeg', 'image/png'],
displayText: 'PDF or image files',
}}
maxFiles={5}
/>
)}
/>

<Button type='submit' colorScheme='blue'>
Submit Application
</Button>
</VStack>
</form>
);
}

Validation with Custom Error Handling

import { StableFormFileUpload } from 'components/Upload/StableFormFileUpload';

function ValidatedUpload() {
const [files, setFiles] = useState([]);
const [error, setError] = useState('');

const validateFiles = selectedFiles => {
// Custom validation logic
const totalSize = selectedFiles.reduce((sum, file) => sum + file.size, 0);

if (totalSize > 50 * 1024 * 1024) {
// 50MB total
return 'Total file size exceeds 50MB limit';
}

const invalidFiles = selectedFiles.filter(
file =>
!file.type.startsWith('image/') && file.type !== 'application/pdf',
);

if (invalidFiles.length > 0) {
return 'Only images and PDF files are allowed';
}

return null;
};

const handleFileChange = selectedFiles => {
const validationError = validateFiles(selectedFiles);

if (validationError) {
setError(validationError);
} else {
setFiles(selectedFiles);
setError('');
}
};

return (
<StableFormFileUpload
name='validated-files'
label='Upload Files with Validation'
multiple={true}
value={files}
error={error}
onChange={handleFileChange}
onRemove={file => {
const newFiles = files.filter(f => f !== file);
setFiles(newFiles);
setError(''); // Clear error when removing files
}}
acceptProps={{
mimeTypes: ['image/*', 'application/pdf'],
displayText: 'Images and PDF files only',
}}
maxFiles={10}
/>
);
}

WPImageUploader

Specialized image upload component with preview functionality and React Hook Form integration.

Component Location

import WPImageUploader from 'components/Upload/WPImageUploader';

Props

PropTypeRequiredDefaultDescription
namestring-Form field name
isDisabledboolean-falseDisabled state
previewstring--Preview image URL
removeImagefunction--Remove image handler
replaceImgfunction--Replace image handler
acceptobject--Accepted file types

Features

Image-Specific Functionality

  • Image preview with thumbnails
  • Image replacement workflow
  • Drag-and-drop image selection
  • Image file validation

Form Integration

  • React Hook Form integration
  • Automatic form registration
  • Form validation support

Usage Examples

Basic Image Upload

import WPImageUploader from 'components/Upload/WPImageUploader';
import { useForm, FormProvider } from 'react-hook-form';

function ImageUploadForm() {
const methods = useForm();
const [previewUrl, setPreviewUrl] = useState('');

const handleImageRemove = () => {
setPreviewUrl('');
methods.setValue('profileImage', null);
};

const handleImageReplace = () => {
setPreviewUrl('');
methods.setValue('profileImage', null);
};

return (
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)}>
<WPImageUploader
name='profileImage'
preview={previewUrl}
removeImage={handleImageRemove}
replaceImg={handleImageReplace}
accept={{
'image/*': ['.png', '.jpg', '.jpeg', '.gif'],
}}
/>
<Button type='submit'>Save</Button>
</form>
</FormProvider>
);
}

Profile Picture Upload

import WPImageUploader from 'components/Upload/WPImageUploader';

function ProfilePictureUpload() {
const [currentImage, setCurrentImage] = useState(user.profilePicture);

return (
<VStack spacing={4}>
<Avatar size='xl' src={currentImage} name={user.name} />

<FormProvider {...methods}>
<WPImageUploader
name='profilePicture'
preview={currentImage}
removeImage={() => {
setCurrentImage('');
// API call to remove profile picture
}}
replaceImg={() => {
setCurrentImage('');
}}
accept={{
'image/*': ['.png', '.jpg', '.jpeg'],
}}
/>
</FormProvider>
</VStack>
);
}

WPProfilePicUpload

Minimal profile picture upload component for simple use cases.

Component Location

import WPProfilePicUpload from 'components/Upload/WPProfilePicUpload';

Props

PropTypeRequiredDefaultDescription
handleSelectedFilefunction-File selection handler
namestring-Input name
labelstring-Upload label/button

Usage Examples

import WPProfilePicUpload from 'components/Upload/WPProfilePicUpload';

function SimpleProfileUpload() {
const handleFileSelect = files => {
const file = files[0];
if (file) {
// Process uploaded file
uploadProfilePicture(file);
}
};

return (
<WPProfilePicUpload
name='profile-pic'
label={&lt;Button&gt;Change Profile Picture</Button>}
handleSelectedFile={handleFileSelect}
/>
);
}

WPControlledFileUpload

Controlled upload component for use with external state management.

Component Location

import WPControlledFileUpload from 'components/Upload/WPControledFileUpload';

Props

PropTypeRequiredDefaultDescription
valueobject-Current file value
onChangefunction-Value change handler
heightstring--Component height
typestring--Theme type

Usage Examples

import WPControlledFileUpload from 'components/Upload/WPControledFileUpload';

function ControlledUpload() {
const [selectedFile, setSelectedFile] = useState(null);

return (
<WPControlledFileUpload
value={selectedFile}
onChange={setSelectedFile}
type='fileB'
height='200px'
/>
);
}

GlobalFileUpload

Global utility upload component for general file upload needs.

Component Location

import GlobalFileUpload from 'components/Upload/GlobalFileUpload';

Props

PropTypeRequiredDefaultDescription
setDefaultFileNamestring--Default file name
getFilefunction-File handler

Usage Examples

import GlobalFileUpload from 'components/Upload/GlobalFileUpload';

function GeneralUpload() {
const handleFileUpload = files => {
const file = files[0];
// Process file
console.log('Uploaded:', file.name);
};

return (
<GlobalFileUpload
getFile={handleFileUpload}
setDefaultFileName='document.pdf'
/>
);
}

Upload Component Patterns

File Validation Patterns

// Common file validation utilities
const validateFileSize = (file, maxSizeInBytes) => {
return file.size <= maxSizeInBytes;
};

const validateFileType = (file, allowedTypes) => {
return allowedTypes.some(
type => file.type.includes(type) || file.name.endsWith(type),
);
};

const validateFiles = (files, options = {}) => {
const {
maxSize = 10 * 1024 * 1024, // 10MB
allowedTypes = ['image/*', 'application/pdf'],
maxFiles = 5,
} = options;

const errors = [];

if (files.length > maxFiles) {
errors.push(`Maximum ${maxFiles} files allowed`);
}

files.forEach((file, index) => {
if (!validateFileSize(file, maxSize)) {
errors.push(`File ${index + 1} exceeds size limit`);
}

if (!validateFileType(file, allowedTypes)) {
errors.push(`File ${index + 1} has invalid type`);
}
});

return errors;
};

Upload Progress Pattern

import { useState } from 'react';

function UploadWithProgress() {
const [uploadProgress, setUploadProgress] = useState(0);
const [isUploading, setIsUploading] = useState(false);

const uploadFile = async file => {
setIsUploading(true);
setUploadProgress(0);

const formData = new FormData();
formData.append('file', file);

try {
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
// Track upload progress
onUploadProgress: progressEvent => {
const progress = Math.round(
(progressEvent.loaded * 100) / progressEvent.total,
);
setUploadProgress(progress);
},
});

if (response.ok) {
console.log('Upload successful');
}
} catch (error) {
console.error('Upload failed:', error);
} finally {
setIsUploading(false);
setUploadProgress(0);
}
};

return (
<VStack spacing={4}>
<WPFileUploaderV2
handleSelectedFile={files => {
const file = Array.isArray(files) ? files[0] : files;
uploadFile(file);
}}
handleRemoveFile={() => {}}
/>

{isUploading && (
<Box width='100%'>
<Text mb={2}>Uploading... {uploadProgress}%</Text>
<Progress value={uploadProgress} colorScheme='green' />
</Box>
)}
</VStack>
);
}

Multiple Upload Management

function MultipleUploadManager() {
const [files, setFiles] = useState([]);
const [uploadedFiles, setUploadedFiles] = useState([]);

const handleFileSelect = newFiles => {
const fileArray = Array.isArray(newFiles) ? newFiles : [newFiles];
setFiles(prev => [...prev, ...fileArray]);
};

const handleFileRemove = fileToRemove => {
setFiles(files.filter(file => file !== fileToRemove));
};

const uploadAllFiles = async () => {
const uploadPromises = files.map(async file => {
const formData = new FormData();
formData.append('file', file);

const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
});

return response.json();
});

try {
const results = await Promise.all(uploadPromises);
setUploadedFiles(results);
setFiles([]); // Clear pending files
} catch (error) {
console.error('Batch upload failed:', error);
}
};

return (
<VStack spacing={4}>
<WPFileUploaderV2
multiple={true}
handleSelectedFile={handleFileSelect}
handleRemoveFile={handleFileRemove}
/>

{files.length > 0 && (
<Button onClick={uploadAllFiles} colorScheme='blue'>
Upload All Files ({files.length})
</Button>
)}

{uploadedFiles.length > 0 && (
&lt;Box&gt;
<Text fontWeight='bold'>Uploaded Files:</Text>
{uploadedFiles.map((file, index) => (
<Text key={index}>{file.filename}</Text>
))}
</Box>
)}
</VStack>
);
}

File Type Configuration

Common File Type Patterns

// Image files
const imageTypes = {
mimeTypes: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
extensions: ['.jpg', '.jpeg', '.png', '.gif', '.webp'],
displayText: 'JPEG, PNG, GIF, or WebP images',
};

// Document files
const documentTypes = {
mimeTypes: [
'application/pdf',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
],
extensions: ['.pdf', '.doc', '.docx'],
displayText: 'PDF or Word documents',
};

// Spreadsheet files
const spreadsheetTypes = {
mimeTypes: [
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'text/csv',
],
extensions: ['.xls', '.xlsx', '.csv'],
displayText: 'Excel or CSV files',
};

// All office files
const officeTypes = {
mimeTypes: [
'application/pdf',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.ms-powerpoint',
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
],
extensions: ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx'],
displayText: 'Office documents and PDFs',
};

File Type Usage Examples

// Employee document upload
function EmployeeDocumentUpload() {
return (
<StableFormFileUpload
name='employee-documents'
label='Employee Documents'
acceptProps={documentTypes}
multiple={true}
maxFiles={5}
/>
);
}

// Profile picture upload
function ProfileImageUpload() {
return (
<WPImageUploader
name='profile-image'
accept={{
'image/*': imageTypes.extensions,
}}
/>
);
}

// Payroll data upload
function PayrollDataUpload() {
return (
<FileUploadV2
name='payroll-data'
fileTypes={spreadsheetTypes.extensions}
maxSizeInBytes={25 * 1024 * 1024} // 25MB
showFileTypes={true}
/>
);
}

Styling and Theming

Upload Component Styling

// Custom styled upload
function StyledUpload() {
return (
<Box
border='2px dashed'
borderColor='blue.300'
borderRadius='lg'
p={6}
bg='blue.50'
_hover={{
borderColor: 'blue.500',
bg: 'blue.100',
}}
>
<WPFileUploaderV2
handleSelectedFile={handleFileSelect}
handleRemoveFile={handleFileRemove}
/>
</Box>
);
}

Responsive Upload Areas

function ResponsiveUpload() {
return (
<Box
width={{ base: '100%', md: '500px' }}
height={{ base: '150px', md: '200px' }}
>
<WPFileUploaderV2
handleSelectedFile={handleFileSelect}
handleRemoveFile={handleFileRemove}
/>
</Box>
);
}

Accessibility

Screen Reader Support

function AccessibleUpload() {
return (
&lt;FormControl&gt;
<FormLabel htmlFor='accessible-upload'>
Upload Required Documents
</FormLabel>
&lt;FormHelperText&gt;
Upload PDF or image files. Maximum 10MB per file.
</FormHelperText>

<StableFormFileUpload
name='accessible-upload'
label='Required Documents'
acceptProps={{
mimeTypes: ['application/pdf', 'image/*'],
displayText: 'PDF or image files (10MB max)',
}}
multiple={true}
/>
</FormControl>
);
}

Keyboard Navigation

function KeyboardAccessibleUpload() {
const [isFocused, setIsFocused] = useState(false);

return (
<Box
onKeyDown={e => {
if (e.key === 'Enter' || e.key === ' ') {
// Trigger file picker
document.getElementById('file-input').click();
}
}}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
outline={isFocused ? '2px solid' : 'none'}
outlineColor='blue.500'
tabIndex={0}
role='button'
aria-label='Upload files'
>
<WPFileUploaderV2
name='keyboard-upload'
handleSelectedFile={handleFileSelect}
handleRemoveFile={handleFileRemove}
/>
</Box>
);
}

Testing Strategies

Upload Component Testing

import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import WPFileUploaderV2 from 'components/Upload/WPFileUploaderV2';

describe('WPFileUploaderV2', () => {
const mockHandleSelectedFile = jest.fn();
const mockHandleRemoveFile = jest.fn();

beforeEach(() => {
mockHandleSelectedFile.mockClear();
mockHandleRemoveFile.mockClear();
});

it('renders upload area', () => {
render(
<WPFileUploaderV2
handleSelectedFile={mockHandleSelectedFile}
handleRemoveFile={mockHandleRemoveFile}
/>,
);

expect(screen.getByText(/drag and drop files here/i)).toBeInTheDocument();
expect(screen.getByText(/click to upload/i)).toBeInTheDocument();
});

it('handles file selection', async () => {
const user = userEvent.setup();

render(
<WPFileUploaderV2
handleSelectedFile={mockHandleSelectedFile}
handleRemoveFile={mockHandleRemoveFile}
/>,
);

const file = new File(['test content'], 'test.pdf', {
type: 'application/pdf',
});

const input = screen.getByRole('button');
await user.upload(input, file);

await waitFor(() => {
expect(mockHandleSelectedFile).toHaveBeenCalledWith([file]);
});
});

it('shows file preview after upload', async () => {
const file = new File(['test'], 'test.pdf', { type: 'application/pdf' });

render(
<WPFileUploaderV2
defaultFiles={[file]}
handleSelectedFile={mockHandleSelectedFile}
handleRemoveFile={mockHandleRemoveFile}
/>,
);

expect(screen.getByText('test.pdf')).toBeInTheDocument();
expect(screen.getByText(/file size/i)).toBeInTheDocument();
});

it('handles file removal', async () => {
const user = userEvent.setup();
const file = new File(['test'], 'test.pdf', { type: 'application/pdf' });

render(
<WPFileUploaderV2
defaultFiles={[file]}
handleSelectedFile={mockHandleSelectedFile}
handleRemoveFile={mockHandleRemoveFile}
/>,
);

const removeButton = screen.getByRole('button', { name: /remove/i });
await user.click(removeButton);

expect(mockHandleRemoveFile).toHaveBeenCalled();
});

it('validates file types', () => {
render(
<WPFileUploaderV2
fileTypes={['.pdf']}
isInvalid={true}
handleSelectedFile={mockHandleSelectedFile}
handleRemoveFile={mockHandleRemoveFile}
/>,
);

const uploadArea = screen.getByRole('button');
expect(uploadArea).toHaveStyle('border-color: red');
});
});

Integration Testing

// Test complete upload workflow
describe('Upload Integration', () => {
it('uploads file and shows success', async () => {
const mockUploadAPI = jest.fn().mockResolvedValue({
success: true,
fileId: '123',
});

render(<UploadWithAPIIntegration uploadAPI={mockUploadAPI} />);

const file = new File(['content'], 'test.pdf', {
type: 'application/pdf',
});

// Upload file
const input = screen.getByRole('button');
await userEvent.upload(input, file);

// Click upload button
const uploadButton = screen.getByText('Upload');
await userEvent.click(uploadButton);

// Wait for success message
await waitFor(() => {
expect(screen.getByText('Upload successful')).toBeInTheDocument();
});

expect(mockUploadAPI).toHaveBeenCalledWith(
expect.objectContaining({
file: expect.any(File),
}),
);
});
});

Performance Optimization

File Processing Optimization

// Optimize large file handling
function OptimizedUpload() {
const [isProcessing, setIsProcessing] = useState(false);

const processFiles = useCallback(async files => {
setIsProcessing(true);

try {
// Process files in chunks for better performance
const chunkSize = 3;
const chunks = [];

for (let i = 0; i < files.length; i += chunkSize) {
chunks.push(files.slice(i, i + chunkSize));
}

for (const chunk of chunks) {
await Promise.all(chunk.map(file => processIndividualFile(file)));
}
} finally {
setIsProcessing(false);
}
}, []);

return (
&lt;Box&gt;
<WPFileUploaderV2
multiple={true}
handleSelectedFile={processFiles}
handleRemoveFile={() => {}}
/>

{isProcessing && <Spinner />}
</Box>
);
}

Memory Management

// Clean up object URLs to prevent memory leaks
function MemoryOptimizedUpload() {
const [files, setFiles] = useState([]);
const [previews, setPreviews] = useState([]);

useEffect(() => {
// Create object URLs for previews
const newPreviews = files.map(file => ({
file,
url: URL.createObjectURL(file),
}));

setPreviews(newPreviews);

// Cleanup function
return () => {
newPreviews.forEach(preview => {
URL.revokeObjectURL(preview.url);
});
};
}, [files]);

return (
&lt;VStack&gt;
<WPFileUploaderV2
handleSelectedFile={setFiles}
handleRemoveFile={() => setFiles([])}
/>

{previews.map(({ file, url }) => (
<Image key={file.name} src={url} alt={file.name} />
))}
</VStack>
);
}

Best Practices

Upload Component Selection

  • Use WPFileUploaderV2 for new implementations requiring modern UI
  • Use StableFormFileUpload for form-integrated uploads with validation
  • Use FileUploadV2 when document count control is needed
  • Use WPImageUploader specifically for image uploads with preview

File Validation

  • Always validate file size to prevent large uploads
  • Restrict file types based on security requirements
  • Implement client and server validation for robust security
  • Provide clear error messages for validation failures

User Experience

  • Show upload progress for large files
  • Provide file previews when possible
  • Enable drag-and-drop for better usability
  • Clear error states and recovery options

Performance

  • Process files in chunks for large batches
  • Clean up object URLs to prevent memory leaks
  • Implement file compression for images when appropriate
  • Use progressive upload for very large files

This comprehensive upload system provides flexible, robust file upload capabilities for all use cases within the WorkPayCore Frontend application.