Tooltip Components
The Tooltip Components provide custom tooltip functionality for the WorkPayCore Frontend application. These components offer customizable tooltip displays with specific positioning and styling options for enhanced user experience.
Overview
This document covers the custom tooltip implementation that provides hover-based information display with branded styling and flexible positioning options.
Components Overview
Core Tooltip Components
- CustomTooltip - Custom tooltip with positioned display and branded styling
CustomTooltip
A custom tooltip component that displays contextual information on hover with flexible positioning and WorkPay-branded styling.
Component Location
import { CustomTooltip } from 'components/Tooltips';
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| label | string | ✓ | - | The text content to display in the tooltip |
| children | ReactNode | ✓ | - | The trigger element that activates the tooltip |
| isImprest | boolean | ✓ | - | Controls tooltip positioning (above/below) |
TypeScript Interface
interface TooltipProps {
label: string;
children: React.ReactNode;
isImprest: boolean;
}
Features
Hover Interaction
- Mouse enter/leave event handling
- Smooth show/hide behavior using Chakra UI's useDisclosure
- Non-blocking interaction with trigger element
Flexible Positioning
- isImprest: false - Tooltip appears above the trigger element
- isImprest: true - Tooltip appears below the trigger element
- Centered horizontal alignment with trigger element
- Automatic arrow pointer positioning
Branded Styling
- WorkPay dark blue background (#003049)
- White text for high contrast
- Rounded corners (4px border radius)
- Consistent sizing (300px min width, 82px min height)
- Arrow pointer with matching background color
Responsive Design
- Relative positioning container
- Absolute positioned tooltip overlay
- Transform-based centering for consistent alignment
- High z-index (1) for proper layering
Usage Examples
Basic Usage Above Element
import { CustomTooltip } from 'components/Tooltips';
function EmployeeCard() {
return (
<CustomTooltip
label='This employee has completed all required training modules and is eligible for promotion.'
isImprest={false}
>
<Badge colorScheme='green'>Certified</Badge>
</CustomTooltip>
);
}
Basic Usage Below Element
import { CustomTooltip } from 'components/Tooltips';
function ImprestField() {
return (
<CustomTooltip
label='Imprest amount is the advance payment provided to employees for official expenses.'
isImprest={true}
>
<Input placeholder='Enter imprest amount' />
</CustomTooltip>
);
}
Button with Tooltip
import { CustomTooltip } from 'components/Tooltips';
function ActionButton() {
return (
<CustomTooltip
label='This action will process the payroll for all active employees. Please ensure all data is correct before proceeding.'
isImprest={false}
>
<Button colorScheme='green' size='lg'>
Process Payroll
</Button>
</CustomTooltip>
);
}
Icon with Explanation
import { CustomTooltip } from 'components/Tooltips';
import { InfoIcon } from '@chakra-ui/icons';
function HelpIcon() {
return (
<CustomTooltip
label='This field requires manager approval for amounts above $1000. Please ensure the expense is properly categorized.'
isImprest={false}
>
<IconButton
aria-label='Help information'
icon={<InfoIcon />}
variant='ghost'
size='sm'
/>
</CustomTooltip>
);
}
Complex Content Tooltip
import { CustomTooltip } from 'components/Tooltips';
function StatusIndicator() {
const statusTooltip = `
Employee Status: Active
Last Login: Today, 9:30 AM
Department: Human Resources
Manager: John Smith
Next Review: March 15, 2024
`;
return (
<CustomTooltip label={statusTooltip} isImprest={false}>
<VStack>
<Avatar name='Jane Doe' size='md' />
<Text fontSize='sm'>Jane Doe</Text>
</VStack>
</CustomTooltip>
);
}
Form Field with Validation Info
import { CustomTooltip } from 'components/Tooltips';
function PasswordField() {
return (
<FormControl>
<FormLabel>Password</FormLabel>
<CustomTooltip
label='Password must be at least 8 characters long, contain uppercase and lowercase letters, numbers, and special characters.'
isImprest={true}
>
<Input type='password' placeholder='Enter secure password' />
</CustomTooltip>
</FormControl>
);
}
Conditional Tooltip Display
import { CustomTooltip } from 'components/Tooltips';
function ConditionalTooltip({ showTooltip, employee }) {
const tooltipContent = `
Employee ID: ${employee.id}
Department: ${employee.department}
Hire Date: ${employee.hireDate}
Status: ${employee.status}
`;
if (showTooltip) {
return (
<CustomTooltip label={tooltipContent} isImprest={false}>
<Text>{employee.name}</Text>
</CustomTooltip>
);
}
return <Text>{employee.name}</Text>;
}
Positioning Behavior
Above Positioning (isImprest: false)
- Tooltip appears above the trigger element
- Arrow points downward to the trigger
- Suitable for elements near the bottom of the viewport
- Better for primary actions and important information
<CustomTooltip label='Important information displayed above' isImprest={false}>
<Button>Trigger Element</Button>
</CustomTooltip>
Below Positioning (isImprest: true)
- Tooltip appears below the trigger element
- Arrow points upward to the trigger
- Suitable for elements near the top of the viewport
- Better for secondary information and form fields
<CustomTooltip label='Additional information displayed below' isImprest={true}>
<Input placeholder='Form field' />
</CustomTooltip>
Styling Specifications
Tooltip Container
- Background: #003049 (WorkPay dark blue)
- Text Color: White
- Padding: 8px horizontal, 4px vertical
- Border Radius: 4px
- Font Size: Small (sm)
- Text Align: Center
- Min Width: 300px
- Min Height: 82px
- Z-Index: 1
Arrow Pointer
- Size: 6px border width
- Color: #003049 (matching background)
- Position: Centered horizontally
- Direction: Changes based on isImprest prop
Positioning
- Container: Relative positioning
- Tooltip: Absolute positioning
- Centering: Transform translateX(-50%)
- Spacing: 120% from bottom (above) or 100% from top (below)
Accessibility Considerations
Screen Reader Support
import { CustomTooltip } from 'components/Tooltips';
function AccessibleTooltip() {
return (
<CustomTooltip
label='This button will save your changes permanently'
isImprest={false}
>
<Button
aria-label='Save changes permanently'
aria-describedby='save-tooltip'
>
Save
</Button>
</CustomTooltip>
);
}
Keyboard Navigation
The component currently supports mouse hover only. For full accessibility, consider these enhancements:
// Enhanced accessibility example
function AccessibleCustomTooltip({ label, children, isImprest }) {
const { isOpen, onOpen, onClose } = useDisclosure();
const handleKeyDown = event => {
if (event.key === 'Escape') {
onClose();
}
};
return (
<Box
position='relative'
display='inline-block'
onMouseEnter={onOpen}
onMouseLeave={onClose}
onFocus={onOpen}
onBlur={onClose}
onKeyDown={handleKeyDown}
tabIndex={0}
>
{children}
{/* Tooltip content */}
</Box>
);
}
Performance Considerations
Optimized Rendering
import { memo } from 'react';
import { CustomTooltip } from 'components/Tooltips';
// Memoized tooltip for performance
const MemoizedTooltip = memo(({ label, children, isImprest }) => (
<CustomTooltip label={label} isImprest={isImprest}>
{children}
</CustomTooltip>
));
function OptimizedComponent() {
return (
<MemoizedTooltip
label="This tooltip won't re-render unnecessarily"
isImprest={false}
>
<Button>Optimized Button</Button>
</MemoizedTooltip>
);
}
Lazy Tooltip Content
import { CustomTooltip } from 'components/Tooltips';
function LazyTooltip({ employee }) {
const getTooltipContent = useCallback(() => {
return `
Employee: ${employee.name}
Department: ${employee.department}
Status: ${employee.status}
`;
}, [employee]);
return (
<CustomTooltip label={getTooltipContent()} isImprest={false}>
<EmployeeCard employee={employee} />
</CustomTooltip>
);
}
Integration Patterns
With Form Components
import { CustomTooltip } from 'components/Tooltips';
function TooltipFormField({ label, tooltip, children, ...props }) {
return (
<FormControl {...props}>
<FormLabel>{label}</FormLabel>
<CustomTooltip label={tooltip} isImprest={true}>
{children}
</CustomTooltip>
</FormControl>
);
}
// Usage
<TooltipFormField
label='Salary Amount'
tooltip='Enter the monthly salary amount in USD. This will be used for payroll calculations.'
>
<Input type='number' placeholder='0.00' />
</TooltipFormField>;
With Table Components
import { CustomTooltip } from 'components/Tooltips';
function TableWithTooltips({ data }) {
return (
<Table>
<Thead>
<Tr>
<Th>
<CustomTooltip
label='Employee identification number assigned by HR'
isImprest={false}
>
Employee ID
</CustomTooltip>
</Th>
<Th>Name</Th>
<Th>
<CustomTooltip
label='Current employment status: Active, Inactive, or Suspended'
isImprest={false}
>
Status
</CustomTooltip>
</Th>
</Tr>
</Thead>
<Tbody>
{data.map(employee => (
<Tr key={employee.id}>
<Td>{employee.id}</Td>
<Td>{employee.name}</Td>
<Td>
<CustomTooltip
label={`Status last updated: ${employee.statusUpdatedAt}`}
isImprest={true}
>
<Badge
colorScheme={employee.status === 'Active' ? 'green' : 'red'}
>
{employee.status}
</Badge>
</CustomTooltip>
</Td>
</Tr>
))}
</Tbody>
</Table>
);
}
Best Practices
Content Guidelines
- Concise Information: Keep tooltip content clear and concise
- Actionable Context: Provide helpful context, not redundant information
- Consistent Tone: Use consistent language and terminology
- Proper Length: Avoid overly long tooltips that are hard to read
Positioning Guidelines
- Viewport Awareness: Use isImprest based on element position
- Content Priority: Position above for critical information
- Form Integration: Position below for form field help text
- Responsive Design: Test tooltips on different screen sizes
Performance Guidelines
- Memo Usage: Memoize tooltips with stable content
- Event Cleanup: Properly handle mouse events
- Conditional Rendering: Only render tooltips when needed
- Content Optimization: Avoid heavy computations in tooltip content
Testing Strategies
import { render, screen, fireEvent } from '@testing-library/react';
import { CustomTooltip } from 'components/Tooltips';
describe('CustomTooltip', () => {
it('shows tooltip on mouse enter', () => {
render(
<CustomTooltip label='Test tooltip' isImprest={false}>
<button>Trigger</button>
</CustomTooltip>,
);
const trigger = screen.getByText('Trigger');
fireEvent.mouseEnter(trigger);
expect(screen.getByText('Test tooltip')).toBeInTheDocument();
});
it('hides tooltip on mouse leave', () => {
render(
<CustomTooltip label='Test tooltip' isImprest={false}>
<button>Trigger</button>
</CustomTooltip>,
);
const trigger = screen.getByText('Trigger');
fireEvent.mouseEnter(trigger);
fireEvent.mouseLeave(trigger);
expect(screen.queryByText('Test tooltip')).not.toBeInTheDocument();
});
it('positions tooltip correctly based on isImprest prop', () => {
const { rerender } = render(
<CustomTooltip label='Test tooltip' isImprest={false}>
<button>Trigger</button>
</CustomTooltip>,
);
fireEvent.mouseEnter(screen.getByText('Trigger'));
let tooltip = screen.getByText('Test tooltip');
expect(tooltip).toHaveStyle('bottom: 120%');
rerender(
<CustomTooltip label='Test tooltip' isImprest={true}>
<button>Trigger</button>
</CustomTooltip>,
);
tooltip = screen.getByText('Test tooltip');
expect(tooltip).toHaveStyle('top: 100%');
});
});
This custom tooltip system provides branded, consistent tooltip functionality with flexible positioning for enhanced user experience in the WorkPayCore Frontend application.