Skip to main content

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@0.2.0

πŸ“– Usage​

import {ListProductsTable} from '@nodeblocks/frontend-list-products-table-block';
Live Editor
function BasicListProductsTable() {
  const [currentTab, setCurrentTab] = useState('published');
  const [isLoading] = 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...

πŸ”§ Props Reference​

Main Component Props​

PropTypeDefaultDescription
listProductsTableTitleReactNodeRequiredTitle for the products table section
labelsTableLabelsRequiredLabels object for table headers, actions, and messages
isLoadingbooleanundefinedWhether the table is currently loading
onNavigate(to: string) => voidRequiredCallback function for navigation
onProductPublic(id: string, title: string) => voidRequiredCallback function when product is made public
onProductPrivate(id: string, title: string) => voidRequiredCallback function when product is made private
onProductArchive(id: string, title: string) => voidRequiredCallback function when product is archived
onProductUnarchive(id: string) => voidRequiredCallback function when product is unarchived
dataListProductsTableRowData[]RequiredArray of product data objects
rowHref(row: ListProductsTableRowData) => stringRequiredFunction to generate row link URLs
updateRowHref(row: ListProductsTableRowData) => stringRequiredFunction to generate edit page URLs
tabsTabData[]RequiredArray of tab configuration objects
currentTabstringundefinedCurrently active tab label
onTabChange(tab: string) => voidundefinedCallback function when tab is changed
createHrefstringRequiredURL for creating new products
paginationPaginationPropsundefinedPagination configuration object
spacingnumber3Spacing between child elements
classNamestringundefinedAdditional CSS class name for styling the container
childrenBlocksOverrideundefinedCustom block components to override default rendering

Note: The main component extends MUI StackProps. Default styling includes p: 3 padding.

Sub-Components​

The ListProductsTable component provides several sub-components that can be used independently:

ListProductsTable.Header​

Container for title and action button.

PropTypeDefaultDescription
childrenReactNodeDefault layoutCustom content to override default header rendering
classNamestringundefinedAdditional CSS class name for styling

Note: This component extends MUI StackProps. Default styling includes direction="row" and justifyContent: 'space-between'.

ListProductsTable.Title​

Title typography component.

PropTypeDefaultDescription
listProductsTableTitleReactNodeFrom contextContent to display as title
variantTypographyVariant"h4"MUI Typography variant
componentElementType"h1"HTML element to render
childrenReactNodeundefinedCustom content to override title
classNamestringundefinedAdditional CSS class name

Note: This component extends MUI TypographyProps.

ListProductsTable.Action​

Container for action buttons (e.g., create product).

PropTypeDefaultDescription
createHrefstringFrom contextURL for the create action button
labelsTableLabelsFrom contextLabels for action text
onNavigate(to: string) => voidFrom contextNavigation callback
childrenReactNodeDefault buttonCustom content to override action rendering
classNamestringundefinedAdditional CSS class name

Note: This component extends MUI StackProps (except direction). Default styling includes direction="row" and alignItems: 'center'.

ListProductsTable.Content​

Container for table content area.

PropTypeDefaultDescription
isLoadingbooleanFrom contextLoading state
childrenReactNodeDefault layoutCustom content to override rendering
classNamestringundefinedAdditional CSS class name

Note: This component extends MUI BoxProps. When no children provided, renders Loader (if loading) or Tabs + Table.

ListProductsTable.Loader​

Loading indicator component.

PropTypeDefaultDescription
childrenReactNodeCircularProgressCustom loading indicator
classNamestringundefinedAdditional CSS class name

Note: This component extends MUI StackProps. Default styling includes alignItems: 'center'.

ListProductsTable.Tabs​

Tab navigation component.

PropTypeDefaultDescription
tabsTabData[]From contextArray of tab configuration objects
currentTabstringFrom contextCurrently active tab label
onTabChange(tab: string) => voidFrom contextTab change callback function
classNamestringundefinedAdditional CSS class name

Note: This component extends MUI TabsProps (except value, onChange, variant). Uses variant="fullWidth" internally.

ListProductsTable.Table​

Data table component with row actions and pagination.

PropTypeDefaultDescription
labelsTableLabelsFrom contextLabels for headers and actions
dataListProductsTableRowData[]From contextArray of table data
rowHref(row) => stringFrom contextFunction to generate row link URLs
updateRowHref(row) => stringFrom contextFunction to generate edit page URLs
onNavigate(to: string) => voidFrom contextNavigation callback function
onProductPublic(id, title) => voidFrom contextCallback when product is made public
onProductPrivate(id, title) => voidFrom contextCallback when product is made private
onProductArchive(id, title) => voidFrom contextCallback when product is archived
onProductUnarchive(id) => voidFrom contextCallback when product is unarchived
paginationPaginationPropsFrom contextPagination configuration
spacingnumber3Spacing between elements
classNamestringundefinedAdditional CSS class name

Note: This component extends MUI StackProps. Shows empty state when data is empty.


🎨 Configuration examples​

Custom Header Layout:

<ListProductsTable {...props}>
<ListProductsTable.Header sx={{bgcolor: 'primary.main', p: 2, borderRadius: 2}}>
<ListProductsTable.Title sx={{color: 'white'}} />
<ListProductsTable.Action />
</ListProductsTable.Header>
<ListProductsTable.Content />
</ListProductsTable>

With Pagination:

const pagination = {
currentPage: currentPage,
totalPages: 10,
onPageChange: page => {
setCurrentPage(page);
console.log('Fetching page:', page);
},
};

<ListProductsTable
{...props}
pagination={pagination}
>
<ListProductsTable.Header />
<ListProductsTable.Content />
</ListProductsTable>

Block Override Pattern:

<ListProductsTable {...props}>
{({defaultBlocks, defaultBlockOrder}) => ({
blocks: {
...defaultBlocks,
header: {
...defaultBlocks.header,
props: {
...defaultBlocks.header.props,
sx: {bgcolor: '#f5f5f5', p: 3, borderRadius: 2},
},
},
},
blockOrder: defaultBlockOrder,
})}
</ListProductsTable>

πŸ”§ TypeScript Support​

Full TypeScript support with comprehensive type definitions:

import {ListProductsTable} from '@nodeblocks/frontend-list-products-table-block';
import {useState} from 'react';

interface ListProductsTableRowData {
id: string;
title: string;
category: string;
createdAt: string;
updatedAt: string;
numberOfApplicants: string;
publication?: {
since?: string;
until?: string;
status?: string;
};
}

interface TabData {
key?: string;
label: string;
isDisabled?: boolean;
subtitle?: string;
}

interface TableLabels {
emptyStateMessage: string;
actions: {
createProduct: string;
};
headerRow: {
title: string;
publicationPeriod: string;
createdAt: string;
updatedAt: string;
numberOfApplicants: string;
};
rowActions: {
public: string;
private: string;
edit: string;
archive: string;
unarchive: string;
};
unsetData: {
since: string;
until: string;
date: string;
};
}

function TypedListProductsTableExample() {
const [currentTab, setCurrentTab] = useState('published');
const [isLoading] = useState(false);

const products: ListProductsTableRowData[] = [
{
id: '1',
title: 'Product Name',
category: 'Electronics',
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',
},
},
];

const tabs: TabData[] = [
{label: 'Published', key: 'published'},
{label: 'Draft', key: 'draft'},
{label: 'Archived', key: 'archived', isDisabled: false},
];

const labels: TableLabels = {
emptyStateMessage: 'No products available',
actions: {createProduct: 'New Product'},
headerRow: {
title: 'Product Name',
publicationPeriod: 'Publication Period',
createdAt: 'Created',
updatedAt: 'Updated',
numberOfApplicants: 'Applicants',
},
rowActions: {
public: 'Publish',
private: 'Unpublish',
edit: 'Edit',
archive: 'Archive',
unarchive: 'Restore',
},
unsetData: {
since: 'Not set',
until: 'Not set',
date: 'No publication period',
},
};

return (
<ListProductsTable
listProductsTableTitle="Products Dashboard"
labels={labels}
isLoading={isLoading}
onNavigate={path => console.log('Navigate:', path)}
onProductPublic={(id, title) => console.log('Publish:', id, title)}
onProductPrivate={(id, title) => console.log('Unpublish:', id, title)}
onProductArchive={(id, title) => console.log('Archive:', id, title)}
onProductUnarchive={id => console.log('Restore:', id)}
data={products}
rowHref={row => `/products/${row.id}`}
updateRowHref={row => `/products/${row.id}/edit`}
tabs={tabs}
currentTab={currentTab}
onTabChange={setCurrentTab}
createHref="#products/create"
>
<ListProductsTable.Header />
<ListProductsTable.Content />
</ListProductsTable>
);
}

πŸ“ Notes​

  • The main component extends MUI StackProps with default spacing={3} and p: 3 padding
  • ListProductsTable.Title uses MUI Typography with variant="h4" and component="h1" by default
  • ListProductsTable.Tabs uses MUI Tabs with variant="fullWidth" - tabs stretch to fill container
  • Dates are formatted using luxon library: yyyy/M/d for publication dates, yyyy/M/d HH:mm for created/updated
  • Row dropdown menu shows different actions based on publication.status:
    • ACTIVE status: shows "Make Private", "Edit", "Archive" options
    • INACTIVE status: shows "Make Public", "Edit", "Archive" options
    • ARCHIVED status: shows only "Unarchive" option
  • Empty state displays a "person" icon with the emptyStateMessage label
  • When rowHref returns a URL, the entire table row becomes clickable with hover effect
  • Pagination uses MUI Pagination component with variant="outlined" and shape="rounded"

Built with ❀️ using React, TypeScript, and MUI.