Drawer Components
The Drawer Components provide slide-out panel functionality for the WorkPayCore Frontend application. These components offer various drawer implementations for different use cases including actions, navigation, filtering, and help content.
Overview
This document covers all drawer-related components that provide slide-out panel functionality with consistent styling and behavior patterns.
Components Overview
Main Drawers
- ActionDrawer - Comprehensive drawer for actions and content display
- SideBarDrawer - Mobile sidebar navigation drawer
Specialized Drawers
- Filter - Filter and exempt functionality drawer
- GetHelpDrawer - Help and support content drawer
ActionDrawer
A comprehensive drawer component with header, body, and footer sections for various actions and content display.
Component Location
import ActionDrawer from 'components/Drawers';
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| isOpen | boolean | ✓ | - | Drawer open state |
| onClose | function | ✓ | - | Close handler |
| children | ReactNode | - | - | Drawer content |
| heading | ReactNode | - | - | Drawer heading |
| width | string | - | - | Custom width |
| maxH | string | - | 'auto' | Maximum height |
| minH | string | - | 'auto' | Minimum height |
| minBodyHeight | string | - | - | Minimum body height |
| noDivider | boolean | - | false | Hide header divider |
| noHeading | boolean | - | false | Hide heading |
| noCloseButton | boolean | - | false | Hide close button |
| isSuccess | boolean | - | false | Success state |
| successComponent | ReactNode | - | - | Success state content |
| footer | ReactNode | - | - | Footer content |
| headingStyles | object | - | { fontSize: '20px', textTransform: 'capitalize' } | Heading styles |
| drawerBg | string | - | 'white' | Drawer background color |
| ...rest | DrawerProps | - | - | Additional Chakra UI Drawer props |
TypeScript Interface
interface ActionDrawerProps extends DrawerProps {
isOpen: boolean;
onClose: () => void;
heading?: React.ReactNode | string;
width?: BoxProps['width'];
maxH?: BoxProps['maxHeight'];
minH?: BoxProps['minHeight'];
minBodyHeight?: BoxProps['minHeight'];
noDivider?: boolean;
noHeading?: boolean;
isSuccess?: boolean;
successComponent?: React.ReactNode;
noCloseButton?: boolean;
footer?: React.ReactNode;
headingStyles?: BoxProps;
drawerBg?: BoxProps['backgroundColor'];
}
Features
Flexible Layout
- Header with customizable heading
- Body with scrollable content
- Footer with action buttons
- Divider between sections (optional)
Success State Handling
- Success state with custom component
- Replaces normal content when active
- Useful for form completion feedback
Responsive Design
- Right-side placement by default
- Medium size by default
- Centered positioning
- Custom width support
Accessibility
- Print-friendly (hides close button in print)
- Proper focus management
- Keyboard navigation support
Usage Examples
Basic Action Drawer
import ActionDrawer from 'components/Drawers';
function EditUserDrawer() {
const [isOpen, setIsOpen] = useState(false);
return (
<ActionDrawer
isOpen={isOpen}
onClose={() => setIsOpen(false)}
heading='Edit User'
>
<UserEditForm />
</ActionDrawer>
);
}
Drawer with Footer
import ActionDrawer from 'components/Drawers';
function ConfirmActionDrawer() {
const [isOpen, setIsOpen] = useState(false);
const footerContent = (
<HStack spacing={4}>
<Button variant='outline' onClick={() => setIsOpen(false)}>
Cancel
</Button>
<Button colorScheme='blue' onClick={handleSave}>
Save Changes
</Button>
</HStack>
);
return (
<ActionDrawer
isOpen={isOpen}
onClose={() => setIsOpen(false)}
heading='Confirm Action'
footer={footerContent}
>
<Text>Are you sure you want to perform this action?</Text>
</ActionDrawer>
);
}
Success State Drawer
import ActionDrawer from 'components/Drawers';
function FormDrawer() {
const [isOpen, setIsOpen] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
const handleSubmit = async () => {
try {
await submitForm();
setIsSuccess(true);
} catch (error) {
console.error('Form submission failed');
}
};
const successComponent = (
<VStack spacing={4} p={8}>
<CheckCircleIcon color='green.500' boxSize={12} />
<Heading size='md'>Form Submitted Successfully!</Heading>
<Text textAlign='center'>
Your form has been submitted and is being processed.
</Text>
<Button onClick={() => setIsOpen(false)}>Close</Button>
</VStack>
);
return (
<ActionDrawer
isOpen={isOpen}
onClose={() => setIsOpen(false)}
heading='Submit Form'
isSuccess={isSuccess}
successComponent={successComponent}
>
<FormContent onSubmit={handleSubmit} />
</ActionDrawer>
);
}
Custom Styling
import ActionDrawer from 'components/Drawers';
function CustomDrawer() {
const [isOpen, setIsOpen] = useState(false);
return (
<ActionDrawer
isOpen={isOpen}
onClose={() => setIsOpen(false)}
heading='Custom Drawer'
width='600px'
maxH='80vh'
drawerBg='gray.50'
headingStyles={{
fontSize: '24px',
fontWeight: 'bold',
color: 'blue.600',
}}
>
<CustomContent />
</ActionDrawer>
);
}
Drawer without Header
import ActionDrawer from 'components/Drawers';
function SimpleDrawer() {
const [isOpen, setIsOpen] = useState(false);
return (
<ActionDrawer
isOpen={isOpen}
onClose={() => setIsOpen(false)}
noHeading
noDivider
noCloseButton
>
<CustomHeaderContent />
<MainContent />
</ActionDrawer>
);
}
Form Drawer with Validation
import ActionDrawer from 'components/Drawers';
function FormDrawer() {
const [isOpen, setIsOpen] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = async formData => {
setIsSubmitting(true);
try {
await submitData(formData);
setIsOpen(false);
} catch (error) {
console.error('Submission failed');
} finally {
setIsSubmitting(false);
}
};
const footerContent = (
<HStack spacing={4}>
<Button
variant='outline'
onClick={() => setIsOpen(false)}
isDisabled={isSubmitting}
>
Cancel
</Button>
<Button
colorScheme='blue'
onClick={handleSubmit}
isLoading={isSubmitting}
loadingText='Saving...'
>
Save
</Button>
</HStack>
);
return (
<ActionDrawer
isOpen={isOpen}
onClose={() => setIsOpen(false)}
heading='Edit Profile'
footer={footerContent}
minBodyHeight='400px'
>
<ProfileForm onSubmit={handleSubmit} />
</ActionDrawer>
);
}
Styling
- Placement: Right side by default
- Size: Medium (md) by default
- Background: White by default
- Header: 20px top padding, fluid-header text style
- Footer: Top border, 8 units vertical padding
- Close Button: Hidden in print mode
SideBarDrawer
A mobile sidebar drawer component that displays the main navigation sidebar in a slide-out panel.
Component Location
import SideBarDrawer from 'components/SideBarDrawer';
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| isOpen | boolean | ✓ | - | Drawer open state |
| onClose | function | ✓ | - | Close handler |
TypeScript Interface
interface SideBarDrawerProps {
isOpen: boolean;
onClose: () => void;
}
Features
Mobile Navigation
- Left-side placement for mobile sidebar
- Full-height sidebar display
- Responsive design for mobile screens
Sidebar Integration
- Integrates with main Sidebar component
- Passes close handler to sidebar
- Maintains sidebar functionality
Usage Examples
Basic Mobile Sidebar
import SideBarDrawer from 'components/SideBarDrawer';
function MobileLayout() {
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
return (
<Box>
<MobileHeader onMenuClick={() => setIsSidebarOpen(true)} />
<SideBarDrawer
isOpen={isSidebarOpen}
onClose={() => setIsSidebarOpen(false)}
/>
<MainContent />
</Box>
);
}
Responsive Navigation
import SideBarDrawer from 'components/SideBarDrawer';
import { useBreakpointValue } from '@chakra-ui/react';
function ResponsiveLayout() {
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const isMobile = useBreakpointValue({ base: true, md: false });
return (
<Box>
{isMobile ? (
<SideBarDrawer
isOpen={isMobileMenuOpen}
onClose={() => setIsMobileMenuOpen(false)}
/>
) : (
<DesktopSidebar />
)}
<MainContent />
</Box>
);
}
Integration with Navbar
import SideBarDrawer from 'components/SideBarDrawer';
import Navbar from 'components/Navbar';
function AppLayout() {
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
return (
<Box>
<Navbar onDrawerOpen={() => setIsDrawerOpen(true)} />
<SideBarDrawer
isOpen={isDrawerOpen}
onClose={() => setIsDrawerOpen(false)}
/>
<MainContent />
</Box>
);
}
Styling
- Placement: Left side
- Size: Full width
- Sidebar: 100% width, fixed position, full height
- Content: No padding on left side for flush sidebar
Filter
A specialized drawer component for filtering and exempt functionality with tabbed interface.
Component Location
import Filter from 'components/Filter/Filter';
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| isOpen | boolean | ✓ | - | Drawer open state |
| onClose | function | ✓ | - | Close handler |
TypeScript Interface
interface FilterProps {
isOpen: boolean;
onClose: () => void;
}
Features
Tabbed Interface
- Filter tab for standard filtering
- Exempt tab for exempt functionality
- Horizontal tab orientation
- Default to Filter tab
Filter Functionality
- Employee filtering
- Department filtering
- Status filtering
- Form-based interface
Usage Examples
Basic Filter Drawer
import Filter from 'components/Filter/Filter';
function DataTable() {
const [isFilterOpen, setIsFilterOpen] = useState(false);
return (
<Box>
<HStack justify='space-between'>
<Heading>Employee List</Heading>
<Button onClick={() => setIsFilterOpen(true)}>Open Filter</Button>
</HStack>
<Filter isOpen={isFilterOpen} onClose={() => setIsFilterOpen(false)} />
<EmployeeTable />
</Box>
);
}
Filter with State Management
import Filter from 'components/Filter/Filter';
function FilteredDataView() {
const [isFilterOpen, setIsFilterOpen] = useState(false);
const [filterState, setFilterState] = useState({});
const handleFilterClose = () => {
setIsFilterOpen(false);
// Process filter state
applyFilters(filterState);
};
return (
<Box>
<DataControls onFilterClick={() => setIsFilterOpen(true)} />
<Filter isOpen={isFilterOpen} onClose={handleFilterClose} />
<DataDisplay filters={filterState} />
</Box>
);
}
Integration with Table
import Filter from 'components/Filter/Filter';
import { useDisclosure } from '@chakra-ui/react';
function TableWithFilter() {
const { isOpen, onOpen, onClose } = useDisclosure();
return (
<VStack spacing={4}>
<HStack justify='space-between' w='full'>
<Heading>Data Table</Heading>
<Button onClick={onOpen}>Filter</Button>
</HStack>
<Filter isOpen={isOpen} onClose={onClose} />
<DataTable />
</VStack>
);
}
Styling
- Placement: Right side
- Size: Default drawer size
- Padding: 5 units vertical
- Tabs: Horizontal orientation
- Auto Focus: Disabled for better UX
GetHelpDrawer
A specialized drawer component for help and support content integrated with the navbar.
Component Location
import { GetHelpDrawerComponent } from 'components/Navbar/GetHelp/drawer';
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| drawerDisclosure | object | ✓ | - | Chakra UI disclosure object |
TypeScript Interface
interface GetHelpDrawerProps {
drawerDisclosure: {
isOpen: boolean;
onOpen: () => void;
onClose: () => void;
};
}
Features
Help Content Sections
- Help topics with external links
- Support channel access
- Feature request functionality
- Contact information
External Integration
- Links to knowledge base
- WhatsApp support integration
- Email support links
- External documentation
Usage Examples
Basic Help Integration
import { GetHelpDrawerComponent } from 'components/Navbar/GetHelp/drawer';
import { useDisclosure } from '@chakra-ui/react';
function HelpIntegration() {
const drawerDisclosure = useDisclosure();
return (
<Box>
<Button onClick={drawerDisclosure.onOpen}>Get Help</Button>
<GetHelpDrawerComponent drawerDisclosure={drawerDisclosure} />
</Box>
);
}
Navbar Integration
import { GetHelpDrawerComponent } from 'components/Navbar/GetHelp/drawer';
function NavbarWithHelp() {
const helpDrawerDisclosure = useDisclosure();
return (
<Box>
<Navbar onHelpClick={helpDrawerDisclosure.onOpen} />
<GetHelpDrawerComponent drawerDisclosure={helpDrawerDisclosure} />
</Box>
);
}
Styling
- Placement: Right side
- Size: Small (sm)
- Background: White
- Header: Normal font weight
- Sections: Organized help content
Drawer Patterns
Form Drawer Pattern
import ActionDrawer from 'components/Drawers';
function useFormDrawer() {
const [isOpen, setIsOpen] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const openDrawer = () => setIsOpen(true);
const closeDrawer = () => setIsOpen(false);
const handleSubmit = async formData => {
setIsLoading(true);
try {
await submitData(formData);
closeDrawer();
} catch (error) {
console.error('Submission failed');
} finally {
setIsLoading(false);
}
};
const FormDrawer = ({ children, title, onSubmit }) => (
<ActionDrawer
isOpen={isOpen}
onClose={closeDrawer}
heading={title}
footer={
<HStack spacing={4}>
<Button
variant='outline'
onClick={closeDrawer}
isDisabled={isLoading}
>
Cancel
</Button>
<Button
colorScheme='blue'
onClick={() => onSubmit(formData)}
isLoading={isLoading}
>
Submit
</Button>
</HStack>
}
>
{children}
</ActionDrawer>
);
return { openDrawer, closeDrawer, FormDrawer, handleSubmit };
}
Responsive Drawer Pattern
import { useBreakpointValue } from '@chakra-ui/react';
function ResponsiveDrawerPattern() {
const drawerSize = useBreakpointValue({ base: 'full', md: 'md', lg: 'lg' });
return (
<ActionDrawer
isOpen={isOpen}
onClose={onClose}
size={drawerSize}
heading='Responsive Drawer'
>
<ResponsiveContent />
</ActionDrawer>
);
}
Multi-Step Drawer Pattern
function MultiStepDrawer() {
const [currentStep, setCurrentStep] = useState(0);
const [isOpen, setIsOpen] = useState(false);
const steps = [
{ title: 'Step 1', component: <Step1Content /> },
{ title: 'Step 2', component: <Step2Content /> },
{ title: 'Step 3', component: <Step3Content /> },
];
const currentStepData = steps[currentStep];
const footerContent = (
<HStack spacing={4}>
<Button
variant='outline'
onClick={() => setCurrentStep(prev => Math.max(0, prev - 1))}
isDisabled={currentStep === 0}
>
Previous
</Button>
<Button
colorScheme='blue'
onClick={() =>
setCurrentStep(prev => Math.min(steps.length - 1, prev + 1))
}
isDisabled={currentStep === steps.length - 1}
>
Next
</Button>
</HStack>
);
return (
<ActionDrawer
isOpen={isOpen}
onClose={() => setIsOpen(false)}
heading={currentStepData.title}
footer={footerContent}
>
<VStack spacing={4}>
<Progress value={((currentStep + 1) / steps.length) * 100} />
{currentStepData.component}
</VStack>
</ActionDrawer>
);
}
Best Practices
Drawer Selection
- ActionDrawer: Use for forms, detailed content, and actions
- SideBarDrawer: Use for mobile navigation only
- Filter: Use specifically for filtering functionality
- GetHelpDrawer: Use for help and support content
Performance
- Lazy Loading: Load drawer content only when needed
- Memory Management: Clean up drawer state on close
- Event Handlers: Properly handle drawer events
- Form State: Manage form state appropriately
User Experience
- Smooth Transitions: Ensure smooth open/close animations
- Focus Management: Handle focus properly on open/close
- Escape Key: Allow escape key to close drawer
- Overlay Click: Configure overlay click behavior
Accessibility
- ARIA Labels: Provide proper ARIA labels
- Keyboard Navigation: Ensure keyboard accessibility
- Screen Readers: Support screen reader navigation
- Focus Trapping: Trap focus within drawer when open
Testing
import { render, screen, fireEvent } from '@testing-library/react';
import ActionDrawer from 'components/Drawers';
describe('Drawer Components', () => {
it('should open and close drawer', () => {
const onClose = jest.fn();
render(
<ActionDrawer isOpen={true} onClose={onClose} heading='Test Drawer'>
<div>Drawer Content</div>
</ActionDrawer>,
);
expect(screen.getByText('Test Drawer')).toBeInTheDocument();
expect(screen.getByText('Drawer Content')).toBeInTheDocument();
fireEvent.click(screen.getByLabelText('Close'));
expect(onClose).toHaveBeenCalled();
});
it('should show success state', () => {
const successComponent = <div>Success!</div>;
render(
<ActionDrawer
isOpen={true}
onClose={() => {}}
isSuccess={true}
successComponent={successComponent}
>
<div>Normal Content</div>
</ActionDrawer>,
);
expect(screen.getByText('Success!')).toBeInTheDocument();
expect(screen.queryByText('Normal Content')).not.toBeInTheDocument();
});
});
This comprehensive drawer system provides consistent, accessible, and flexible slide-out panel functionality for the WorkPayCore Frontend application.