Skip to main content

Tables

Overview

The Tables library provides a comprehensive set of data table components built on top of React Table and Chakra UI. These components offer different table variants for various use cases, from basic data display to complex interactive tables with selection, expansion, and pagination.

Table Components

Basic Tables

  • WPBasicDataTable - Simple data table with pagination
  • WPGlobalBasicDataTable - Basic table with global search
  • RemoteWPBasicDataTable - Basic table for remote data

Interactive Tables

  • WPSelectDataTable - Table with row selection capabilities
  • WPExpandableDataTable - Table with expandable rows
  • WPSubComponentTable - Table with sub-component rendering
  • WPActionableTable - Table with inline editing capabilities

Editable Tables

  • WPBasicEditableTable - Basic table with inline editing
  • ActionableTable - Advanced editable table with actions

Modern Tables (V2)

  • V2WPBaseTable - Modern table using TanStack Table
  • V2WPSelectDataTable - Modern selectable table

Utility Components

  • TableError - Error state component
  • TablePagination - Pagination component
  • TableSkeletons - Loading state components
  • Checkbox - Table checkbox component
  • GlobalFilter - Global search component

When to Use

WPBasicDataTable

  • Use for simple data display without interactions
  • When you need basic pagination
  • For read-only data tables

WPSelectDataTable

  • Use when you need row selection functionality
  • For bulk operations on table data
  • When implementing data management interfaces

WPExpandableDataTable

  • Use when rows contain additional detailed information
  • For hierarchical or nested data display
  • When you need to show/hide additional content per row

WPSubComponentTable

  • Use when you need to render custom components within rows
  • For complex data visualization within table cells
  • When standard table cells aren't sufficient

WPActionableTable

  • Use for inline editing capabilities
  • When you need to modify data directly in the table
  • For data entry forms within tables

V2WPBaseTable

  • Use for new implementations with modern React Table
  • When you need advanced table features
  • For better performance with large datasets

Common Props

Standard Table Props

All table components share these common props:

PropTypeRequiredDefaultDescription
customColumnArray-Table column configuration
tableDataArray-Data to display in table
isLoadingboolean-falseLoading state
errorObject--Error object
noDataMsgstring-'No data available'Message when no data
paginatedDataObject--Pagination metadata
initialStateObject--Initial table state
hiddenColumnsArray--Columns to hide

Selection Props

For selectable tables:

PropTypeRequiredDefaultDescription
isSelectableboolean-falseEnable row selection
onRowClickFunction--Row click handler
selectedRowsArray--Currently selected rows

Pagination Props

PropTypeRequiredDefaultDescription
noFooterboolean-falseHide pagination footer
defaultFooterboolean-falseUse default footer
recordsPerPagenumber-10Records per page

API Documentation

WPBasicDataTable

Props

PropTypeRequiredDefaultDescription
customColumnArray-Column definitions
tableDataArray-Table data
isLoadingboolean-falseLoading state
errorObject--Error object
noDataMsgstring-'No data available'No data message
paginatedDataObject--Pagination data
noFooterboolean-falseHide footer
hiddenColumnsArray--Hidden columns
globalSearchValuestring--Global search value
initialStateObject--Initial table state

Usage Example

import WPBasicDataTable from 'components/Tables/WPBasicDataTable';

const columns = [
{
Header: 'Name',
accessor: 'name',
},
{
Header: 'Email',
accessor: 'email',
},
{
Header: 'Role',
accessor: 'role',
},
];

const data = [
{ name: 'John Doe', email: 'john@example.com', role: 'Admin' },
{ name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
];

<WPBasicDataTable
customColumn={columns}
tableData={data}
isLoading={false}
noDataMsg='No users found'
/>;

WPSelectDataTable

Props

PropTypeRequiredDefaultDescription
customColumnArray-Column definitions
tableDataArray-Table data
isLoadingboolean-falseLoading state
errorObject--Error object
noDataMsgstring-'No data available'No data message
paginatedDataObject--Pagination data
onRowClickFunction--Row click handler
initialStateObject--Initial table state

Usage Example

import WPSelectDataTable from 'components/Tables/WPSelectDataTable';

const handleRowSelection = selectedRows => {
console.log('Selected rows:', selectedRows);
};

<WPSelectDataTable
customColumn={columns}
tableData={data}
isLoading={false}
onRowClick={handleRowSelection}
noDataMsg='No data to select'
/>;

WPExpandableDataTable

Props

PropTypeRequiredDefaultDescription
customColumnArray-Column definitions
tableDataArray-Table data
isLoadingboolean-falseLoading state
errorObject--Error object
noDataMsgstring-'No data available'No data message
paginatedDataObject--Pagination data
renderRowSubComponentFunction--Sub-component renderer

Usage Example

import WPExpandableDataTable from 'components/Tables/WPExpandableDataTable';

const renderSubComponent = ({ row }) => (
<div>
<h4>Details for {row.original.name}</h4>
<p>Additional information...</p>
</div>
);

<WPExpandableDataTable
customColumn={columns}
tableData={data}
isLoading={false}
renderRowSubComponent={renderSubComponent}
/>;

WPSubComponentTable

Props

PropTypeRequiredDefaultDescription
customColumnArray-Column definitions
tableDataArray-Table data
isLoadingboolean-falseLoading state
errorObject--Error object
noDataMsgstring-'No data available'No data message
paginatedDataObject--Pagination data
renderRowSubComponentFunction-Sub-component renderer
isBasicboolean-falseBasic mode without selection
noFooterboolean-falseHide footer

Usage Example

import WPSubComponentTable from 'components/Tables/WPSubComponentTable';

const renderRowSubComponent = ({ row }) => (
<Box p={4} bg='gray.50'>
<VStack align='start'>
&lt;Text&gt;
<strong>ID:</strong> {row.original.id}
</Text>
&lt;Text&gt;
<strong>Created:</strong> {row.original.created_at}
</Text>
&lt;Text&gt;
<strong>Updated:</strong> {row.original.updated_at}
</Text>
</VStack>
</Box>
);

<WPSubComponentTable
customColumn={columns}
tableData={data}
isLoading={false}
renderRowSubComponent={renderRowSubComponent}
isBasic={false}
/>;

V2WPBaseTable

Props

PropTypeRequiredDefaultDescription
columnsArray-TanStack Table columns
tableDataArray-Table data
isLoadingboolean-falseLoading state
errorObject--Error object
noDataMsgstring-'No data available'No data message
isSelectableboolean-falseEnable selection
pageMetaObject--Pagination metadata
enableColumnPinningboolean-falseEnable column pinning
enableColumnResizingboolean-falseEnable column resizing
variantstring-'base'Table variant

Usage Example

import { V2WPBaseTable } from 'components/Tables/V2WPTables';

const columns = [
{
accessorKey: 'name',
header: 'Name',
cell: info => info.getValue(),
},
{
accessorKey: 'email',
header: 'Email',
cell: info => info.getValue(),
},
];

<V2WPBaseTable
columns={columns}
tableData={data}
isLoading={false}
isSelectable={true}
enableColumnPinning={true}
variant='striped'
/>;

Column Configuration

Basic Column Structure

const columns = [
{
Header: 'Column Title',
accessor: 'dataKey', // key in data object
Cell: ({ value, row }) => <span>{value}</span>, // custom cell renderer
width: 200, // column width
minWidth: 100, // minimum width
maxWidth: 300, // maximum width
canSort: true, // enable sorting
canFilter: true, // enable filtering
canGroupBy: true, // enable grouping
canHide: true, // can be hidden
sticky: 'left', // sticky positioning
},
];

Custom Cell Renderers

const columns = [
{
Header: 'Status',
accessor: 'status',
Cell: ({ value }) => (
<Badge colorScheme={value === 'active' ? 'green' : 'red'}>{value}</Badge>
),
},
{
Header: 'Actions',
accessor: 'id',
Cell: ({ value, row }) => (
&lt;HStack&gt;
<IconButton
icon={<EditIcon />}
onClick={() => handleEdit(row.original)}
/>
<IconButton icon={<DeleteIcon />} onClick={() => handleDelete(value)} />
</HStack>
),
},
];

Grouped Columns

const columns = [
{
Header: 'Personal Info',
columns: [
{
Header: 'First Name',
accessor: 'firstName',
},
{
Header: 'Last Name',
accessor: 'lastName',
},
],
},
{
Header: 'Contact Info',
columns: [
{
Header: 'Email',
accessor: 'email',
},
{
Header: 'Phone',
accessor: 'phone',
},
],
},
];

Table Patterns

Basic Data Table

function UserTable() {
const { data, isLoading, error } = useQuery('users', fetchUsers);

const columns = useMemo(
() => [
{ Header: 'Name', accessor: 'name' },
{ Header: 'Email', accessor: 'email' },
{ Header: 'Role', accessor: 'role' },
],
[],
);

return (
<WPBasicDataTable
customColumn={columns}
tableData={data || []}
isLoading={isLoading}
error={error}
noDataMsg='No users found'
/>
);
}

Selectable Table with Actions

function SelectableUserTable() {
const [selectedRows, setSelectedRows] = useState([]);
const { data, isLoading, error } = useQuery('users', fetchUsers);

const handleBulkDelete = () => {
// Handle bulk delete of selected rows
selectedRows.forEach(row => deleteUser(row.id));
};

const columns = useMemo(
() => [
{ Header: 'Name', accessor: 'name' },
{ Header: 'Email', accessor: 'email' },
{ Header: 'Role', accessor: 'role' },
{
Header: 'Actions',
accessor: 'id',
Cell: ({ value, row }) => (
&lt;Menu&gt;
<MenuButton as={IconButton} icon={<MoreVertical />} />
&lt;MenuList&gt;
<MenuItem onClick={() => editUser(row.original)}>Edit</MenuItem>
<MenuItem onClick={() => deleteUser(value)}>Delete</MenuItem>
</MenuList>
</Menu>
),
},
],
[],
);

return (
&lt;Box&gt;
<HStack mb={4}>
<Button
onClick={handleBulkDelete}
isDisabled={selectedRows.length === 0}
>
Delete Selected ({selectedRows.length})
</Button>
</HStack>
<WPSelectDataTable
customColumn={columns}
tableData={data || []}
isLoading={isLoading}
error={error}
onRowClick={setSelectedRows}
/>
</Box>
);
}

Expandable Table with Details

function ExpandableOrderTable() {
const { data, isLoading, error } = useQuery('orders', fetchOrders);

const renderRowSubComponent = ({ row }) => (
<Box p={4} bg='gray.50'>
<VStack align='start' spacing={2}>
&lt;Text&gt;
<strong>Order ID:</strong> {row.original.id}
</Text>
&lt;Text&gt;
<strong>Customer:</strong> {row.original.customer}
</Text>
&lt;Text&gt;
<strong>Items:</strong>
</Text>
&lt;List&gt;
{row.original.items.map(item => (
<ListItem key={item.id}>
{item.name} - ${item.price}
</ListItem>
))}
</List>
</VStack>
</Box>
);

const columns = useMemo(
() => [
{ Header: 'Order #', accessor: 'orderNumber' },
{ Header: 'Date', accessor: 'date' },
{ Header: 'Total', accessor: 'total' },
{ Header: 'Status', accessor: 'status' },
],
[],
);

return (
<WPExpandableDataTable
customColumn={columns}
tableData={data || []}
isLoading={isLoading}
error={error}
renderRowSubComponent={renderRowSubComponent}
/>
);
}

Editable Table

function EditableProductTable() {
const [data, setData] = useState(initialData);
const [skipPageReset, setSkipPageReset] = useState(false);

const updateData = (rowIndex, columnId, value) => {
setSkipPageReset(true);
setData(oldData =>
oldData.map((row, index) => {
if (index === rowIndex) {
return { ...row, [columnId]: value };
}
return row;
}),
);
};

const columns = useMemo(
() => [
{
Header: 'Product Name',
accessor: 'name',
Cell: ({ value, row, column }) => (
<EditableCell
value={value}
row={row}
column={column}
updateData={updateData}
/>
),
},
{
Header: 'Price',
accessor: 'price',
Cell: ({ value, row, column }) => (
<EditableCell
value={value}
row={row}
column={column}
updateData={updateData}
type='number'
/>
),
},
],
[],
);

return (
<WPBasicEditableTable
customColumn={columns}
tableData={data}
updateData={updateData}
skipPageReset={skipPageReset}
/>
);
}

Table with Pagination

function PaginatedTable() {
const [pageIndex, setPageIndex] = useState(0);
const [pageSize, setPageSize] = useState(10);

const { data, isLoading, error } = useQuery(
['users', pageIndex, pageSize],
() => fetchUsers({ page: pageIndex + 1, limit: pageSize }),
);

const paginatedData = {
current_page: data?.current_page || 1,
last_page: data?.last_page || 1,
per_page: data?.per_page || 10,
total: data?.total || 0,
};

return (
<WPBasicDataTable
customColumn={columns}
tableData={data?.data || []}
isLoading={isLoading}
error={error}
paginatedData={paginatedData}
initialState={{ pageIndex, pageSize }}
/>
);
}

Styling & Theming

Table Variants

// Available variants
<V2WPBaseTable
variant='simple' // Simple table
variant='striped' // Striped rows
variant='outline' // Outlined table
variant='unstyled' // No styling
/>

Custom Styling

<WPBasicDataTable
customColumn={columns}
tableData={data}
tableBgColor='white'
tableHeadBgColor='gray.50'
sx={{
'& thead tr': {
bg: 'blue.50',
},
'& tbody tr:hover': {
bg: 'gray.50',
},
}}
/>

Responsive Tables

<Box overflowX='auto'>
<WPBasicDataTable customColumn={columns} tableData={data} minWidth='600px' />
</Box>

Table Context

Using Table Context

import { TableProvider, useTableState } from 'components/Tables/TableContext';

function MyTable() {
const { pagination, filters } = useTableState();

return (
<WPBasicDataTable
customColumn={columns}
tableData={data}
// Table will use context state
/>
);
}

function App() {
return (
&lt;TableProvider&gt;
<MyTable />
</TableProvider>
);
}

Table Actions

import { useTableDispatch } from 'components/Tables/TableContext';

function TableControls() {
const dispatch = useTableDispatch();

const handleResetPage = () => {
dispatch({ type: 'PAGE_RESET' });
};

const handleSetFilter = filters => {
dispatch({ type: 'SET_FILTERS', payload: filters });
};

return (
&lt;HStack&gt;
<Button onClick={handleResetPage}>Reset Page</Button>
<Button onClick={() => handleSetFilter({ status: 'active' })}>
Filter Active
</Button>
</HStack>
);
}

Performance Optimization

Memoization

const columns = useMemo(
() => [
{ Header: 'Name', accessor: 'name' },
{ Header: 'Email', accessor: 'email' },
],
[],
);

const data = useMemo(() => tableData, [tableData]);

const MemoizedTable = memo(WPBasicDataTable);

Virtualization

// For large datasets
import { FixedSizeList as List } from 'react-window';

const VirtualizedRow = ({ index, style }) => (
<div style={style}>{/* Row content */}</div>
);

<List height={400} itemCount={data.length} itemSize={50}>
{VirtualizedRow}
</List>;

Lazy Loading

const TableComponent = lazy(() => import('components/Tables/WPBasicDataTable'));

<Suspense fallback={<TableSkeleton />}>
<TableComponent {...props} />
</Suspense>;

Accessibility

ARIA Support

  • Tables include proper ARIA labels and roles
  • Sortable columns have ARIA sort indicators
  • Selected rows are announced to screen readers
  • Loading states are announced
  • Error states are accessible

Keyboard Navigation

  • Tab navigation between interactive elements
  • Arrow keys for row/column navigation
  • Space/Enter for selection
  • Sort controls are keyboard accessible

Screen Reader Support

  • Table headers are properly associated
  • Row/column counts are announced
  • Selection state is announced
  • Pagination state is accessible

Testing

Unit Testing

import { render, screen } from '@testing-library/react';
import WPBasicDataTable from 'components/Tables/WPBasicDataTable';

const mockData = [{ name: 'John', email: 'john@example.com' }];

const mockColumns = [
{ Header: 'Name', accessor: 'name' },
{ Header: 'Email', accessor: 'email' },
];

test('renders table with data', () => {
render(<WPBasicDataTable customColumn={mockColumns} tableData={mockData} />);

expect(screen.getByText('John')).toBeInTheDocument();
expect(screen.getByText('john@example.com')).toBeInTheDocument();
});

Integration Testing

import { render, screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

test('table selection works', async () => {
const user = userEvent.setup();
const onRowClick = jest.fn();

render(
<WPSelectDataTable
customColumn={mockColumns}
tableData={mockData}
onRowClick={onRowClick}
/>,
);

const checkbox = screen.getByRole('checkbox');
await user.click(checkbox);

expect(onRowClick).toHaveBeenCalledWith(expect.any(Array));
});

Migration Guide

From V1 to V2 Tables

// V1 (React Table v7)
import WPBasicDataTable from 'components/Tables/WPBasicDataTable';

<WPBasicDataTable customColumn={columns} tableData={data} />;

// V2 (TanStack Table v8)
import { V2WPBaseTable } from 'components/Tables/V2WPTables';

<V2WPBaseTable columns={columns} tableData={data} />;

Column Definition Changes

// V1 Column
{
Header: 'Name',
accessor: 'name',
Cell: ({ value }) => <span>{value}</span>
}

// V2 Column
{
header: 'Name',
accessorKey: 'name',
cell: info => <span>{info.getValue()}</span>
}

Common Issues

Performance Issues

  • Use memoization for columns and data
  • Implement pagination for large datasets
  • Consider virtualization for very large tables
  • Avoid complex calculations in cell renderers

Styling Issues

  • Check Chakra UI theme compatibility
  • Verify responsive behavior
  • Test with different screen sizes
  • Ensure proper contrast ratios

Data Issues

  • Validate data structure matches columns
  • Handle empty/null data gracefully
  • Implement proper error boundaries
  • Test with edge cases

Best Practices

  1. Column Configuration: Define columns outside component or use useMemo
  2. Data Handling: Validate and normalize data before passing to table
  3. Performance: Use pagination and memoization for large datasets
  4. Accessibility: Test with screen readers and keyboard navigation
  5. Error Handling: Implement proper error states and fallbacks
  6. Loading States: Show appropriate loading indicators
  7. Responsive Design: Test on various screen sizes
  8. Type Safety: Use TypeScript for better development experience