Skip to content

Commit

Permalink
itemsPerPage, eslint type fixes, constant height
Browse files Browse the repository at this point in the history
  • Loading branch information
jpfisher72 committed Oct 22, 2024
1 parent b85157f commit d40af7a
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 40 deletions.
6 changes: 6 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,10 @@ export default tseslint.config(
],
},
},
{
files: ["**/*.stories.tsx"],
rules: {
"react-hooks/rules-of-hooks": "off"
}
}
)
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@weng-lab/psychscreen-ui-components",
"description": "Typescript and Material UI based components used for psychSCREEN",
"author": "SCREEN Team @ UMass Chan Medical School",
"version": "2.0.5",
"version": "2.0.6",
"license": "MIT",
"type": "module",
"typings": "dist/index.d.ts",
Expand Down Expand Up @@ -75,5 +75,6 @@
"extends": [
"plugin:storybook/recommended"
]
}
},
"packageManager": "[email protected]+sha512.2dc70be5fce9f66756d25b00a888f3ca66f86b502b76750e72ba54cec89da767b938c54124595e26f868825688e0fe3552c26c76a330673343057acadd5cfcf2"
}
26 changes: 21 additions & 5 deletions src/components/DataTable/DataTable.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,10 @@ export const FunctionalComponentColumn: Story = {
args: {
rows: ROWS,
columns: FCCOLUMNS,
itemsPerPage: 4,
tableTitle: 'Table Title',
searchable: true,
showMoreColumns: true,
noOfDefaultColumns: 3,
noOfDefaultColumns: 5,
}
}

Expand Down Expand Up @@ -295,6 +294,23 @@ export const HeaderColored: Story = {
}
}

export const ItemsPerPage: Story = {
args: {
rows: ROWS,
columns: COLUMNS,
tableTitle: "Table Title"
},
render: (args) =>
<Stack spacing={2}>
<Typography>Default</Typography>
<DataTable {...args} />
<Typography>itemsPerPage = 5</Typography>
<DataTable {...args} itemsPerPage={5} />
<Typography>itemsPerPage = [3,5,10]</Typography>
<DataTable {...args} itemsPerPage={[3,5,10]}/>
</Stack>
}

export const ConstrainSize: Story = {
args: {
rows: ROWS,
Expand Down Expand Up @@ -432,7 +448,7 @@ export const LotsOfCols: Story = {
}
}

const headeRenderCOLUMNS = (func: any) => {
const headeRenderCOLUMNS = (setX: React.Dispatch<React.SetStateAction<boolean | null>>) => {
return [
{
header: 'Index',
Expand Down Expand Up @@ -488,7 +504,7 @@ const headeRenderCOLUMNS = (func: any) => {
switch (value) {
case 0:
setDistanceChecked(event.target.checked);
func(event.target.checked);
setX(event.target.checked);
break;
case 1:
setCTCF_ChIAPETChecked(event.target.checked);
Expand Down Expand Up @@ -570,7 +586,7 @@ export const HeaderRender: Story = {
searchable: true
},
render: (args) => {
const [x, setX] = React.useState(null);
const [x, setX] = React.useState<boolean | null>(null);
useEffect(() => console.log(x));
return (
<DataTable
Expand Down
75 changes: 47 additions & 28 deletions src/components/DataTable/datatable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const StyledInputBase = styled(InputBase)(({ theme }) => ({

//Styling for "Add Columns" Modal
const boxStyle = {
position: 'absolute' as 'absolute',
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
Expand All @@ -104,10 +104,12 @@ const boxStyle = {
const DataTable = <T,>(
props: DataTableProps<T>
) => {
// Sets default rows to display at 5 if unspecified
const itemsPerPage = props.itemsPerPage || 5;
const [page, setPage] = useState(props.page || 0);
const [rowsPerPage, setRowsPerPage] = useState(itemsPerPage);
const [rowsPerPage, setRowsPerPage] = useState<number>(() => {
if (Array.isArray(props.itemsPerPage)) { return props.itemsPerPage[0] }
else if (typeof props.itemsPerPage == "number") { return props.itemsPerPage }
else return 5
})

const handleChangePage = (page: number) => {
setPage(page);
Expand All @@ -120,16 +122,16 @@ const DataTable = <T,>(
setPage(0);
};

function handleEmptyTable(noColumns: number): React.JSX.Element[] {
let cells = [];
const handleSpawnEmptyCells = (noColumns: number): React.JSX.Element[] => {
const cells = [];
for (let i = 1; i < noColumns; i++) {
cells.push(<TableCell key={i}></TableCell>);
}
return cells;
}

function highlightCheck(row: T): boolean {
var found = false;
let found = false;
if (Array.isArray(props.highlighted)) {
props.highlighted.forEach((highlight) => {
if (JSON.stringify(row) === JSON.stringify(highlight)) {
Expand Down Expand Up @@ -169,7 +171,7 @@ const DataTable = <T,>(
});

const search = useCallback(
(row: any, value: string): boolean => {
(row: T, value: string): boolean => {
/* look for any matching searchable column */
for (const i in state.columns) {
/* get column; look for a user-defined search function first */
Expand Down Expand Up @@ -207,7 +209,7 @@ const DataTable = <T,>(
);

const displayRows = useCallback(
(sortedRows: any[], filterValue: string): any[] =>
(sortedRows: T[], filterValue: string): T[] =>
filterValue === ''
? [...sortedRows]
: sortedRows.filter((row) => search(row, filterValue)),
Expand All @@ -216,21 +218,24 @@ const DataTable = <T,>(

const displayedRows = useMemo(
() => sort(displayRows(props.rows, state.filter || props.search || '')),
[displayRows, sort, state.filter, props.rows, state.sort, props.search]
[displayRows, sort, state.filter, props.rows, props.search]
);

const rowsOnCurrentPage = useMemo(() => {
const newRowsOnPage = displayedRows.slice(page * rowsPerPage, (page + 1) * rowsPerPage);
props.onDisplayedRowsChange?.(page, newRowsOnPage)
return newRowsOnPage
}, [displayedRows, page, rowsPerPage])
}, [displayedRows, page, rowsPerPage, props.onDisplayedRowsChange])

// Avoid a layout jump when reaching the last page with empty rows.
const emptyRows = page > 0 ? Math.max(0, (1 + page) * rowsPerPage - props.rows.length) : 0;

const download = useCallback(() => {
const data =
state.columns.map((col) => col.header).join('\t') +
'\n' +
displayedRows
.map((row: any) =>
.map((row: T) =>
state.columns.map((col) => col.value(row)).join('\t')
)
.join('\n') +
Expand All @@ -245,7 +250,8 @@ const DataTable = <T,>(
a.click();
window.URL.revokeObjectURL(url);
a.remove();
}, [state.columns, displayedRows]);
}, [state.columns, displayedRows, props.downloadFileName]);


//Refs used in tracking overflow
const containerRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -279,14 +285,14 @@ const DataTable = <T,>(
monitorOverflow(containerRef, arrowRightRef, arrowLeftRef)
);

new ResizeObserver((entries) => {
for (const _ of entries) {
monitorOverflow(containerRef, arrowRightRef, arrowLeftRef);
}
new ResizeObserver(() => {
monitorOverflow(containerRef, arrowRightRef, arrowLeftRef);
}).observe(containerRef.current);
}
}, [containerRef, arrowLeftRef, arrowRightRef]);

console.log(document.getElementById('row0')?.offsetHeight)

return (
(<Paper
elevation={3}
Expand Down Expand Up @@ -389,8 +395,9 @@ const DataTable = <T,>(
sx={i !== state.columns.length - 1 ? { pr: 0 } : {}}
key={`${column.header}${i}`}
onClick={() => {
!column.unsortable &&
if (!column.unsortable) {
dispatch({ type: 'sortChanged', sortColumn: i });
}
setPage(0);
}}
>
Expand Down Expand Up @@ -425,16 +432,18 @@ const DataTable = <T,>(
<TableRow>
<TableCell>{props.emptyText || 'No data available.'}</TableCell>
{/* Render needed number of empty cells to fill row */}
{handleEmptyTable(props.columns.length)}
{handleSpawnEmptyCells(props.columns.length)}
</TableRow>
) : (
rowsOnCurrentPage
<>
{rowsOnCurrentPage
.map((row, i) => (
<TableRow
// Check that there's a row to select, it's the right one, and either none have been highlighted or it's the correct one
selected={props.highlighted ? highlightCheck(row) : false}
hover
key={'row' + i}
id={"row" + i}
onClick={() =>
props.onRowClick &&
props.onRowClick(row, i + page * rowsPerPage)
Expand Down Expand Up @@ -463,7 +472,7 @@ const DataTable = <T,>(
}
>
{column.FunctionalRender ? (
<column.FunctionalRender {...row} />
<column.FunctionalRender row={row} />
) : column.render ? (
column.render(row)
) : (
Expand All @@ -473,7 +482,17 @@ const DataTable = <T,>(
);
})}
</TableRow>
))
))}
{emptyRows > 0 && (
<TableRow
style={{
height: (document.getElementById('row0')?.offsetHeight ?? (props.dense ? 33 : 53)) * emptyRows,
}}
>
<TableCell colSpan={6} />
</TableRow>
)}
</>
)}
</TableBody>
</Table>
Expand Down Expand Up @@ -521,7 +540,7 @@ const DataTable = <T,>(
`Showing ${displayedRows.length} matching rows of ${props.rows.length} total.`}
</Typography>
<TablePagination
rowsPerPageOptions={[itemsPerPage, 10, 25, 100]}
rowsPerPageOptions={props.itemsPerPage ? Array.isArray(props.itemsPerPage) ? props.itemsPerPage : [props.itemsPerPage] : [5, 10, 25, 100]}
component="div"
count={displayedRows.length}
rowsPerPage={rowsPerPage}
Expand All @@ -534,11 +553,11 @@ const DataTable = <T,>(
sx={
props.dense
? {
'& .MuiTablePagination-toolbar': { pl: '6px' },
'& .css-h0cf5v-MuiInputBase-root-MuiTablePagination-select':
{ mr: '6px', ml: '0px' },
'& .MuiTablePagination-actions': { ml: '4px !important' },
}
'& .MuiTablePagination-toolbar': { pl: '6px' },
'& .css-h0cf5v-MuiInputBase-root-MuiTablePagination-select':
{ mr: '6px', ml: '0px' },
'& .MuiTablePagination-actions': { ml: '4px !important' },
}
: undefined
}
/>
Expand Down
17 changes: 14 additions & 3 deletions src/components/DataTable/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import React from "react"
export type DataTableColumn<T> = {
tooltip?: string
header: string
HeaderRender?: React.FC<any>
HeaderRender?: React.FC
value: (row: T) => string | number
search?: (row: T) => boolean
unsearchable?: boolean
render?: (row: T) => string | JSX.Element
FunctionalRender?: React.FC<any>
FunctionalRender?: (props: { row: T }) => JSX.Element;
sort?: (a: T, b: T) => number
unsortable?: boolean
}
Expand All @@ -20,7 +20,18 @@ type HEX = `#${string}` | `# ${string}`;
export type DataTableProps<T> = {
columns: DataTableColumn<T>[]
rows: T[]
itemsPerPage?: number

/**
* Sets the number of items on each page.
* If one number specified, the rows per page selection is hidden.
* Specify an array to provide user-selectable options
*
* @default
* [5, 10, 25, 100]
*
*/
itemsPerPage?: number | number[]

hidePageMenu?: boolean
tableTitle?: string
selectable?: boolean
Expand Down

0 comments on commit d40af7a

Please sign in to comment.