Skip to main content

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​

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​

PropTypeRequiredDefaultDescription
isOpenboolean✓-Drawer open state
onClosefunction✓-Close handler
childrenReactNode--Drawer content
headingReactNode--Drawer heading
widthstring--Custom width
maxHstring-'auto'Maximum height
minHstring-'auto'Minimum height
minBodyHeightstring--Minimum body height
noDividerboolean-falseHide header divider
noHeadingboolean-falseHide heading
noCloseButtonboolean-falseHide close button
isSuccessboolean-falseSuccess state
successComponentReactNode--Success state content
footerReactNode--Footer content
headingStylesobject-{ fontSize: '20px', textTransform: 'capitalize' }Heading styles
drawerBgstring-'white'Drawer background color
...restDrawerProps--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>
);
}
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}
>
&lt;Text&gt;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​

PropTypeRequiredDefaultDescription
isOpenboolean✓-Drawer open state
onClosefunction✓-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
  • 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 (
&lt;Box&gt;
<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 (
&lt;Box&gt;
{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 (
&lt;Box&gt;
<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​

PropTypeRequiredDefaultDescription
isOpenboolean✓-Drawer open state
onClosefunction✓-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 (
&lt;Box&gt;
<HStack justify='space-between'>
&lt;Heading&gt;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 (
&lt;Box&gt;
<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'>
&lt;Heading&gt;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​

PropTypeRequiredDefaultDescription
drawerDisclosureobject✓-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 (
&lt;Box&gt;
<Button onClick={drawerDisclosure.onOpen}>Get Help</Button>
<GetHelpDrawerComponent drawerDisclosure={drawerDisclosure} />
</Box>
);
}
import { GetHelpDrawerComponent } from 'components/Navbar/GetHelp/drawer';

function NavbarWithHelp() {
const helpDrawerDisclosure = useDisclosure();

return (
&lt;Box&gt;
<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​

  1. ActionDrawer: Use for forms, detailed content, and actions
  2. SideBarDrawer: Use for mobile navigation only
  3. Filter: Use specifically for filtering functionality
  4. GetHelpDrawer: Use for help and support content

Performance​

  1. Lazy Loading: Load drawer content only when needed
  2. Memory Management: Clean up drawer state on close
  3. Event Handlers: Properly handle drawer events
  4. Form State: Manage form state appropriately

User Experience​

  1. Smooth Transitions: Ensure smooth open/close animations
  2. Focus Management: Handle focus properly on open/close
  3. Escape Key: Allow escape key to close drawer
  4. Overlay Click: Configure overlay click behavior

Accessibility​

  1. ARIA Labels: Provide proper ARIA labels
  2. Keyboard Navigation: Ensure keyboard accessibility
  3. Screen Readers: Support screen reader navigation
  4. 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.