List Products Table Block
The ListProductsTable Component is a fully customizable and accessible products table interface built with React and TypeScript. It provides a complete tabular product listing experience with modern design patterns, sortable columns, action dropdowns, tabs, publication status management, pagination support, loading states, and flexible customization options for advanced product management applications.
🚀 Installation
npm install @nodeblocks/frontend-list-products-table-block
📖 Usage
import {ListProductsTable} from '@nodeblocks/frontend-list-products-table-block';
- Basic Usage
- Advanced Usage
Live Editor
function BasicListProductsTable() { const [currentTab, setCurrentTab] = useState('published'); const [isLoading, setIsLoading] = useState(false); const productData = [ { id: '1', title: 'Premium Office Chair', category: 'Furniture', createdAt: '2024-01-15T10:30:00Z', updatedAt: '2024-01-16T14:20:00Z', numberOfApplicants: '5', publication: { since: '2024-01-15T00:00:00Z', until: '2024-03-15T23:59:59Z', status: 'ACTIVE' } }, { id: '2', title: 'Wireless Headphones', category: 'Electronics', createdAt: '2024-01-14T09:15:00Z', updatedAt: '2024-01-14T09:15:00Z', numberOfApplicants: '12', publication: { since: '2024-01-20T00:00:00Z', until: '2024-04-20T23:59:59Z', status: 'INACTIVE' } } ]; const tabs = [ { label: 'Published Products' }, { label: 'Draft Products' }, { label: 'Archived Products' } ]; const labels = { emptyStateMessage: 'No products found', actions: { createProduct: 'Create Product' }, headerRow: { title: 'Product Title', publicationPeriod: 'Publication Period', createdAt: 'Created At', updatedAt: 'Updated At', numberOfApplicants: 'Applicants' }, rowActions: { public: 'Make Public', private: 'Make Private', edit: 'Edit Product', archive: 'Archive Product', unarchive: 'Unarchive Product' }, unsetData: { since: 'Not set', until: 'Not set', date: 'No publication period' } }; const handleProductPublic = (productId, title) => { console.log('Product made public:', productId, title); }; const handleProductPrivate = (productId, title) => { console.log('Product made private:', productId, title); }; const handleProductArchive = (productId, title) => { console.log('Product archived:', productId, title); }; const handleProductUnarchive = (productId) => { console.log('Product unarchived:', productId); }; const handleNavigate = (path) => { console.log('Navigate to:', path); }; const getRowHref = (row) => `/products/${row.id}`; const getUpdateRowHref = (row) => `/products/${row.id}/edit`; return ( <ListProductsTable listProductsTableTitle="Product Management" labels={labels} isLoading={isLoading} onNavigate={handleNavigate} onProductPublic={handleProductPublic} onProductPrivate={handleProductPrivate} onProductArchive={handleProductArchive} onProductUnarchive={handleProductUnarchive} data={productData} rowHref={getRowHref} updateRowHref={getUpdateRowHref} tabs={tabs} currentTab={currentTab} onTabChange={setCurrentTab} createHref="#products/new"> <ListProductsTable.Header style={{display: 'flex', justifyContent: 'space-between'}}> <ListProductsTable.Title /> <ListProductsTable.Action /> </ListProductsTable.Header> <ListProductsTable.Content> {isLoading ? ( <ListProductsTable.Loader /> ) : ( <> <ListProductsTable.Tabs /> <ListProductsTable.Table /> </> )} </ListProductsTable.Content> </ListProductsTable> ); }
Result
Loading...
Live Editor
function AdvancedProductsTable() { const [currentTab, setCurrentTab] = useState('published'); const [isLoading, setIsLoading] = useState(false); const productData = [ { id: '3', title: '🎧 Premium Noise-Canceling Headphones', category: 'Electronics', createdAt: '2024-01-15T10:30:00Z', updatedAt: '2024-01-16T14:20:00Z', numberOfApplicants: '24', publication: { since: '2024-01-15T00:00:00Z', until: '2024-03-15T23:59:59Z', status: 'ACTIVE' } }, { id: '4', title: '💻 MacBook Pro 16" M3 Max', category: 'Computing', createdAt: '2024-01-14T09:15:00Z', updatedAt: '2024-01-14T09:15:00Z', numberOfApplicants: '45', publication: { since: '2024-01-20T00:00:00Z', until: '2024-04-20T23:59:59Z', status: 'INACTIVE' } }, { id: '6', title: '🎮 PlayStation 5 Pro Console', category: 'Gaming', createdAt: '2024-01-13T08:45:00Z', updatedAt: '2024-01-15T16:30:00Z', numberOfApplicants: '67', publication: { since: '2024-01-13T00:00:00Z', until: '2024-05-13T23:59:59Z', status: 'ACTIVE' } } ]; const tabs = [ { label: '🚀 Live Products', key: 'published' }, { label: '📝 Draft Products', key: 'draft' }, { label: '📦 Archived Products', key: 'archived' } ]; const labels = { emptyStateMessage: 'No products found in this category', actions: { createProduct: '✨ Add New Product' }, headerRow: { title: 'Product Information', publicationPeriod: 'Availability Period', createdAt: 'Created', updatedAt: 'Last Updated', numberOfApplicants: 'Interest Count' }, rowActions: { public: 'Publish', private: 'Make Private', edit: 'Edit Details', archive: 'Archive', unarchive: 'Restore' }, unsetData: { since: 'Start date TBD', until: 'End date TBD', date: 'Publication period not configured' } }; return ( <ListProductsTable listProductsTableTitle="🛍️ Premium Product Dashboard" labels={labels} isLoading={isLoading} onNavigate={(path) => console.log('Navigate to:', path)} onProductPublic={(id, title) => console.log('Publishing:', title)} onProductPrivate={(id, title) => console.log('Making private:', title)} onProductArchive={(id, title) => console.log('Archiving:', title)} onProductUnarchive={(id) => console.log('Restoring product:', id)} data={productData} rowHref={(row) => `/products/${row.id}`} updateRowHref={(row) => `/products/${row.id}/edit`} tabs={tabs} currentTab={currentTab} onTabChange={setCurrentTab} createHref="#products/new"> {({ defaultBlocks, defaultBlockOrder }) => ({ blocks: { ...defaultBlocks, // Enhanced header with gradient styling header: { ...defaultBlocks.header, props: { ...defaultBlocks.header.props, style: { background: 'white', borderRadius: '20px', padding: '32px', color: 'white', marginBottom: '24px', width: 'calc(100% - 48px)', margin: '0 auto', marginTop: '24px', boxShadow: '0 15px 35px rgba(0,0,0,0.1)', border: '0.5px solid rgb(0, 0, 0)', } } }, // Enhanced content with modern styling content: { ...defaultBlocks.content, props: { ...defaultBlocks.content.props, style: { background: 'white', borderRadius: '16px', padding: '24px', boxShadow: '0 10px 35px rgba(0,0,0,0.05)', width: 'calc(100% - 48px)', margin: '0 auto', borderRadius: '16px', border: '0.5px solid rgb(0, 0, 0)', } } } }, blockOrder: defaultBlockOrder })} </ListProductsTable> ); }
Result
Loading...
🔧 Props Reference
Main Component Props
Prop | Type | Default | Description |
---|---|---|---|
listProductsTableTitle | ReactNode | Required | Title for the products table section |
labels | TableLabels | Required | Labels object for table headers, actions, and messages |
isLoading | boolean | undefined | Whether the table is currently loading |
onNavigate | (to: string) => void | Required | Callback function for navigation |
onProductPublic | ProductAction | Required | Callback function when product is made public |
onProductPrivate | ProductAction | Required | Callback function when product is made private |
onProductArchive | ProductAction | Required | Callback function when product is archived |
onProductUnarchive | (productId: string) => void | Required | Callback function when product is unarchived |
data | ListProductsTableRowData[] | Required | Array of product data objects |
rowHref | (row: ListProductsTableRowData) => string | Required | Function to generate row link URLs |
updateRowHref | (row: ListProductsTableRowData) => string | Required | Function to generate update row link URLs |
tabs | Tab[] | Required | Array of tab configuration objects |
currentTab | string | undefined | Currently active tab identifier |
onTabChange | (tab: string) => void | undefined | Callback function when tab is changed |
createHref | string | Required | URL for creating new products |
pagination | {currentPage: number; onPageChange: (page: number) => void; totalPages: number} | From context | Pagination configuration |
className | string | undefined | Additional CSS class name for styling the container |
children | BlocksOverride | undefined | Custom block components to override default rendering |
Sub-Components
The ListProductsTable component provides several sub-components that can be used independently:
ListProductsTable.Header
Prop | Type | Default | Description |
---|---|---|---|
children | ReactNode | undefined | Custom content to override default header rendering |
className | string | undefined | Additional CSS class name for styling |
ListProductsTable.Title
Prop | Type | Default | Description |
---|---|---|---|
children | ReactNode | undefined | Custom content to override default title rendering |
className | string | undefined | Additional CSS class name for styling |
size | enum | "3XL" | Typography size for the title |
color | enum | "low-emphasis" | Color theme for the title |
weight | enum | "bold" | Weight of the title |
ListProductsTable.Action
Prop | Type | Default | Description |
---|---|---|---|
children | ReactNode | undefined | Custom content to override default action rendering |
className | string | undefined | Additional CSS class name for styling |
createHref | string | From context | URL for the create action button |
labels | TableLabels | From context | Labels for action text |
direction | enum | "row" | Flex direction for action buttons |
alignItems | enum | "stretch" | Alignment of items in the container |
gapSize | enum | "S" | Gap between items in the container |
ListProductsTable.Content
Prop | Type | Default | Description |
---|---|---|---|
children | ReactNode | undefined | Custom content to override default content rendering |
className | string | undefined | Additional CSS class name for styling |
isLoading | boolean | From context | Loading state from context |
ListProductsTable.Loader
Prop | Type | Default | Description |
---|---|---|---|
children | ReactNode | Circular progress indicator | Custom loading indicator content |
className | string | undefined | Additional CSS class name for styling |
direction | enum | "row" | Flex direction for action buttons |
alignItems | enum | "stretch" | Alignment of items in the container |
gapSize | enum | "S" | Gap between items in the container |
ListProductsTable.Tabs
Prop | Type | Default | Description |
---|---|---|---|
tabs | Tab[] | From context | Array of tab configuration objects |
currentTab | string | From context | Currently active tab identifier |
onTabChange | (tab: string) => void | From context | Tab change callback function |
tabWidth | string | "stretch" | Width behavior for tabs |
className | string | undefined | Additional CSS class name for styling |
ListProductsTable.Table
Prop | Type | Default | Description |
---|---|---|---|
className | string | undefined | Additional CSS class name for styling |
onProductPublic | (id: string, title: string) => void | From context | Callback function when product is made public |
onProductPrivate | (id: string, title: string) => void | From context | Callback function when product is made private |
onProductArchive | (id: string, title: string) => void | From context | Callback function when product is archived |
onProductUnarchive | (id: string) => void | From context | Callback function when product is unarchived |
data | ListProductsTableRowData[] | From context | Array of table data |
dropdownMenuItems | (cell: ListProductsTableRowData) => Array<DropDownMenuItems> | From context | Function to generate dropdown menu items |
dropdownMenuState | {openedDropdownMenuId: string} | From context | Currently opened dropdown menu |
setDropdownMenuState | ({openedDropdownMenuId: string}) => void | From context | Callback function when the opened dropdown menu changes |
emptyState | {icon?: IconType; message: string} | From context | Configuration for empty state |
isLoading | boolean | From context | Loading state |
onNavigate | (to: string) => void | From context | Navigation callback function |
pagination | {currentPage: number; onPageChange: (page: number) => void; totalPages: number} | From context | Pagination configuration |
rowHref | `(cell: ListProductsTableRowData) => string | undefined | null` |
updateRowHref | `(cell: ListProductsTableRowData) => string | undefined | null` |
🔧 TypeScript Support
Full TypeScript support with comprehensive type definitions:
import {ListProductsTable} from '@nodeblocks/frontend-list-products-table-block';
interface Tab {
isDisabled?: boolean;
key?: string;
label: string;
subtitle?: string;
}
interface ListProductsTableRowData {
category: string;
createdAt: string;
id: string;
numberOfApplicants: string;
publication?: {
since?: string;
status?: string;
until?: string;
};
title: string;
updatedAt: string;
}
interface CustomProductTableProps {
products: ListProductsTableRowData[];
onPublishProduct: (productId: string) => void;
onUnpublishProduct: (productId: string) => void;
onArchiveProduct: (productId: string) => void;
onRestoreProduct: (productId: string) => void;
tableTabs: Array<Tab>;
currentActiveTab: string;
onTabSwitch: (tab: string) => void;
isTableLoading: boolean;
}
const ProductTableComponent = ({
products,
onPublishProduct,
onUnpublishProduct,
onArchiveProduct,
onRestoreProduct,
tableTabs,
currentActiveTab,
onTabSwitch,
isTableLoading,
}: CustomProductTableProps) => {
const tableLabels = {
emptyStateMessage: 'No products available',
actions: {createProduct: 'New Product'},
headerRow: {
title: 'Product Name',
publicationPeriod: 'Publication Dates',
createdAt: 'Date Created',
updatedAt: 'Last Modified',
numberOfApplicants: 'Total Applicants',
},
rowActions: {
public: 'Publish',
private: 'Unpublish',
edit: 'Edit',
archive: 'Archive',
unarchive: 'Restore',
},
unsetData: {
since: 'Start date not set',
until: 'End date not set',
date: 'Publication period not configured',
},
};
return (
<ListProductsTable
listProductsTableTitle="Products Dashboard"
labels={tableLabels}
isLoading={isTableLoading}
onNavigate={path => console.log('Navigate:', path)}
onProductPublic={onPublishProduct}
onProductPrivate={onUnpublishProduct}
onProductArchive={onArchiveProduct}
onProductUnarchive={onRestoreProduct}
data={products}
rowHref={row => `/products/${row.id}`}
updateRowHref={row => `/products/${row.id}/edit`}
tabs={tableTabs}
currentTab={currentActiveTab}
onTabChange={onTabSwitch}
createHref="/products/create">
<ListProductsTable.Header />
<ListProductsTable.Content />
</ListProductsTable>
);
};
Built with ❤️ using React, TypeScript, and modern web standards.