Skip to main content

Tab Components

The Tab Components provide comprehensive tabbed navigation functionality for the WorkPayCore Frontend application. These components handle multi-view interfaces, page navigation, and content organization with consistent styling and behavior patterns.

Overview

This document covers tab-related components that enable organized content presentation, navigation between different views, and complex page layouts with tabbed interfaces.

Components Overview

Core Tab Components

  • TabsWrapper - Basic flexible tab component with routing support
  • AdminTabsWrapper - Admin interface tabs with progress indicators
  • WPTabbedPage - Complete page template with integrated tabs
  • WPTabPage - Advanced tab page wrapper with complex layouts
  • NavTabs - Routed tabs with React Router integration
  • RoutedTabs - Router-aware tab container

Specialized Tab Patterns

  • Manual Tab Control - Manual tab switching patterns
  • Nested Tab Layouts - Complex nested tab structures
  • Tab with Filters - Tabs integrated with filtering systems

TabsWrapper

Basic flexible tab component supporting horizontal/vertical orientation with custom styling and routing.

Component Location

import TabsWrapper from 'components/Tabs/TabsWrapper';

Props

PropTypeRequiredDefaultDescription
tabDataarray-Tab configuration array
orientationstring-'horizontal'Tab orientation
variantstring--Custom styling variant
defaultIndexnumber-0Default active tab
indexnumber--Controlled active tab
onTabChangefunction--Tab change handler
widthstring-'max-content'Tab width

TypeScript Interface

interface TabData {
id: string | number;
title: string;
link?: string;
children?: ReactNode;
}

interface TabsWrapperProps {
tabData: TabData[];
orientation?: 'horizontal' | 'vertical';
variant?: 'custom' | string;
defaultIndex?: number;
index?: number;
onTabChange?: (index: number) => void;
width?: string;
}

Usage Examples

Basic Horizontal Tabs

import TabsWrapper from 'components/Tabs/TabsWrapper';

function BasicTabs() {
const tabData = [
{
id: 1,
title: 'Overview',
link: '/dashboard/overview',
},
{
id: 2,
title: 'Reports',
link: '/dashboard/reports',
},
{
id: 3,
title: 'Settings',
link: '/dashboard/settings',
},
];

return (
<TabsWrapper
tabData={tabData}
orientation='horizontal'
variant='custom'
onTabChange={index => console.log('Tab changed:', index)}
/>
);
}

Vertical Navigation Tabs

function VerticalTabs() {
const tabData = [
{
id: 1,
title: 'Employee Management',
link: '/hr/employees',
},
{
id: 2,
title: 'Leave Management',
link: '/hr/leaves',
},
{
id: 3,
title: 'Payroll',
link: '/hr/payroll',
},
];

return <TabsWrapper tabData={tabData} orientation='vertical' width='200px' />;
}

Controlled Tabs with State

function ControlledTabs() {
const [activeTab, setActiveTab] = useState(0);

const tabData = [
{ id: 1, title: 'Active', link: '/employees?status=active' },
{ id: 2, title: 'Inactive', link: '/employees?status=inactive' },
{ id: 3, title: 'Pending', link: '/employees?status=pending' },
];

return (
<TabsWrapper
tabData={tabData}
index={activeTab}
onTabChange={setActiveTab}
variant='custom'
/>
);
}

AdminTabsWrapper

Specialized tab component for admin interfaces with progress indicators and step navigation.

Component Location

import AdminTabsWrapper from 'components/Tabs/AdminTabsWrapper';

Props

PropTypeRequiredDefaultDescription
tabDataarray-Tab configuration array
orientationstring--Tab orientation
variantstring--Styling variant
tabIsDisabledboolean-falseDisable tab interaction
iconIsDisabledboolean-falseDisable progress icons

Features

  • Progress Indicators: Visual step progress with numbered icons
  • Completion Status: Shows completed vs. pending steps
  • Onboarding Integration: Works with employee onboarding workflows
  • URL State Management: Manages tab state via URL parameters

Usage Examples

Onboarding Workflow Tabs

import AdminTabsWrapper from 'components/Tabs/AdminTabsWrapper';

function OnboardingTabs() {
const onboardingSteps = [
{
id: 1,
title: 'Personal Information',
link: '/onboarding/personal',
completed: true,
},
{
id: 2,
title: 'Employment Details',
link: '/onboarding/employment',
completed: true,
},
{
id: 3,
title: 'Documents Upload',
link: '/onboarding/documents',
completed: false,
},
{
id: 4,
title: 'Review & Submit',
link: '/onboarding/review',
completed: false,
},
];

return (
<AdminTabsWrapper
tabData={onboardingSteps}
orientation='vertical'
variant='custom'
/>
);
}

Multi-Step Process with Disabled States

function MultiStepProcess() {
const [currentStep, setCurrentStep] = useState(0);
const [completedSteps, setCompletedSteps] = useState([]);

const processSteps = [
{ id: 1, title: 'Step 1: Setup', link: '/process/setup' },
{ id: 2, title: 'Step 2: Configuration', link: '/process/config' },
{ id: 3, title: 'Step 3: Verification', link: '/process/verify' },
{ id: 4, title: 'Step 4: Completion', link: '/process/complete' },
];

return (
<AdminTabsWrapper
tabData={processSteps}
orientation='vertical'
tabIsDisabled={false}
iconIsDisabled={false}
/>
);
}

WPTabbedPage

Complete page template component with integrated tabs, filters, and table functionality.

Component Location

import { WPTabbedPage } from 'components/WPagesTemplates/WPTabbedPage';

Sub-components

  • WPTabbedPage.BreadCrumb - Navigation breadcrumb
  • WPTabbedPage.Title - Page title with tooltip support
  • WPTabbedPage.Body - Main tab container
  • WPTabbedPage.HeaderSection - Header layout section
  • WPTabbedPage.TabsNFilters - Tabs with integrated filters
  • WPTabbedPage.Table - Table component with tab integration

Props for Main Component

PropTypeRequiredDefaultDescription
pageTitlestring--Main page title
noFooterboolean-falseHide page footer
overflowstring--Overflow behavior
childrenReactNode-Page content

Title Component Props

PropTypeRequiredDefaultDescription
titlestring-Title text
descriptionstring--Tooltip description
hasToolTipboolean-falseShow tooltip
fontSizenumber-26Title font size
colorstring-'charcoal'Title color

TabsNFilters Component Props

PropTypeRequiredDefaultDescription
tabDataarray-Tab configuration
childrenReactNode--Filter components
childrenWidthstring-'full'Filter section width

Usage Examples

Complete Page with Tabs and Filters

import { WPTabbedPage } from 'components/WPagesTemplates/WPTabbedPage';
import { Tab, TabList, TabPanels, TabPanel } from '@chakra-ui/react';

function EmployeePage() {
const tabData = [
{ title: 'Active Employees', id: 'active' },
{ title: 'Inactive Employees', id: 'inactive' },
{ title: 'Pending Approval', id: 'pending' },
];

return (
<WPTabbedPage pageTitle='Employee Management'>
<WPTabbedPage.BreadCrumb>
<HStack as={Link} to='/dashboard'>
<ChevronLeftOutline />
&lt;Text&gt;Back to Dashboard</Text>
</HStack>
</WPTabbedPage.BreadCrumb>

<WPTabbedPage.Title
title='Employee Management'
description='Manage all employee records, statuses, and related information'
hasToolTip
/>

<WPTabbedPage.Body defaultIndex={0}>
<WPTabbedPage.HeaderSection>
<WPTabbedPage.TabsNFilters tabData={tabData}>
<EmployeeFilters />
</WPTabbedPage.TabsNFilters>
</WPTabbedPage.HeaderSection>

&lt;TabPanels&gt;
&lt;TabPanel&gt;
<ActiveEmployeesTable />
</TabPanel>
&lt;TabPanel&gt;
<InactiveEmployeesTable />
</TabPanel>
&lt;TabPanel&gt;
<PendingEmployeesTable />
</TabPanel>
</TabPanels>
</WPTabbedPage.Body>
</WPTabbedPage>
);
}

Page with Integrated Table

function TransactionsPage() {
const tabData = [
{
id: 'pending',
title: 'Pending',
tableData: pendingTransactions,
children: <PendingTransactionsView />,
},
{
id: 'completed',
title: 'Completed',
tableData: completedTransactions,
children: <CompletedTransactionsView />,
},
];

return (
<WPTabbedPage pageTitle='Transactions'>
<WPTabbedPage.Title title='Payment Transactions' />

<WPTabbedPage.Body>
<WPTabbedPage.Table
tabData={tabData}
customColumn={transactionColumns}
isLoading={isLoading}
paginatedData={paginationData}
/>
</WPTabbedPage.Body>
</WPTabbedPage>
);
}

Page with Right Section Actions

function DashboardPage() {
return (
<WPTabbedPage pageTitle='Analytics Dashboard'>
<WPTabbedPage.Title title='Analytics & Reports' />

<WPTabbedPage.Body>
<WPTabbedPage.HeaderSection>
<WPTabbedPage.TabsNFilters tabData={tabData}>
<DateFilter />
<StatusFilter />
</WPTabbedPage.TabsNFilters>

<WPTabbedPage.RightSection>
<StatisticsCard />
<ExportButton />
</WPTabbedPage.RightSection>
</WPTabbedPage.HeaderSection>

&lt;TabPanels&gt;
{tabData.map((tab, index) => (
<TabPanel key={index}>{tab.children}</TabPanel>
))}
</TabPanels>
</WPTabbedPage.Body>
</WPTabbedPage>
);
}

WPTabPage

Advanced tab page wrapper with complex layout support and header sections.

Component Location

import { WPTabPage } from 'components/Tabs/AdvancedTabsWrapper';

Sub-components

  • WPTabPage.BreadCrumb - Breadcrumb navigation
  • WPTabPage.Title - Page title section
  • WPTabPage.Body - Main tab container
  • WPTabPage.HeaderSection - Header layout
  • WPTabPage.TabsNFilters - Tabs with filters
  • WPTabPage.Filters - Filter section wrapper
  • WPTabPage.RightSection - Right-aligned content
  • WPTabPage.Content - Tab panels content

Usage Examples

Advanced Layout with Multiple Sections

import { WPTabPage } from 'components/Tabs/AdvancedTabsWrapper';

function AdvancedDashboard() {
const tabData = [
{ title: 'Overview', children: <OverviewPanel /> },
{ title: 'Analytics', children: <AnalyticsPanel /> },
{ title: 'Reports', children: <ReportsPanel /> },
];

return (
&lt;WPTabPage&gt;
<WPTabPage.BreadCrumb>
<BreadcrumbNavigation />
</WPTabPage.BreadCrumb>

<WPTabPage.Title title='Advanced Dashboard' />

<WPTabPage.Body>
<WPTabPage.HeaderSection>
<WPTabPage.TabsNFilters tabData={tabData}>
<WPTabPage.Filters>
<DateRangeFilter />
<DepartmentFilter />
</WPTabPage.Filters>
</WPTabPage.TabsNFilters>

<WPTabPage.RightSection>
<QuickStats />
<ActionButtons />
</WPTabPage.RightSection>
</WPTabPage.HeaderSection>

<WPTabPage.Content tabData={tabData} />
</WPTabPage.Body>
</WPTabPage>
);
}

Router-integrated tab component that works with React Router for navigation.

Component Location

import { NavTab } from 'components/RoutedTabs/NavTabs';

Props

PropTypeRequiredDefaultDescription
tostring-Navigation path
exactboolean-falseExact path matching
strictboolean-falseStrict path matching
activeClassNamestring--CSS class for active tab
disabledboolean-falseDisable tab interaction
allowClickOnActiveboolean-falseAllow clicking active tab

Usage Examples

Router-Based Navigation

import { NavTab } from 'components/RoutedTabs/NavTabs';

function NavigationTabs() {
return (
<div className='tab-container'>
<NavTab to='/employees' exact activeClassName='active-tab'>
Employees
</NavTab>

<NavTab to='/departments' activeClassName='active-tab'>
Departments
</NavTab>

<NavTab to='/payroll' activeClassName='active-tab'>
Payroll
</NavTab>
</div>
);
}

Nested Route Tabs

function NestedRouteTabs() {
return (
<RoutedTabs startPathWith='/hr'>
<NavTab to='/employees' exact>
Employee Management
</NavTab>
<NavTab to='/leaves'>Leave Management</NavTab>
<NavTab to='/attendance'>Attendance</NavTab>
</RoutedTabs>
);
}

Tab Patterns

Manual Tab Control Pattern

import { Tabs, TabList, Tab, TabPanels, TabPanel } from '@chakra-ui/react';
import { NavLink, useLocation } from 'react-router-dom';

function ManualTabControl() {
const location = useLocation();
const [tab, setTab] = useState('overview');

const tabs = [
{ tabName: 'overview', tab: 'Overview', path: '/dashboard?tab=overview' },
{ tabName: 'reports', tab: 'Reports', path: '/dashboard?tab=reports' },
{ tabName: 'settings', tab: 'Settings', path: '/dashboard?tab=settings' },
];

useEffect(() => {
const urlParams = new URLSearchParams(location.search);
const currentTab = urlParams.get('tab') || 'overview';
setTab(currentTab);
}, [location.search]);

return (
<Tabs
isLazy
width='full'
isManual
index={tabs.findIndex(({ tabName }) => tabName === tab)}
>
<TabList borderBottom='1px solid' borderColor='hue-navy.50'>
{tabs.map((tab, idx) => (
<Tab
key={idx}
textStyle='body-regular'
color='slategrey'
cursor='pointer'
as={NavLink}
to={tab.path}
>
{tab.tab}
</Tab>
))}
</TabList>

&lt;TabPanels&gt;
{tabs.map((tab, idx) => (
<TabPanel p={0} key={idx}>
{tab.tabPanel}
</TabPanel>
))}
</TabPanels>
</Tabs>
);
}

Tab with Filters Integration Pattern

function TabsWithFilters() {
const [selectedFilters, setSelectedFilters] = useState({});
const [activeTab, setActiveTab] = useState(0);

const tabData = [
{ title: 'All Items', filter: {} },
{ title: 'Active', filter: { status: 'active' } },
{ title: 'Pending', filter: { status: 'pending' } },
];

const handleTabChange = index => {
setActiveTab(index);
setSelectedFilters(tabData[index].filter);
};

return (
<WPTabbedPage pageTitle='Items Management'>
<WPTabbedPage.Body defaultIndex={0} onTabChange={handleTabChange}>
<WPTabbedPage.HeaderSection>
<WPTabbedPage.TabsNFilters tabData={tabData}>
<FilterComponent
filters={selectedFilters}
onFilterChange={setSelectedFilters}
/>
</WPTabbedPage.TabsNFilters>
</WPTabbedPage.HeaderSection>

&lt;TabPanels&gt;
{tabData.map((tab, index) => (
<TabPanel key={index}>
<DataTable filters={{ ...selectedFilters, ...tab.filter }} />
</TabPanel>
))}
</TabPanels>
</WPTabbedPage.Body>
</WPTabbedPage>
);
}

Nested Tab Layout Pattern

function NestedTabLayout() {
return (
<WPTabbedPage pageTitle='Complex Dashboard'>
<WPTabbedPage.Body>
&lt;TabList&gt;
&lt;Tab&gt;Analytics</Tab>
&lt;Tab&gt;Reports</Tab>
&lt;Tab&gt;Settings</Tab>
</TabList>

&lt;TabPanels&gt;
&lt;TabPanel&gt;
{/* Nested tabs within Analytics */}
<Tabs variant='enclosed'>
&lt;TabList&gt;
&lt;Tab&gt;Revenue</Tab>
&lt;Tab&gt;Users</Tab>
&lt;Tab&gt;Performance</Tab>
</TabList>
&lt;TabPanels&gt;
&lt;TabPanel&gt;
<RevenueAnalytics />
</TabPanel>
&lt;TabPanel&gt;
<UserAnalytics />
</TabPanel>
&lt;TabPanel&gt;
<PerformanceAnalytics />
</TabPanel>
</TabPanels>
</Tabs>
</TabPanel>

&lt;TabPanel&gt;
<ReportsSection />
</TabPanel>

&lt;TabPanel&gt;
<SettingsSection />
</TabPanel>
</TabPanels>
</WPTabbedPage.Body>
</WPTabbedPage>
);
}

Dynamic Tab Generation Pattern

function DynamicTabs() {
const [tabsConfig, setTabsConfig] = useState([]);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
fetchTabConfiguration().then(config => {
setTabsConfig(config);
setIsLoading(false);
});
}, []);

if (isLoading) {
return <SkeletonLoader />;
}

return (
<TabsWrapper
tabData={tabsConfig.map(config => ({
id: config.id,
title: config.label,
link: config.path,
disabled: !config.enabled,
}))}
orientation='horizontal'
variant='custom'
/>
);
}

Best Practices

Tab Design

  1. Clear Navigation

    • Use descriptive tab labels
    • Maintain consistent tab ordering
    • Provide visual feedback for active states
  2. Content Organization

    • Group related content logically
    • Keep tab content focused and concise
    • Use progressive disclosure for complex data
  3. Responsive Behavior

    • Handle mobile tab layouts appropriately
    • Use scrollable tabs for many items
    • Consider accordion alternatives on small screens

State Management

  1. URL Synchronization

    • Keep tab state in URL parameters
    • Enable deep linking to specific tabs
    • Handle browser back/forward navigation
  2. Data Loading

    • Implement lazy loading for tab content
    • Cache data between tab switches
    • Show loading states appropriately
  3. Filter Integration

    • Reset filters when changing tabs
    • Maintain filter state within tabs
    • Provide clear filter indicators

Performance Optimization

  1. Lazy Loading

    <Tabs isLazy>
    &lt;TabPanels&gt;
    &lt;TabPanel&gt;
    <Suspense fallback={<Loading />}>
    <HeavyComponent />
    </Suspense>
    </TabPanel>
    </TabPanels>
    </Tabs>
  2. Memoization

    const MemoizedTabContent = memo(({ data }) => {
    return <ExpensiveComponent data={data} />;
    });
  3. Virtual Scrolling

    // For tabs with large datasets
    &lt;TabPanel&gt;
    <VirtualizedTable data={largeDataset} />
    </TabPanel>

Testing

Unit Tests

import { render, screen, fireEvent } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';
import TabsWrapper from 'components/Tabs/TabsWrapper';

describe('TabsWrapper', () => {
const mockTabData = [
{ id: 1, title: 'Tab 1', link: '/tab1' },
{ id: 2, title: 'Tab 2', link: '/tab2' },
];

it('renders all tabs', () => {
render(
&lt;BrowserRouter&gt;
<TabsWrapper tabData={mockTabData} />
</BrowserRouter>,
);

expect(screen.getByText('Tab 1')).toBeInTheDocument();
expect(screen.getByText('Tab 2')).toBeInTheDocument();
});

it('calls onTabChange when tab is clicked', async () => {
const mockOnTabChange = jest.fn();
const { user } = render(
&lt;BrowserRouter&gt;
<TabsWrapper tabData={mockTabData} onTabChange={mockOnTabChange} />
</BrowserRouter>,
);

await user.click(screen.getByText('Tab 2'));
expect(mockOnTabChange).toHaveBeenCalledWith(1);
});

it('applies custom variant styling', () => {
render(
&lt;BrowserRouter&gt;
<TabsWrapper tabData={mockTabData} variant='custom' />
</BrowserRouter>,
);

const tabList = screen.getByRole('tablist');
expect(tabList).toHaveStyle({ backgroundColor: 'white' });
});
});

Integration Tests

describe('WPTabbedPage Integration', () => {
it('integrates tabs with filters correctly', async () => {
const { user } = render(<EmployeeManagementPage />);

// Switch to inactive tab
await user.click(screen.getByText('Inactive Employees'));

// Apply filter
await user.click(screen.getByText('Department'));
await user.click(screen.getByText('Engineering'));

// Verify API call with correct parameters
expect(mockApiCall).toHaveBeenCalledWith({
status: 'inactive',
department: 'engineering',
});
});

it('maintains filter state across tab switches', async () => {
const { user } = render(<EmployeeManagementPage />);

// Apply filter on first tab
await user.click(screen.getByText('Department'));
await user.click(screen.getByText('Engineering'));

// Switch to second tab
await user.click(screen.getByText('Inactive Employees'));

// Switch back to first tab
await user.click(screen.getByText('Active Employees'));

// Verify filter is still applied
expect(screen.getByText('Engineering')).toBeInTheDocument();
});
});

Migration Guide

From Legacy Tab Components

  1. Update Import Paths

    // Old
    import TabComponent from 'components/OldTabs';

    // New
    import TabsWrapper from 'components/Tabs/TabsWrapper';
  2. Update Tab Data Structure

    // Old
    const tabs = ['Tab 1', 'Tab 2', 'Tab 3'];

    // New
    const tabData = [
    { id: 1, title: 'Tab 1', link: '/tab1' },
    { id: 2, title: 'Tab 2', link: '/tab2' },
    { id: 3, title: 'Tab 3', link: '/tab3' },
    ];
  3. Update Event Handlers

    // Old
    <TabComponent onChange={handleTabChange} />

    // New
    <TabsWrapper
    tabData={tabData}
    onTabChange={handleTabChange}
    />

CSS to Chakra UI Migration

// Old CSS-based tabs
.tab-container {
display: flex;
border-bottom: 1px solid #ccc;
}

.tab {
padding: 12px 16px;
cursor: pointer;
}

.tab.active {
border-bottom: 2px solid #green;
background: #f0f0f0;
}

// New Chakra UI tabs
<Tabs variant="enclosed">
&lt;TabList&gt;
<Tab
_selected={{
borderBottom: '2px solid',
borderColor: 'green',
bg: 'gray.50'
}}
>
Tab Content
</Tab>
</TabList>
</Tabs>

Advanced Usage

Custom Tab Animations

import { motion, AnimatePresence } from 'framer-motion';

function AnimatedTabs() {
const [activeTab, setActiveTab] = useState(0);

return (
<Tabs index={activeTab} onChange={setActiveTab}>
&lt;TabList&gt;
{tabData.map((tab, index) => (
<Tab key={index}>{tab.title}</Tab>
))}
</TabList>

&lt;TabPanels&gt;
<AnimatePresence mode='wait'>
{tabData.map(
(tab, index) =>
activeTab === index && (
<motion.div
key={index}
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.2 }}
>
&lt;TabPanel&gt;{tab.content}</TabPanel>
</motion.div>
),
)}
</AnimatePresence>
</TabPanels>
</Tabs>
);
}

Tab State Persistence

function PersistentTabs() {
const [activeTab, setActiveTab] = useLocalStorage('activeTab', 0);

const handleTabChange = index => {
setActiveTab(index);
// Also update URL if needed
history.pushState({}, '', `?tab=${index}`);
};

return (
<TabsWrapper
tabData={tabData}
index={activeTab}
onTabChange={handleTabChange}
/>
);
}

Conditional Tab Rendering

function ConditionalTabs({ userPermissions }) {
const allTabs = [
{ id: 'overview', title: 'Overview', permission: 'view_overview' },
{ id: 'admin', title: 'Admin', permission: 'admin_access' },
{ id: 'reports', title: 'Reports', permission: 'view_reports' },
];

const visibleTabs = allTabs.filter(tab =>
userPermissions.includes(tab.permission),
);

return <TabsWrapper tabData={visibleTabs} orientation='horizontal' />;
}