diff --git a/packages/webapp/src/components/Table/Header/TableHead.jsx b/packages/webapp/src/components/Table/Header/TableHead.jsx
index 5489231083..4ba23254a6 100644
--- a/packages/webapp/src/components/Table/Header/TableHead.jsx
+++ b/packages/webapp/src/components/Table/Header/TableHead.jsx
@@ -13,6 +13,7 @@
* GNU General Public License for more details, see .
*/
import PropTypes from 'prop-types';
+import { Checkbox } from '@mui/material';
import TableCell from '@mui/material/TableCell';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
@@ -22,7 +23,17 @@ import { ReactComponent as UnfoldCircle } from '../../../assets/images/unfold-ci
import { ReactComponent as ArrowDownCircle } from '../../../assets/images/arrow-down-circle.svg';
import styles from '../styles.module.scss';
-export default function EnhancedTableHead({ columns, order, orderBy, onRequestSort, dense }) {
+export default function EnhancedTableHead({
+ columns,
+ order,
+ orderBy,
+ onRequestSort,
+ dense,
+ shouldShowCheckbox,
+ onSelectAllClick,
+ numSelected,
+ rowCount,
+}) {
const createSortHandler = (property) => (event) => {
onRequestSort(event, property);
};
@@ -30,6 +41,16 @@ export default function EnhancedTableHead({ columns, order, orderBy, onRequestSo
return (
+ {shouldShowCheckbox && (
+
+ 0 && numSelected < rowCount}
+ checked={rowCount > 0 && numSelected === rowCount}
+ onChange={onSelectAllClick}
+ />
+
+ )}
{columns.map(({ id, align, columnProps, label, sortable = true }) => {
if (!id) {
return null;
@@ -80,4 +101,8 @@ EnhancedTableHead.propTypes = {
order: PropTypes.oneOf(['asc', 'desc']).isRequired,
orderBy: PropTypes.string.isRequired,
dense: PropTypes.bool,
+ shouldShowCheckbox: PropTypes.bool,
+ onSelectAllClick: PropTypes.func,
+ numSelected: PropTypes.number,
+ rowCount: PropTypes.number,
};
diff --git a/packages/webapp/src/components/Table/TableV2.jsx b/packages/webapp/src/components/Table/TableV2.jsx
index 1017c19c79..2ab8308e1e 100644
--- a/packages/webapp/src/components/Table/TableV2.jsx
+++ b/packages/webapp/src/components/Table/TableV2.jsx
@@ -12,9 +12,10 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details, see .
*/
-import { useState, useMemo, useEffect } from 'react';
+import { useState, useMemo } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
+import { Checkbox } from '@mui/material';
import Box from '@mui/material/Box';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
@@ -93,6 +94,11 @@ export default function TableV2(props) {
defaultOrderBy,
alternatingRowColor,
showHeader,
+ onCheck,
+ handleSelectAllClick,
+ selectedIds,
+ stickyHeader,
+ maxHeight,
} = props;
const [order, setOrder] = useState('asc');
@@ -101,6 +107,7 @@ export default function TableV2(props) {
const [rowsPerPage, setRowsPerPage] = useState(minRows);
const fullColSpan = columns.reduce((total, column) => total + (column.id ? 1 : 0), 0);
+ const shouldShowCheckbox = onCheck && handleSelectAllClick && selectedIds;
const handleRequestSort = (event, property) => {
const isAsc = orderBy === property && order === 'asc';
@@ -108,12 +115,20 @@ export default function TableV2(props) {
setOrderBy(property);
};
- const handleRowClick = (event, index) => {
+ const handleRowClick = (event, row) => {
if (onRowClick) {
- onRowClick(event, index);
+ onRowClick(event, row);
}
};
+ const handleCheckboxClick = (event, row) => {
+ if (onCheck) {
+ onCheck(event, row);
+ }
+ // prevent handleRowClick from being called
+ event.stopPropagation();
+ };
+
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
@@ -142,10 +157,11 @@ export default function TableV2(props) {
return (
-
+
{showHeader && (
)}
{visibleRows.map((row, index) => {
+ const isItemSelected = selectedIds?.includes(row.id);
+
return (
handleRowClick(event, row)}
+ isItemSelected={isItemSelected}
+ aria-checked={isItemSelected}
className={clsx(
styles.tableRow,
styles.itemRow,
@@ -169,6 +193,16 @@ export default function TableV2(props) {
alternatingRowColor ? styles.alternatingRowColor : styles.plainRowColor,
)}
>
+ {shouldShowCheckbox && (
+
+ handleCheckboxClick(event, row)}
+ checked={isItemSelected}
+ className={styles.checkbox}
+ />
+
+ )}
{columns.map(({ id, format, align, columnProps }) => {
if (!id) {
return null;
@@ -204,19 +238,22 @@ export default function TableV2(props) {
)}
{columns.some((column) => column.id && column.Footer) && (
- {columns.map(({ id, align, columnProps, Footer }) => {
+ {columns.map(({ id, align, columnProps, Footer }, index) => {
if (!id) {
return null;
}
return (
-
- {Footer}
-
+ <>
+ {!index && shouldShowCheckbox && }
+
+ {Footer}
+
+ >
);
})}
@@ -271,6 +308,11 @@ TableV2.propTypes = {
defaultOrderBy: PropTypes.string,
alternatingRowColor: PropTypes.bool,
showHeader: PropTypes.bool,
+ onCheck: PropTypes.func,
+ handleSelectAllClick: PropTypes.func,
+ selectedIds: PropTypes.array,
+ stickyHeader: PropTypes.bool,
+ maxHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
};
TableV2.defaultProps = {
diff --git a/packages/webapp/src/components/Table/styles.module.scss b/packages/webapp/src/components/Table/styles.module.scss
index 603058cb42..4635adc654 100644
--- a/packages/webapp/src/components/Table/styles.module.scss
+++ b/packages/webapp/src/components/Table/styles.module.scss
@@ -14,25 +14,57 @@
*/
.table {
- padding: 0.5px;
+ border-collapse: separate;
+
&.fixed {
table-layout: fixed;
}
}
.headerRow {
- border-top: 1px solid var(--Colors-Neutral-Neutral-50);
- border-bottom: 1px solid var(--Colors-Neutral-Neutral-50);
margin-bottom: 16px;
+
&.clickable {
cursor: pointer;
}
+
+ th {
+ border-top: 1px solid var(--Colors-Neutral-Neutral-50);
+ border-bottom: 1px solid var(--Colors-Neutral-Neutral-50);
+ }
}
.tableRow {
&.clickable {
cursor: pointer;
}
+
+ &[aria-checked="true"] {
+ border: solid 1px var(--Colors-Accent-Accent-yellow-400);
+ background-color: var(--Colors-Accent-Accent-yellow-50);
+
+ & > td {
+ border-top: solid 1px var(--Colors-Accent-Accent-yellow-400);
+ border-bottom: solid 1px var(--Colors-Accent-Accent-yellow-400);
+
+ &:first-child {
+ border-radius: 4px 0 0 4px;
+ border: solid 1px var(--Colors-Accent-Accent-yellow-400);
+ border-right: none;
+ }
+ &:last-child {
+ border-radius: 0 4px 4px 0;
+ border: solid 1px var(--Colors-Accent-Accent-yellow-400);
+ border-left: none;
+ }
+ };
+ }
+
+ // override mui's style
+ td:first-child {
+ border-top: solid 1px transparent;
+ border-bottom: solid 1px var(--grey200);
+ }
}
.itemRow {
@@ -74,12 +106,9 @@
.tableCell {
padding: 0 12px;
height: 56px;
+ border-top: solid 1px transparent;
border-bottom: solid 1px var(--grey200);
- &.tableHead {
- border-bottom: none;
- }
-
&.dense {
padding: 0 12px;
height: 40px;
@@ -172,3 +201,17 @@
line-height: 16px;
color: transparent;
}
+
+// checkbox
+.checkboxCell {
+ width: 32px;
+ text-align: center;
+
+ span {
+ padding: 0;
+
+ &:hover {
+ background-color: transparent;
+ }
+ }
+}
diff --git a/packages/webapp/src/components/Table/types.ts b/packages/webapp/src/components/Table/types.ts
index bd42a2a19a..809b15d1e2 100644
--- a/packages/webapp/src/components/Table/types.ts
+++ b/packages/webapp/src/components/Table/types.ts
@@ -75,4 +75,9 @@ export type TableV2Props = {
shouldFixTableLayout?: boolean;
defaultOrderBy?: string;
showHeader?: boolean;
+ onCheck?: (id: string | number) => void;
+ handleSelectAllClick?: () => void;
+ selectedIds?: (string | number)[];
+ stickyHeader?: boolean;
+ maxHeight?: number;
};
diff --git a/packages/webapp/src/stories/Table/TableV2.stories.jsx b/packages/webapp/src/stories/Table/TableV2.stories.jsx
index 008e995f7a..65f6db6bfa 100644
--- a/packages/webapp/src/stories/Table/TableV2.stories.jsx
+++ b/packages/webapp/src/stories/Table/TableV2.stories.jsx
@@ -12,10 +12,12 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details, see .
*/
-import React from 'react';
+
import { v2TableDecorator } from '../Pages/config/Decorators';
import Table from '../../components/Table';
import { TableKind } from '../../components/Table/types';
+import { useState } from 'react';
+import TextButton from '../../components/Form/Button/TextButton';
export default {
title: 'Components/Tables/V2',
@@ -74,16 +76,16 @@ const getCropSalesColumns = (mobileView = true) => {
const getCropSalesData = (length) => {
return [
- { crop: 'White corn, Corn', quantity: 2124, revenue: 8796.0 },
- { crop: 'Koto, Buckwheat', quantity: 724, revenue: 692.5 },
- { crop: 'Lutz green leaf, Beetroot', quantity: 58, revenue: 210.0 },
- { crop: 'Cox’s orange pippin, Apple', quantity: 48, revenue: 340.0 },
- { crop: 'Macoun, Apples', quantity: 124, revenue: 1234.0 },
- { crop: 'Butter Boy Hybrid, Butternut ', quantity: 24, revenue: 785.5 },
- { crop: 'King Edward, Potato', quantity: 58, revenue: 237.0 },
- { crop: 'Blanco Veneto, Celeriac', quantity: 56, revenue: 895.0 },
- { crop: 'Hollow Crown, Parsnips ', quantity: 23, revenue: 354.0 },
- { crop: 'Early White Hybrid, Cauliflower', quantity: 87, revenue: 789.5 },
+ { id: 1, crop: 'White corn, Corn', quantity: 2124, revenue: 8796.0 },
+ { id: 2, crop: 'Koto, Buckwheat', quantity: 724, revenue: 692.5 },
+ { id: 3, crop: 'Lutz green leaf, Beetroot', quantity: 58, revenue: 210.0 },
+ { id: 4, crop: 'Cox’s orange pippin, Apple', quantity: 48, revenue: 340.0 },
+ { id: 5, crop: 'Macoun, Apples', quantity: 124, revenue: 1234.0 },
+ { id: 6, crop: 'Butter Boy Hybrid, Butternut ', quantity: 24, revenue: 785.5 },
+ { id: 7, crop: 'King Edward, Potato', quantity: 58, revenue: 237.0 },
+ { id: 8, crop: 'Blanco Veneto, Celeriac', quantity: 56, revenue: 895.0 },
+ { id: 9, crop: 'Hollow Crown, Parsnips ', quantity: 23, revenue: 354.0 },
+ { id: 10, crop: 'Early White Hybrid, Cauliflower', quantity: 87, revenue: 789.5 },
].slice(0, length);
};
@@ -110,7 +112,7 @@ const FooterCell = () => (
);
-export const CropSalesMobileView = {
+export const WithCustomFooterCell = {
args: {
kind: TableKind.V2,
columns: getCropSalesColumns(),
@@ -121,7 +123,7 @@ export const CropSalesMobileView = {
},
};
-export const CropSalesDesktopView = {
+export const WithNormalFooter = {
args: {
kind: TableKind.V2,
columns: getCropSalesColumns(false),
@@ -142,57 +144,6 @@ export const AlternatingRowColor = {
},
};
-const getEmployeesLabourColumns = () => {
- return [
- {
- id: 'employee',
- label: 'Employee',
- Footer: 'DAILY TOTAL',
- },
- {
- id: 'time',
- label: 'Time',
- format: (d) => `${d.time} h`,
- align: 'right',
- Footer: 89 h,
- },
- {
- id: 'labourCost',
- label: 'Labour cost',
- format: (d) => {
- const sign = '$';
- return (
-
- {sign}
- {Math.abs(d.labourCost).toFixed(2)}
-
- );
- },
- align: 'right',
- Footer: $3732.50,
- },
- ];
-};
-
-const getEmployeesLabourData = (length) => {
- return [
- { employee: 'Sue D.', time: 1.25, labourCost: 0.0 },
- { employee: 'L.F. C.', time: 77.5, labourCost: 3692.5 },
- { employee: 'Joey.', time: 2.75, labourCost: 0.0 },
- { employee: 'Farmie.', time: 7.5, labourCost: 40.0 },
- ].slice(0, length);
-};
-
-export const EmployeesLabour = {
- args: {
- kind: TableKind.V2,
- columns: getEmployeesLabourColumns(),
- data: getEmployeesLabourData(10),
- minRows: 10,
- onClickMore: () => console.log('Go to labour page'),
- },
-};
-
const getTasksLabourColumns = () => {
return [
{
@@ -246,7 +197,7 @@ const getTasksLabourData = (length) => {
].slice(0, length);
};
-export const TasksLabour = {
+export const WithOnClickMore = {
args: {
kind: TableKind.V2,
columns: getTasksLabourColumns(),
@@ -256,7 +207,7 @@ export const TasksLabour = {
},
};
-export const TasksLabourWithPagination = {
+export const WithPagination = {
args: {
kind: TableKind.V2,
columns: getTasksLabourColumns(),
@@ -266,3 +217,60 @@ export const TasksLabourWithPagination = {
showPagination: true,
},
};
+
+export const withCheckboxes = {
+ args: {
+ kind: TableKind.V2,
+ columns: getCropSalesColumns(false),
+ minRows: 10,
+ shouldFixTableLayout: true,
+ handleSelectAllClick: () => console.log('all checked!'),
+ },
+ render: (props) => {
+ const [selectedIds, setSelectedIds] = useState([]);
+ const data = getCropSalesData(10);
+ const onCheck = (e, { id }) => {
+ setSelectedIds((prevSelectedTypeIds) => {
+ const isSelected = prevSelectedTypeIds.includes(id);
+ const newSelectedIds = isSelected
+ ? selectedIds.filter((selectedId) => id !== selectedId)
+ : [...prevSelectedTypeIds, id];
+
+ return newSelectedIds;
+ });
+ };
+ const handleSelectAllClick = (e) => {
+ if (e.target.checked) {
+ setSelectedIds(data.map(({ id }) => id));
+ } else {
+ setSelectedIds([]);
+ }
+ };
+
+ return (
+ {
+ console.log(`Row id ${rowData.id} is clicked!`);
+ }}
+ />
+ );
+ },
+};
+
+export const stickyHeader = {
+ args: {
+ kind: TableKind.V2,
+ columns: getCropSalesColumns(false),
+ data: getCropSalesData(10),
+ minRows: 10,
+ shouldFixTableLayout: true,
+ handleSelectAllClick: () => console.log('all checked!'),
+ stickyHeader: true,
+ maxHeight: 200,
+ },
+};