@material-ui/core#TablePagination TypeScript Examples
The following examples show how to use
@material-ui/core#TablePagination.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Pager.tsx From glific-frontend with GNU Affero General Public License v3.0 | 6 votes |
pagination = (
columnNames: Array<any>,
totalRows: number,
handleTableChange: Function,
tableVals: any
) => (
<TablePagination
className={styles.FooterRow}
colSpan={columnNames.length}
count={totalRows}
onPageChange={(e, newPage) => {
handleTableChange('pageNum', newPage);
}}
onRowsPerPageChange={(e) => {
handleTableChange('pageRows', parseInt(e.target.value, 10));
}}
page={tableVals.pageNum}
rowsPerPage={tableVals.pageRows}
rowsPerPageOptions={[50, 75, 100, 150, 200]}
/>
)
Example #2
Source File: ProjectPreview.tsx From backstage with Apache License 2.0 | 5 votes |
ProjectPreview = ({
bazaarProjects,
fetchBazaarProjects,
catalogEntities,
}: Props) => {
const classes = useStyles();
const [page, setPage] = useState(1);
const [rows, setRows] = useState(12);
const handlePageChange = (_: any, newPage: number) => {
setPage(newPage + 1);
};
const handleRowChange = (
event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
setRows(parseInt(event.target.value, 10));
setPage(1);
};
if (!bazaarProjects.length) {
return (
<div className={classes.empty}>Please add projects to the Bazaar.</div>
);
}
return (
<Content className={classes.content} noPadding>
<Grid wrap="wrap" container spacing={3}>
{bazaarProjects
.slice((page - 1) * rows, rows * page)
.map((bazaarProject: BazaarProject, i: number) => {
return (
<Grid key={i} item xs={2}>
<ProjectCard
project={bazaarProject}
key={i}
fetchBazaarProjects={fetchBazaarProjects}
catalogEntities={catalogEntities}
/>
</Grid>
);
})}
</Grid>
<TablePagination
className={classes.pagination}
rowsPerPageOptions={[12, 24, 48, 96]}
count={bazaarProjects?.length}
page={page - 1}
onPageChange={handlePageChange}
rowsPerPage={rows}
onRowsPerPageChange={handleRowChange}
backIconButtonProps={{ disabled: page === 1 }}
nextIconButtonProps={{
disabled: rows * page >= bazaarProjects.length,
}}
/>
</Content>
);
}
Example #3
Source File: QueryResults.tsx From SeeQR with MIT License | 5 votes |
QueryResults = ({ results }: QueryResultsProps) => {
if (!results || !results.length) return null;
const [page, setPage] = React.useState(0);
const rowsPerPage = 10;
// reset page to 1 if page is further than it could reasonable be. e.g. if
// user edits a query that had many pages of results while being in the last
// page and new query has a single page
if (page * rowsPerPage > results.length) setPage(0);
const columns = buildColumns(results[0]);
const handleChangePage = (event: unknown, newPage: number) => {
setPage(newPage);
};
// if there are performance issues, look into https://material-ui.com/components/tables/#virtualized-table
return (
<DarkPaperFull>
<TableContainer>
<Table>
<TableHead>
<TableRow>
{columns.map((column) => (
<TableCell key={column.name} align={column.align}>
<strong>{column.name}</strong>
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{results
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row) => (
<TableRow
hover
role="checkbox"
tabIndex={-1}
key={Object.values(row).join()}
>
{columns.map((column) => (
<StyledCell
align={column.align}
key={`${column.name}_${row[column.name]}`}
>
{(row[column.name] as any)?.toString()}
</StyledCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
<TablePagination
// if there is only one option the dropdown is not displayed
rowsPerPageOptions={[10]}
component="div"
count={results.length}
rowsPerPage={rowsPerPage}
page={page}
onChangePage={handleChangePage}
/>
</DarkPaperFull>
);
}
Example #4
Source File: CallStatusTable.tsx From twilio-voice-notification-app with Apache License 2.0 | 5 votes |
CallStatusTable: React.FC<CallStatusTableProps> = ({
recipients,
meta,
loading,
pageCount,
rowsPerPage,
setRowsPerPage,
currentPage,
setCurrentPage,
}) => {
const rowsPerPageOptions = useRowsPerPageOptions(meta?.total);
const onChangeRowsPerPage = useCallback(
(event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const rows = parseInt(event.target.value, 10);
setRowsPerPage(rows);
setCurrentPage(0);
},
[setRowsPerPage, setCurrentPage]
);
const onChangePagination = useCallback(
(event: unknown, value: number) => {
setCurrentPage(value);
},
[setCurrentPage]
);
return (
<>
{recipients && recipients.length > 0 && (
<>
<TableContainer
component={Paper}
style={{
marginBottom: loading ? '0' : '4px',
}}
>
<Table
aria-label="Notification recipients calls details and statuses"
size="small"
data-testid={RECIPIENTS_TABLE_TEST_ID}
>
<TableHead>
<TableRow>
<TableCell>Number</TableCell>
<TableCell>Status</TableCell>
</TableRow>
</TableHead>
<TableBody>
{recipients?.map((recipient) => (
<TableRow key={recipient.callSid}>
<TableCell>{recipient.to}</TableCell>
<TableCell>{recipient.status}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
{loading && <LinearProgress />}
</>
)}
{pageCount > 0 && meta.total && (
<TablePagination
data-testid={PAGINATOR_TEST_ID}
rowsPerPageOptions={rowsPerPageOptions}
component="div"
page={currentPage}
count={meta.total}
rowsPerPage={rowsPerPage}
onChangePage={onChangePagination}
onChangeRowsPerPage={onChangeRowsPerPage}
/>
)}
</>
);
}
Example #5
Source File: NumbersTable.tsx From twilio-voice-notification-app with Apache License 2.0 | 5 votes |
NumbersTable: React.FC<NumbersTableProps> = ({
headers,
rows,
data,
}) => {
const classes = useNumbersTableStyles();
const {
page,
rowsPerPage,
handleChangePage,
handleChangeRowsPerPage,
paginatedData,
rowsPerPageOptions,
} = usePagination(data);
const rowCells = useMemo(() => {
return paginatedData.map(
(rowData: { [key: string]: string }, idx: number) => (
<TableRow key={idx}>
{rows.map((cell: string, index: number) => (
<TableCell key={`${idx}-${index}`}>{rowData[cell]}</TableCell>
))}
</TableRow>
)
);
}, [paginatedData, rows]);
return (
<>
<TableContainer component={Box} className={classes.tableContainer}>
<Table stickyHeader size="small">
<TableHead>
<TableRow>
{headers.map((title: string, idx: number) => (
<StyledTableCell key={idx}>{title}</StyledTableCell>
))}
</TableRow>
</TableHead>
<TableBody>{rowCells}</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={rowsPerPageOptions}
component="div"
count={data.length}
rowsPerPage={rowsPerPage}
page={page}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
/>
</>
);
}
Example #6
Source File: DataTable.tsx From interface-v2 with GNU General Public License v3.0 | 4 votes |
DataTable: React.FC<DataTableProps<any>> = ({
headCells,
data,
renderRow,
toolbar,
sortUpIcon,
sortDownIcon,
caption,
defaultOrderBy = headCells[0],
defaultOrder = 'asc',
loading = false,
isSinglelineHeader = false,
size = 0,
rowPerPage = 10,
emptyMesage = 'No results.',
showEmptyRows = true,
showPagination,
}) => {
const classes = useStyles({ isSinglelineHeader });
const [order, setOrder] = useState<SortOrder>(defaultOrder);
const [orderBy, setOrderBy] = useState<HeadCell<any>>(defaultOrderBy);
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(rowPerPage);
const count = size || data.length;
const handleRequestSort = (
event: React.MouseEvent<unknown>,
property: HeadCell<any>,
) => {
const isAsc = orderBy.id === property.id && order === 'asc';
setOrder(isAsc && !property.sortDisabled ? 'desc' : 'asc');
setOrderBy(property);
};
const handleChangePage = (event: unknown, newPage: number) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (
event: React.ChangeEvent<HTMLInputElement>,
) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
};
const emptyRows =
rowsPerPage - Math.min(rowsPerPage, data.length - page * rowsPerPage);
return (
<Box>
{toolbar}
<TableContainer>
<Table
className={classes.table}
aria-labelledby='tableTitle'
size='medium'
aria-label='enhanced table'
>
<TableHead>
<TableRow>
{headCells.map((headCell, index) => (
<TableCell
className={headCell.buttonCell ? 'buttonCell' : ''}
key={`${headCell.id}_${index}`}
align={headCell.align}
padding='normal'
sortDirection={orderBy.id === headCell.id ? order : false}
>
{headCell.element}
{sortUpIcon && sortDownIcon ? (
<Grid
container
alignItems='center'
className={classes.label}
onClick={(event: any) =>
handleRequestSort(event, headCell)
}
>
<Box
className={cx(
classes.headCellLabel,
orderBy.id === headCell.id &&
classes.sortRequestedHeadLabel,
)}
>
{headCell.label}
</Box>
{!headCell.sortDisabled && (
<Box
className={cx(
classes.sortIcon,
orderBy.id === headCell.id &&
classes.sortRequestedIcon,
)}
>
{order === 'asc' && orderBy.id === headCell.id
? sortUpIcon
: sortDownIcon}
</Box>
)}
</Grid>
) : (
<TableSortLabel
className={classes.label}
active={orderBy.id === headCell.id}
direction={orderBy.id === headCell.id ? order : 'asc'}
onClick={(event: any) =>
handleRequestSort(event, headCell)
}
>
{headCell.label}
{orderBy.id === headCell.id ? (
<span className={classes.visuallyHidden}>
{order === 'desc'
? 'sorted descending'
: 'sorted ascending'}
</span>
) : null}
</TableSortLabel>
)}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{loading && (
<TableRow style={{ height: 53 * emptyRows }}>
<TableCell colSpan={headCells.length}>
<Grid container justifyContent='center' alignItems='center'>
<CircularProgress />
</Grid>
</TableCell>
</TableRow>
)}
{stableSort(data, getComparator(order, orderBy))
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((item, index) => renderRow(item, index, page, rowsPerPage))}
{!loading && data.length < 1 && (
<TableRow style={{ height: 53 }}>
<TableCell colSpan={headCells.length} align='center'>
{emptyMesage}
</TableCell>
</TableRow>
)}
{!loading && emptyRows > 0 && showEmptyRows && (
<TableRow
style={{
height: 53 * (data.length < 1 ? emptyRows - 1 : emptyRows),
}}
>
<TableCell colSpan={headCells.length} />
</TableRow>
)}
</TableBody>
{/* Todo: show captions */}
{caption === false && (
<caption style={{ marginTop: 24 }}>{caption}</caption>
)}
</Table>
</TableContainer>
{showPagination && (
<TablePagination
rowsPerPageOptions={[5, 10, 25, 50]}
className={classes.tablePagination}
component='div'
count={count}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
)}
</Box>
);
}
Example #7
Source File: index.tsx From prism-frontend with MIT License | 4 votes |
function AnalysisTable({ classes, tableData, columns }: AnalysisTableProps) {
// only display local names if local language is selected, otherwise display english name
const { t } = useSafeTranslation();
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const [sortColumn, setSortColumn] = useState<Column['id']>();
const [isAscending, setIsAscending] = useState(true);
const dispatch = useDispatch();
const handleChangePage = (event: unknown, newPage: number) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (
event: React.ChangeEvent<HTMLInputElement>,
) => {
setRowsPerPage(+event.target.value);
setPage(0);
};
const handleChangeOrderBy = (newSortColumn: Column['id']) => {
const newIsAsc = !(sortColumn === newSortColumn && isAscending);
setPage(0);
setSortColumn(newSortColumn);
setIsAscending(newIsAsc);
};
return (
<div>
<TableContainer className={classes.tableContainer}>
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
{columns.map(column => (
<TableCell key={column.id} className={classes.tableHead}>
<TableSortLabel
active={sortColumn === column.id}
direction={
sortColumn === column.id && !isAscending ? 'desc' : 'asc'
}
onClick={() => handleChangeOrderBy(column.id)}
>
{t(column.label)}
</TableSortLabel>
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{orderBy(tableData, sortColumn, isAscending ? 'asc' : 'desc')
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map(row => {
return (
<TableRow
hover
role="checkbox"
tabIndex={-1}
key={row.key}
onClick={() => {
// TODO if we decide to keep, add popup data?
if (row.coordinates) {
dispatch(
showPopup({
coordinates: row.coordinates,
locationName: row.name,
locationLocalName: row.localName,
}),
);
}
}}
style={{ cursor: row.coordinates ? 'pointer' : 'none' }}
>
{columns.map(column => {
const value = row[column.id];
return (
<TableCell key={column.id}>
{column.format && typeof value === 'number'
? column.format(value)
: value}
</TableCell>
);
})}
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={[10, 25, 100]}
component="div"
count={tableData.length}
rowsPerPage={rowsPerPage}
page={page}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
labelRowsPerPage={t('Rows Per Page')}
// Temporary manual translation before we upgrade to MUI 5.
labelDisplayedRows={({ from, to, count }) => {
return `${from}–${to} ${t('of')} ${
count !== -1 ? count : `${t('more than')} ${to}`
}`;
}}
/>
</div>
);
}
Example #8
Source File: Table.tsx From frontegg-react with MIT License | 4 votes |
Table: FC<TableProps> = <T extends object>(props: TableProps<T>) => { const classes = useStyles(); const tableRef = useRef<HTMLTableElement>(null); const firstRender = useRef<boolean>(true); const columns = useMemo(() => { const columns = props.columns.map( ({ sortable, Filter, Header, ...rest }) => ({ ...rest, disableSortBy: !sortable, disableFilters: !Filter, Filter, Header: Header ?? <div style={{ minWidth: rest.minWidth, maxWidth: rest.maxWidth }} />, } as FeTableColumnOptions<T>) ); if (props.expandable) { columns.unshift({ id: 'fe-expander', minWidth: 60, maxWidth: '60px' as any, Header: <div style={{ minWidth: '1.5rem', maxWidth: '1.5rem' }} />, Cell: (cell: Cell<T>) => { const row = cell.row as Row<T> & UseExpandedRowProps<T>; return ( <IconButton className={classes.expandIcon} {...row.getToggleRowExpandedProps()}> {row.isExpanded ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />} </IconButton> ); }, }); } if (props.selection) { columns.unshift({ id: 'fe-selection', minWidth: 60, maxWidth: '60px' as any, Cell: (cell: Cell<T>) => { const row = cell.row as Row<T> & UseRowSelectRowProps<T>; return ( <Checkbox className={classes.checkBox} {...row.getToggleRowSelectedProps()} checked={row.isSelected} onChange={(e) => onRowSelected(row.original, e.target.checked)} /> ); }, }); } return columns as Column<T>[]; }, [props.columns, props.expandable]); const tableHooks: PluginHook<T>[] = [useFilters, useSortBy]; if (props.expandable) { tableHooks.push(useExpanded); } if (props.pagination) { tableHooks.push(usePagination); } if (props.selection) { tableHooks.push(useRowSelect); } tableHooks.push(useFlexLayout); const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow, state, // The page controls ;) page, // canPreviousPage, // canNextPage, pageOptions, pageCount, gotoPage, nextPage, previousPage, // setPageSize, // select props toggleAllRowsSelected, isAllRowsSelected, selectedFlatRows, toggleRowSelected, } = useTable( { columns, data: props.data, getRowId: (row: any) => row[props.rowKey], manualSortBy: !!props.onSortChange, manualFilters: !!props.onFilterChange, manualPagination: !!props.onPageChange, manualRowSelectedKey: props.rowKey, pageCount: !!props.onPageChange ? props.pageCount : undefined, autoResetPage: !props.onPageChange, useControlledState: (state1: any) => { return { ...state1, sortBy: props.sortBy ?? state1.sortBy, filters: props.filters ?? state1.filters, selectedRowIds: props.selectedRowIds ?? state1.selectedRowIds, } as TableState<T> & UseFiltersState<T> & UseSortByState<T> & UseRowSelectState<T>; }, expandSubRows: false, initialState: { pageIndex: 0, pageSize: props.pageSize ?? 0, selectedRowIds: props.selectedRowIds || {}, }, } as UseTableOptions<T> & UseFiltersOptions<T> & UseSortByOptions<T> & UseExpandedOptions<T> & UseRowSelectOptions<T> & UsePaginationOptions<T>, ...tableHooks ) as TableInstance<T> & UseTableInstanceProps<T> & UsePaginationInstanceProps<T> & UseRowSelectInstanceProps<T>; if (props.expandable && !props.renderExpandedComponent) { throw Error('FeTable: you must provide renderExpandedComponent property if the table is expandable'); } if (props.hasOwnProperty('sortBy') && !props.onSortChange) { throw Error('FeTable: you must provide onSortChange property if sortBy is controlled'); } if (props.hasOwnProperty('filters') && !props.onFilterChange) { throw Error('FeTable: you must provide onFilterChange property if filters is controlled'); } if (props.hasOwnProperty('pagination') && !props.pageSize) { throw Error('FeTable: you must provide pageSize property if pagination enabled'); } if (props.hasOwnProperty('onPageChange') && !props.pageCount) { throw Error('FeTable: you must provide pageCount property if onPageChange is controlled'); } const tableState = state as UseSortByState<T> & UseFiltersState<T> & UsePaginationState<T> & UseRowSelectState<T>; const onSortChange = useCallback( (column: FeTableColumnProps<T>) => { if (props.hasOwnProperty('sortBy')) { const sortBy = props.isMultiSort ? tableState.sortBy.filter(({ id }) => id !== column.id) : []; if (!column.isSorted) { sortBy.push({ id: column.id, desc: false }); } else if (!column.isSortedDesc) { sortBy.push({ id: column.id, desc: true }); } props.onSortChange?.(sortBy); } else { if (column.isSorted && column.isSortedDesc) { column.clearSortBy(); } else { column.toggleSortBy(column.isSorted, props.isMultiSort ?? false); } } }, [props.onSortChange] ); const onFilterChange = useCallback( (column: FeTableColumnProps<T>, filterValue?: any) => { if (props.hasOwnProperty('filters')) { const filters = tableState.filters.filter(({ id }) => id !== column.id); if (filterValue != null) { filters.push({ id: column.id, value: filterValue }); } props.onFilterChange?.(filters); } else { column.setFilter(filterValue); } }, [props.onFilterChange, tableState] ); const onToggleAllRowsSelected = useCallback( (value: boolean) => { if (props.hasOwnProperty('selectedRowIds')) { const selectedIds = props.data.reduce((p, n: any) => ({ ...p, [n[props.rowKey]]: true }), {}); props.onRowSelected?.(value ? selectedIds : {}); } else { toggleAllRowsSelected(value); } }, [props.onRowSelected] ); const onRowSelected = useCallback( (row: any, value: boolean) => { const id = row[props.rowKey]; if (props.hasOwnProperty('selectedRowIds')) { const newSelectedRows: any = { ...props.selectedRowIds }; if (value) { newSelectedRows[id] = true; } else { delete newSelectedRows[id]; } props.onRowSelected?.(newSelectedRows); } else { toggleRowSelected(id, value); } }, [props.onRowSelected] ); const handleOnPageChange = useCallback(() => { if (pagination === 'pages') { tableRef.current?.scroll?.({ top: 0, left: 0, behavior: 'smooth' }); } props.onPageChange?.(tableState.pageSize, tableState.pageIndex); }, [tableState.pageIndex]); useEffect(() => { !props.hasOwnProperty('sortBy') && props.onSortChange?.(tableState.sortBy); }, [props.sortBy, tableState.sortBy]); useEffect(() => { !props.hasOwnProperty('filters') && props.onFilterChange?.(tableState.filters); }, [props.filters, tableState.filters]); useEffect(() => { firstRender.current ? (firstRender.current = false) : handleOnPageChange(); }, [tableState.pageIndex]); useEffect(() => { !props.hasOwnProperty('selectedRowIds') && props.onRowSelected?.(tableState.selectedRowIds as any); }, [tableState.selectedRowIds]); const onPageChangeHandler = (page: number) => { if (page > tableState.pageIndex) { nextPage(); } else { previousPage(); } }; const { className, loading, pagination, totalData, pageSize } = props; return ( <Paper ref={tableRef} className={classes.paper}> <MaUTable className={classNames(classes.table, className)} {...getTableProps()}> <TableHead headerGroups={headerGroups} onSortChange={onSortChange} onFilterChange={onFilterChange} toggleAllRowsSelected={onToggleAllRowsSelected} isAllRowsSelected={isAllRowsSelected} selectedFlatRows={selectedFlatRows} /> <TableBody pageSize={pageSize} pagination={pagination} getTableBodyProps={getTableBodyProps} prepareRow={prepareRow} loading={loading} rows={(pagination ? page : rows) as (Row<T> & UseExpandedRowProps<T>)[]} renderExpandedComponent={props.renderExpandedComponent} onInfiniteScroll={handleOnPageChange} /> </MaUTable> {loading && pagination === 'pages' && rows.length > 0 && <Loader center size={24} />} {pagination === 'pages' && ( <TablePagination className={classes.footer} rowsPerPageOptions={[]} component='div' count={totalData || rows.length} rowsPerPage={tableState.pageSize} page={tableState.pageIndex} onChangePage={(e, page) => onPageChangeHandler(page)} ActionsComponent={(props) => ( <TablePaginationActions {...props} gotoPage={gotoPage} pageOptions={pageOptions} /> )} /> )} </Paper> ); }
Example #9
Source File: List.tsx From glific-frontend with GNU Affero General Public License v3.0 | 4 votes |
List: React.SFC<ListProps> = ({
columnNames = [],
countQuery,
listItem,
listIcon,
filterItemsQuery,
deleteItemQuery,
listItemName,
dialogMessage = '',
secondaryButton,
pageLink,
columns,
columnStyles,
title,
dialogTitle,
filterList,
listOrder = 'asc',
removeSortBy = null,
button = {
show: true,
label: 'Add New',
},
showCheckbox,
deleteModifier = { icon: 'normal', variables: null, label: 'Delete' },
editSupport = true,
searchParameter = ['label'],
filters = null,
displayListType = 'list',
cardLink = null,
additionalAction = [],
backLinkButton,
restrictedAction,
collapseOpen = false,
collapseRow = undefined,
defaultSortBy,
noItemText = null,
isDetailsPage = false,
customStyles,
}: ListProps) => {
const { t } = useTranslation();
// DialogBox states
const [deleteItemID, setDeleteItemID] = useState<number | null>(null);
const [deleteItemName, setDeleteItemName] = useState<string>('');
const [newItem, setNewItem] = useState(false);
const [searchVal, setSearchVal] = useState('');
// check if the user has access to manage collections
const userRolePermissions = getUserRolePermissions();
const capitalListItemName = listItemName
? listItemName[0].toUpperCase() + listItemName.slice(1)
: '';
let defaultColumnSort = columnNames[0];
// check if there is a default column set for sorting
if (defaultSortBy) {
defaultColumnSort = defaultSortBy;
}
// get the last sort column value from local storage if exist else set the default column
const getSortColumn = (listItemNameValue: string, columnName: string) => {
// set the column name
let columnnNameValue;
if (columnName) {
columnnNameValue = columnName;
}
// check if we have sorting stored in local storage
const sortValue = getLastListSessionValues(listItemNameValue, false);
// update column name from the local storage
if (sortValue) {
columnnNameValue = sortValue;
}
return setColumnToBackendTerms(listItemName, columnnNameValue);
};
// get the last sort direction value from local storage if exist else set the default order
const getSortDirection = (listItemNameValue: string) => {
let sortDirection: any = listOrder;
// check if we have sorting stored in local storage
const sortValue = getLastListSessionValues(listItemNameValue, true);
if (sortValue) {
sortDirection = sortValue;
}
return sortDirection;
};
// Table attributes
const [tableVals, setTableVals] = useState<TableVals>({
pageNum: 0,
pageRows: 50,
sortCol: getSortColumn(listItemName, defaultColumnSort),
sortDirection: getSortDirection(listItemName),
});
let userRole: any = getUserRole();
const handleTableChange = (attribute: string, newVal: any) => {
let updatedList;
let attributeValue = newVal;
if (attribute === 'sortCol') {
attributeValue = setColumnToBackendTerms(listItemName, newVal);
updatedList = getUpdatedList(listItemName, newVal, false);
} else {
updatedList = getUpdatedList(listItemName, newVal, true);
}
// set the sort criteria in local storage
setListSession(JSON.stringify(updatedList));
setTableVals({
...tableVals,
[attribute]: attributeValue,
});
};
let filter: any = {};
if (searchVal !== '') {
searchParameter.forEach((parameter: string) => {
filter[parameter] = searchVal;
});
}
filter = { ...filter, ...filters };
const filterPayload = useCallback(() => {
let order = 'ASC';
if (tableVals.sortDirection) {
order = tableVals.sortDirection.toUpperCase();
}
return {
filter,
opts: {
limit: tableVals.pageRows,
offset: tableVals.pageNum * tableVals.pageRows,
order,
orderWith: tableVals.sortCol,
},
};
}, [searchVal, tableVals, filters]);
// Get the total number of items here
const {
loading: l,
error: e,
data: countData,
refetch: refetchCount,
} = useQuery(countQuery, {
variables: { filter },
});
// Get item data here
const [fetchQuery, { loading, error, data, refetch: refetchValues }] = useLazyQuery(
filterItemsQuery,
{
variables: filterPayload(),
fetchPolicy: 'cache-and-network',
}
);
// Get item data here
const [fetchUserCollections, { loading: loadingCollections, data: userCollections }] =
useLazyQuery(GET_CURRENT_USER);
const checkUserRole = () => {
userRole = getUserRole();
};
useEffect(() => {
refetchCount();
}, [filterPayload, searchVal, filters]);
useEffect(() => {
if (userRole.length === 0) {
checkUserRole();
} else {
if (!userRolePermissions.manageCollections && listItem === 'collections') {
// if user role staff then display collections related to login user
fetchUserCollections();
}
fetchQuery();
}
}, [userRole]);
let deleteItem: any;
// Make a new count request for a new count of the # of rows from this query in the back-end.
if (deleteItemQuery) {
[deleteItem] = useMutation(deleteItemQuery, {
onCompleted: () => {
checkUserRole();
refetchCount();
if (refetchValues) {
refetchValues(filterPayload());
}
},
});
}
const showDialogHandler = (id: any, label: string) => {
setDeleteItemName(label);
setDeleteItemID(id);
};
const closeDialogBox = () => {
setDeleteItemID(null);
};
const deleteHandler = (id: number) => {
const variables = deleteModifier.variables ? deleteModifier.variables(id) : { id };
deleteItem({ variables });
setNotification(`${capitalListItemName} deleted successfully`);
};
const handleDeleteItem = () => {
if (deleteItemID !== null) {
deleteHandler(deleteItemID);
}
setDeleteItemID(null);
};
const useDelete = (message: string | any) => {
let component = {};
const props = { disableOk: false, handleOk: handleDeleteItem };
if (typeof message === 'string') {
component = message;
} else {
/**
* Custom component to render
* message should contain 3 params
* 1. component: Component to render
* 2. isConfirm: To check true or false value
* 3. handleOkCallback: Callback action to delete item
*/
const {
component: componentToRender,
isConfirmed,
handleOkCallback,
} = message(deleteItemID, deleteItemName);
component = componentToRender;
props.disableOk = !isConfirmed;
props.handleOk = () => handleOkCallback({ refetch: fetchQuery, setDeleteItemID });
}
return {
component,
props,
};
};
let dialogBox;
if (deleteItemID) {
const { component, props } = useDelete(dialogMessage);
dialogBox = (
<DialogBox
title={
dialogTitle || `Are you sure you want to delete the ${listItemName} "${deleteItemName}"?`
}
handleCancel={closeDialogBox}
colorOk="secondary"
alignButtons="center"
{...props}
>
<div className={styles.DialogText}>
<div>{component}</div>
</div>
</DialogBox>
);
}
if (newItem) {
return <Redirect to={`/${pageLink}/add`} />;
}
if (loading || l || loadingCollections) return <Loading />;
if (error || e) {
if (error) {
setErrorMessage(error);
} else if (e) {
setErrorMessage(e);
}
return null;
}
// Reformat all items to be entered in table
function getIcons(
// id: number | undefined,
item: any,
label: string,
isReserved: boolean | null,
listItems: any,
allowedAction: any | null
) {
// there might be a case when we might want to allow certain actions for reserved items
// currently we don't allow edit or delete for reserved items. hence return early
const { id } = item;
if (isReserved) {
return null;
}
let editButton = null;
if (editSupport) {
editButton = allowedAction.edit && (
<Link to={`/${pageLink}/${id}/edit`}>
<IconButton aria-label={t('Edit')} color="default" data-testid="EditIcon">
<Tooltip title={t('Edit')} placement="top">
<EditIcon />
</Tooltip>
</IconButton>
</Link>
);
}
const deleteButton = (Id: any, text: string) =>
allowedAction.delete ? (
<IconButton
aria-label={t('Delete')}
color="default"
data-testid="DeleteIcon"
onClick={() => showDialogHandler(Id, text)}
>
<Tooltip title={`${deleteModifier.label}`} placement="top">
{deleteModifier.icon === 'cross' ? <CrossIcon /> : <DeleteIcon />}
</Tooltip>
</IconButton>
) : null;
if (id) {
return (
<div className={styles.Icons}>
{additionalAction.map((action: any, index: number) => {
if (allowedAction.restricted) {
return null;
}
// check if we are dealing with nested element
let additionalActionParameter: any;
const params: any = additionalAction[index].parameter.split('.');
if (params.length > 1) {
additionalActionParameter = listItems[params[0]][params[1]];
} else {
additionalActionParameter = listItems[params[0]];
}
const key = index;
if (action.link) {
return (
<Link to={`${action.link}/${additionalActionParameter}`} key={key}>
<IconButton
color="default"
className={styles.additonalButton}
data-testid="additionalButton"
>
<Tooltip title={`${action.label}`} placement="top">
{action.icon}
</Tooltip>
</IconButton>
</Link>
);
}
if (action.dialog) {
return (
<IconButton
color="default"
data-testid="additionalButton"
className={styles.additonalButton}
id="additionalButton-icon"
onClick={() => action.dialog(additionalActionParameter, item)}
key={key}
>
<Tooltip title={`${action.label}`} placement="top" key={key}>
{action.icon}
</Tooltip>
</IconButton>
);
}
if (action.button) {
return action.button(listItems, action, key, fetchQuery);
}
return null;
})}
{/* do not display edit & delete for staff role in collection */}
{userRolePermissions.manageCollections || listItems !== 'collections' ? (
<>
{editButton}
{deleteButton(id, label)}
</>
) : null}
</div>
);
}
return null;
}
function formatList(listItems: Array<any>) {
return listItems.map(({ ...listItemObj }) => {
const label = listItemObj.label ? listItemObj.label : listItemObj.name;
const isReserved = listItemObj.isReserved ? listItemObj.isReserved : null;
// display only actions allowed to the user
const allowedAction = restrictedAction
? restrictedAction(listItemObj)
: { chat: true, edit: true, delete: true };
return {
...columns(listItemObj),
operations: getIcons(listItemObj, label, isReserved, listItemObj, allowedAction),
recordId: listItemObj.id,
};
});
}
const resetTableVals = () => {
setTableVals({
pageNum: 0,
pageRows: 50,
sortCol: getSortColumn(listItemName, defaultColumnSort),
sortDirection: getSortDirection(listItemName),
});
};
const handleSearch = (searchError: any) => {
searchError.preventDefault();
const searchValInput = searchError.target.querySelector('input').value.trim();
setSearchVal(searchValInput);
resetTableVals();
};
// Get item data and total number of items.
let itemList: any = [];
if (data) {
itemList = formatList(data[listItem]);
}
if (userCollections) {
if (listItem === 'collections') {
itemList = formatList(userCollections.currentUser.user.groups);
}
}
let itemCount: number = tableVals.pageRows;
if (countData) {
itemCount = countData[`count${listItem[0].toUpperCase()}${listItem.slice(1)}`];
}
let displayList;
if (displayListType === 'list') {
displayList = (
<Pager
columnStyles={columnStyles}
removeSortBy={removeSortBy !== null ? removeSortBy : []}
columnNames={columnNames}
data={itemList}
listItemName={listItemName}
totalRows={itemCount}
handleTableChange={handleTableChange}
tableVals={tableVals}
showCheckbox={showCheckbox}
collapseOpen={collapseOpen}
collapseRow={collapseRow}
/>
);
} else if (displayListType === 'card') {
/* istanbul ignore next */
displayList = (
<>
<ListCard data={itemList} link={cardLink} />
<table>
<TableFooter className={styles.TableFooter} data-testid="tableFooter">
<TableRow>
<TablePagination
className={styles.FooterRow}
colSpan={columnNames.length}
count={itemCount}
onPageChange={(event, newPage) => {
handleTableChange('pageNum', newPage);
}}
onRowsPerPageChange={(event) => {
handleTableChange('pageRows', parseInt(event.target.value, 10));
}}
page={tableVals.pageNum}
rowsPerPage={tableVals.pageRows}
rowsPerPageOptions={[50, 75, 100, 150, 200]}
/>
</TableRow>
</TableFooter>
</table>
</>
);
}
const backLink = backLinkButton ? (
<div className={styles.BackLink}>
<Link to={backLinkButton.link}>
<BackIcon />
{backLinkButton.text}
</Link>
</div>
) : null;
let buttonDisplay;
if (button.show) {
let buttonContent;
if (button.action) {
buttonContent = (
<Button
color="primary"
variant="contained"
onClick={() => button.action && button.action()}
>
{button.label}
</Button>
);
} else if (!button.link) {
buttonContent = (
<Button
color="primary"
variant="contained"
onClick={() => setNewItem(true)}
data-testid="newItemButton"
>
{button.label}
</Button>
);
} else {
buttonContent = (
<Link to={button.link}>
<Button color="primary" variant="contained" data-testid="newItemLink">
{button.label}
</Button>
</Link>
);
}
buttonDisplay = <div className={styles.AddButton}>{buttonContent}</div>;
}
const noItemsText = (
<div className={styles.NoResults}>
{searchVal ? (
<div>{t('Sorry, no results found! Please try a different search.')}</div>
) : (
<div>
There are no {noItemText || listItemName}s right now.{' '}
{button.show && t('Please create one.')}
</div>
)}
</div>
);
let headerSize = styles.Header;
if (isDetailsPage) {
headerSize = styles.DetailsPageHeader;
}
return (
<>
<div className={headerSize} data-testid="listHeader">
<Typography variant="h5" className={styles.Title}>
<IconButton disabled className={styles.Icon}>
{listIcon}
</IconButton>
{title}
</Typography>
{filterList}
<div className={styles.Buttons}>
<SearchBar
handleSubmit={handleSearch}
onReset={() => {
setSearchVal('');
resetTableVals();
}}
searchVal={searchVal}
handleChange={(err: any) => {
// reset value only if empty
if (!err.target.value) setSearchVal('');
}}
searchMode
/>
</div>
<div>
{dialogBox}
<div className={styles.ButtonGroup}>
{buttonDisplay}
{secondaryButton}
</div>
</div>
</div>
<div className={`${styles.Body} ${customStyles}`}>
{backLink}
{/* Rendering list of items */}
{itemList.length > 0 ? displayList : noItemsText}
</div>
</>
);
}