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.