Skip to main content

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

PropTypeRequiredDefaultDescription
labelstring-The text content to display in the tooltip
childrenReactNode-The trigger element that activates the tooltip
isImprestboolean-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}>
&lt;VStack&gt;
<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 (
&lt;FormControl&gt;
&lt;FormLabel&gt;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}>
&lt;Text&gt;{employee.name}</Text>
</CustomTooltip>
);
}

return &lt;Text&gt;{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}>
&lt;Button&gt;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}
>
&lt;Button&gt;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}>
&lt;FormLabel&gt;{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 (
&lt;Table&gt;
&lt;Thead&gt;
&lt;Tr&gt;
&lt;Th&gt;
<CustomTooltip
label='Employee identification number assigned by HR'
isImprest={false}
>
Employee ID
</CustomTooltip>
</Th>
&lt;Th&gt;Name</Th>
&lt;Th&gt;
<CustomTooltip
label='Current employment status: Active, Inactive, or Suspended'
isImprest={false}
>
Status
</CustomTooltip>
</Th>
</Tr>
</Thead>
&lt;Tbody&gt;
{data.map(employee => (
<Tr key={employee.id}>
&lt;Td&gt;{employee.id}</Td>
&lt;Td&gt;{employee.name}</Td>
&lt;Td&gt;
<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

  1. Concise Information: Keep tooltip content clear and concise
  2. Actionable Context: Provide helpful context, not redundant information
  3. Consistent Tone: Use consistent language and terminology
  4. Proper Length: Avoid overly long tooltips that are hard to read

Positioning Guidelines

  1. Viewport Awareness: Use isImprest based on element position
  2. Content Priority: Position above for critical information
  3. Form Integration: Position below for form field help text
  4. Responsive Design: Test tooltips on different screen sizes

Performance Guidelines

  1. Memo Usage: Memoize tooltips with stable content
  2. Event Cleanup: Properly handle mouse events
  3. Conditional Rendering: Only render tooltips when needed
  4. 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.