@jeremyling/react-material-ui-enhanced-table
v0.6.2
Published
An extended React Material UI Table that aims to make creating complex tables more straightforward.
Downloads
49
Maintainers
Readme
React Material UI Enhanced Table
An extended React Material UI Table that aims to make creating complex tables more straightforward.
Installation
npm install --save @jeremyling/react-material-ui-enhanced-table
The following packages are peer dependencies that must be installed for this package to work.
@mui/material
@mui/icons-material
@mui/lab
date-fns
lodash
Usage Examples
Example of a collapsible table with a nested table as the collapse content.
import React, { useEffect, useState } from "react";
import EnhancedTable from "@jeremyling/react-material-ui-enhanced-table";
import { amber, green, indigo, lightGreen, red } from "@mui/material/colors";
import { Block, MoveToInbox } from "@mui/icons-material";
export default function Orders(props) {
const [data, setData] = useState([]);
const [totalCount, setTotalCount] = useState();
const [tableLoading, setTableLoading] = useState(false);
const [order, setOrder] = useState("");
const [orderBy, setOrderBy] = useState("");
const [page, setPage] = useState(1);
const [rowsPerPage, setRowsPerPage] = useState(10);
const [selectedRow, setSelectedRow] = useState();
const [selectedItems, setSelectedItems] = useState({});
const [openRows, setOpenRows] = useState({});
const [filters, setFilters] = useState();
const [notificationsCount, setNotificationsCount] = useState();
const headers = [
{
key: "collapse",
collapse: true,
},
{ attribute: "order_number", label: "Order No" },
{
key: "status",
attribute: "status",
label: "Status",
chip: true,
chipColor: {
"In Progress": [amber[50], amber[200]],
Completed: [green[50], green[200]],
},
},
{
attribute: "updated_at",
datetime: true,
label: "Updated",
},
{
multiField: true,
key: "customer",
multiFieldData: [
{
attribute: "user.name",
},
{
attribute: "user.email",
},
{
attribute: "user.phone",
},
],
html: `
<div><strong>{{0}}</strong></div>
<div style="color: #777">{{1}}</div>
<div style="color: #777">{{2}}</div>
`,
label: "Customer",
},
{
attribute: "total",
label: "Total",
numeric: true,
price: true,
},
{
key: "actions",
actions: [
{
id: "process",
icon: <MoveToInbox />,
tooltip: "Process",
onClick: (event, row) => handleAction(event, "process", row),
color: amber[400],
hideCondition: (row) => {},
},
{
id: "cancel",
icon: <Block />,
tooltip: "Cancel",
onClick: (event, row) => handleAction(event, "cancel", row),
color: indigo[400],
},
],
label: "Actions",
},
];
const collapseContent = () => {
const collapseHeaders = [
{
key: "checkbox",
checkbox: true,
},
{
key: "image",
label: "Image",
component: (data) => (
<img src={data.image.url} alt={data.name} loading="lazy" />
),
stopPropagation: true,
},
{
multiField: true,
key: "product",
multiFieldData: [
{
attribute: "product.name",
},
{
attribute: "product.sku",
},
],
html: `
<div><strong>{{0}}</strong></div>
<div><strong style="color: #777">SKU: {{1}}</strong></div>
`,
label: "Product",
minWidth: "200px",
},
{
key: "status",
attribute: "status",
label: "Status",
chip: true,
chipColor: {
Pending: [amber[50], amber[200]],
Paid: [lightGreen[50], lightGreen[200]],
Processed: [indigo[50], indigo[200]],
Completed: [green[50], green[200]],
},
},
{
attribute: "updated_at",
datetime: true,
label: "Updated",
},
{ attribute: "quantity", label: "Qty", numeric: true },
{
key: "actions",
actions: [
{
id: "process",
icon: <MoveToInbox />,
tooltip: "Process",
onClick: (event, row) => handleAction(event, "process", row),
color: amber[400],
hideCondition: (data) => {},
},
{
id: "cancel-order",
icon: <Block />,
tooltip: "Cancel",
onClick: (event, row) => handleAction(event, "cancel", row),
color: indigo[400],
},
],
label: "Actions",
},
];
if (data) {
return data.map((order) => (
<EnhancedTable
key={order.order_number}
rows={order.order_items}
headers={collapseHeaders}
handleActionClick={(event, key) => handleAction(event, key)}
handleRowClick={(event, row) =>
handleCollapsibleTableRowClick(event, row)
}
selected={selectedItems[order.id]}
showToolbar={false}
selectibleRows={getSelectibleItems(selectedRow)}
/>
));
}
};
const handleRowClick = (event, item) => {
setSelectedRow(item);
};
function handleCollapsibleTableRowClick(event, row) {}
function getSelectibleItems(row) {}
function updateOpenRows(event, key, value) {
event.stopPropagation();
let updated = JSON.parse(JSON.stringify(openRows));
updated[key] = value;
setOpenRows(updated);
}
const handleRequestSort = (event, property) => {
if (orderBy !== property) {
setOrder("asc");
setOrderBy(property);
return;
}
switch (order) {
case "asc":
setOrder("desc");
break;
case "desc":
setOrder("");
setOrderBy("");
break;
default:
break;
}
};
const handlePageChange = (event, newPage) => {
setPage(newPage);
};
const handleRowsPerPageChange = (event) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(1);
};
const handleDateChange = (type, value) => {
// Update filters
};
function handleAction(event, key, row = null) {}
useEffect(() => {
getData();
function getData() {
// Fetch data with filters
}
}, [order, orderBy, rowsPerPage, page, filters]);
return (
<EnhancedTable
rows={data || []}
totalCount={totalCount}
headers={headers}
order={order}
orderBy={orderBy}
loading={tableLoading}
page={page}
rowsPerPage={rowsPerPage}
handleRowClick={handleRowClick}
handleRequestSort={handleRequestSort}
handlePageChange={handlePageChange}
handleRowsPerPageChange={handleRowsPerPageChange}
handleActionClick={(event, key) => handleAction(event, key)}
handleDateChange={handleDateChange}
dates={{ from: filters.dateFrom, to: filters.dateTo }}
actionButtons={["dateFilters", "refresh"]}
collapsible={true}
collapseContent={collapseContent}
refreshBadgeCount={notificationsCount}
disableSelection={true}
openRows={openRows}
handleCollapseIconClick={updateOpenRows}
/>
);
}
Example with a nested table within each row
import React, { useEffect, useState } from "react";
import EnhancedTable from "@jeremyling/react-material-ui-enhanced-table";
import { green, red } from "@mui/material/colors";
export default function Products(props) {
const [data, setData] = useState([]);
const [totalCount, setTotalCount] = useState();
const [tableLoading, setTableLoading] = useState(true);
const [order, setOrder] = useState("asc");
const [orderBy, setOrderBy] = useState("");
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const [selected, setSelected] = useState({});
const [universalFilter, setUniversalFilter] = useState("");
const [nestedRowAction, setNestedRowAction] = useState({});
const [filters, setFilters] = useState();
const headers = [
{
key: "images",
label: "Images",
component: (data) => (
<img src={data.image.url} alt={data.name} loading="lazy" />
),
stopPropagation: true,
},
{
multiField: true,
key: "product",
multiFieldData: [
{
attribute: "name",
},
{
attribute: "sku",
},
],
html: `
<div><strong>{{0}}</strong></div>
<div><strong style="color: #777">SKU: {{2}}</strong></div>
`,
label: "Product",
minWidth: "200px",
},
{
key: "status",
attribute: "status",
label: "Status",
chip: true,
chipColor: {
Inactive: [red[50], red[200]],
Active: [green[50], green[200]],
},
},
{ attribute: "category", label: "Category" },
{ attribute: "stock", label: "Stock", sortable: true, numeric: true },
{
key: "price",
label: "Price",
arrayAttribute: "prices",
childAttribute: "amount",
childAttribute2: "currency",
childLabelAttribute: "tag",
childActions: { delete: true, edit: true, add: true },
numeric: true,
price: true,
},
];
const handleRowClick = (event, item) => {
if (item.id === selected.id) {
setSelected({});
return;
}
setSelected(item);
};
const handleAction = (event, key) => {};
const handleRequestSort = (event, property) => {
if (orderBy !== property) {
setOrder("asc");
setOrderBy(property);
return;
}
switch (order) {
case "asc":
setOrder("desc");
break;
case "desc":
setOrder("asc");
setOrderBy("");
break;
default:
break;
}
};
const handleUniversalFilterChange = (event) => {
setUniversalFilter(event.target.value);
};
const handlePageChange = (event, newPage) => {
setPage(newPage);
};
const handleRowsPerPageChange = (event) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
};
const handleNestedAction = (event, type, item, id) => {};
const handleNestedFieldChange = (parentId, id, attribute, value) => {};
useEffect(() => {
const getData = () => {
// Fetch data with filters
};
getData();
}, [universalFilter, order, orderBy, rowsPerPage, page, filters]);
return (
<EnhancedTable
rows={data || []}
totalCount={totalCount}
descriptorAttribute="name"
headers={headers}
order={order}
orderBy={orderBy}
loading={tableLoading}
page={page}
rowsPerPage={rowsPerPage}
selected={[selected]}
handleRowClick={handleRowClick}
handleRequestSort={handleRequestSort}
handlePageChange={handlePageChange}
handleRowsPerPageChange={handleRowsPerPageChange}
handleNestedAction={handleNestedAction}
handleNestedFieldChange={handleNestedFieldChange}
handleActionClick={(event, key) => handleAction(event, key)}
actionButtons={["create", "edit", "delete", "filter", "refresh"]}
handleUniversalFilterChange={handleUniversalFilterChange}
nestedRowAction={nestedRowAction}
></EnhancedTable>
);
}
Props
| Prop | Type | Default | Description |
| --------------------------- | -------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| title | string
| undefined
| Table title |
| rows | array
| required | Table row data |
| headers | array
| required | Table headers. Customise with props (see below) |
| dense | bool
| true
| Whether to use dense prop for Material UI's Table |
| order | string
| asc
| Column sort order. One of :asc
,desc
|
| orderBy | string
| id
| Attribute to sort column by |
| loading | bool
| false
| Whether to display loading Backdrop component |
| page | number
| 1
| Current page |
| totalCount | number
| undefined
| Total result count |
| rowsPerPage | number
| 10
| Number of rows per page |
| selected | array
| []
| Selected row |
| descriptorAttribute | string
| undefined | Attributed used to display descriptor for selected row in toolbar |
| handleRowClick | func
| () => {}
| Method to handle row click event |
| handleRequestSort | func
| () => {}
| Method to handle column sort click |
| handlePageChange | func
| undefined
| Method to handle page change |
| handleRowsPerPageChange | func
| undefined
| Method to handle rows per page change |
| handleUniversalFilterChange | func
| () => {}
| Method to handle universal filter onChange event |
| handleDateChange | func
| () => {}
| Method to handle date changes in date filters |
| handleActionClick | func
| () => {}
| Method to handle table action click |
| handleNestedAction | func
| () => {}
| Method to handle action click within a nested table in a row |
| handleNestedFieldChange | func
| () => {}
| Method to handle nested field onChange event |
| nestedRowAction | object
| {}
| Object indicating the actions available for each nested row. Possible actions include add
or edit
. Object needs to be of the form { [nestedRowIdentifier]: { add: true, edit: true } }
|
| date | string
| undefined | Filter dates. Must be in the form yyyy-MM-dd |
| dates | object
| undefined | Filter dates. Must be in the form { from: "yyyy-MM-dd", to: "yyyy-MM-dd" }
|
| actionButtons | array
| ["create", "edit", "delete", "filter"]
| Actions to include in table |
| showToolbar | bool
| true
| Whether to show the toolbar |
| collapsible | bool
| false
| Whether each row should be collapsible |
| collapseContent | array
| null
| Array of content for each collapsible row. Index of this array should correspond to index of rows. The collapse content for rows[0]
should be collapseContent[0]
. Default collapse content is a table. |
| collapseHeaders | array
| []
| Headers for default table within collapse content. Required if collapsible = true
and collapseContent
prop is not passed |
| openRows | object
| {}
| Object to indicate which collapsible rows should be open. Object should be of the form { [row[identifier]]: true }
|
| identifier | string
| id
| Attribute used as row identifier |
| handleCollapseIconClick | func
| () => {}
| Method to handle collapse icon click event |
| disableRowClick | bool
| false
| Whether to ignore click event on row |
| disableSelection | bool
| false
| Makes rows unselectable |
| selectibleRows | array
| null
| Manually define the selectible rows. Array should contain the row identifiers |
| refreshBadgeCount | number
| 0
| Badge count for refresh button. This can be used to indicate whether the table has pending unfetched data |
Header Props
| Prop | Type | Default | Description |
| ------------------------ | -------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| key | string
| attribute
| Attribute used to determine row key |
| attribute | string
| undefined
| Attribute used to determine cell content |
| label | string
| undefined
| Header label |
| multiField | bool
| undefined
| Whether the cell should display content from multiple attributes. Best used with data
and html
props. |
| multiFieldData | array
| undefined
| Array of attributes used for multiField content. Required if multiField = true
. Array should be of the form [{ attribute: "name" }]
|
| html | string
| undefined
| HTML code for displaying content. Attribute will be substituted in for {{0}}
. If used with multiField
, data
attributes will be substituted in for {{index}}
, where index is the position index of the data
array |
| chip | bool
| undefined
| If true, column will contain a Material UI Chip populated with attribute
. The color
prop can be used to determine the Chip's color |
| chipColor | object
| undefined
| Used with chip
. Object of the form { [option]: [bgColor, borderColor] }
|
| collapse | bool
| undefined
| If true, column will contain a collapse icon |
| checkbox | bool
| undefined
| If true, column will contain a Material UI Checkbox component that is controlled by whether the row is selected |
| actions | array
| undefined
| Array containing possible actions for each row. Should be of the form { id: "cancel", icon: <Block />, tooltip: "Cancel", onClick: (event, row) => {}, color: indigo[400], hideCondition: (data) => {} }
|
| headerActions | array
| undefined
| Array containing possible actions for the header. Should be of the form { id: 'edit', icon: <Edit />, tooltip: 'Edit', onClick: (event) => {}, hideCondition: false, color: indigo[400]}
|
| component | func
| undefined
| Custom component for cell content. Row data is passed as sole parameter. |
| stopPropagation | bool
| undefined
| Used with component
. If true, cell component click will stop further propagation to parent row. |
| arrayAttribute | string
| undefined
| Attribute used to populate nested table within row |
| childAttribute | string
| undefined
| Attribute within arrayAttribute
object to display as nested row |
| childAttribute2 | string
| undefined
| Attribute within arrayAttribute
object to display on left of childAttribute
within nested row |
| childLabelAttribute | string
| undefined
| Attribute within arrayAttribute
object to display on left of childAttribute and childAttribute2 within nested row |
| childAttributeLabel | string
| undefined
| Label for childAttribute
|
| childAttribute2Label | string
| undefined
| Label for childAttribute2
|
| childLabelAttributeLabel | string
| undefined
| Label for childLabelAttribute
|
| childActions | object
| undefined
| Object indicating which actions to show for each nested row. Actions available are add
, edit
and delete
. Should be of the form { delete: true, edit: true, add: true }
. |
| orderBy^ | array
| undefined
| Attribute to order nested table content. Should be of the form [attribute, asc\|desc]
|
| numeric | bool
| undefined
| If true, content will be right-aligned |
| price | bool
| undefined
| If true, content will be prefixed with $
|
| date | bool
| undefined
| If true, content will be parsed with date-fns in the format d MMM yyyy
|
| datetime | bool
| undefined
| If true, content will be parsed with date-fns in the format d MMM yyyy
and h:mm:ss a
|
| time | bool
| undefined
| If true, content will be parsed with date-fns in the format h:mm:ss a
|
| truncate | number
| undefined
| Max length of string content |
| width | number
| undefined
| Fixed width for column |
| minWidth | number
| undefined
| Minimum width for column |
| maxWidth | number
| undefined
| Maximum width for column |
| disablePadding | bool
| undefined
| If true, cell padding will be set to 0 |
| sortable | bool
| undefined
| If true, column will be sortable based on attribute
. attribute
is required for column to be sortable |
^Only for nested table