Skip to content

Commit

Permalink
Custom hook pagination (#3)
Browse files Browse the repository at this point in the history
* Add new Pagination component using pagination custom hook

* Change up data table pagination to only use the datastore functions and not react table

* Use offset / limit instead of current page to keep caching of state
  • Loading branch information
dgading authored Jan 20, 2021
1 parent 65ec73a commit ad3f8da
Show file tree
Hide file tree
Showing 8 changed files with 243 additions and 106 deletions.
188 changes: 88 additions & 100 deletions src/components/DataTable/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { ResourceDispatch, transformTableFilterToQueryCondition, transformTableFilterToSQLCondition, transformTableSortToQuerySort } from '@civicactions/data-catalog-services';
import { useTable, usePagination, useSortBy, useFilters } from 'react-table';
import { TextField } from '@cmsgov/design-system';
import DataTablePagination from '../DataTablePagination';
import Pagination from '../Pagination';

const DataTable = ({ canFilter, tablePadding }) => {
const {
Expand All @@ -13,9 +13,9 @@ const DataTable = ({ canFilter, tablePadding }) => {
columns,
totalRows,
limit,
currentPage,
offset
} = useContext(ResourceDispatch);
const { setOffset, setCurrentPage, setConditions, setSort } = actions;
const { setConditions, setSort, setOffset } = actions;

if(columns.length === 0) {
return null;
Expand Down Expand Up @@ -69,20 +69,15 @@ const DataTable = ({ canFilter, tablePadding }) => {
getTableProps,
getTableBodyProps,
prepareRow,
canPreviousPage,
canNextPage,
nextPage,
previousPage,
rows,
headerGroups,
state: { pageIndex, filters, sortBy },
setPageSize
state: { filters, sortBy },
} = useTable(
{
columns,
data,
filterTypes,
initialState: { pageIndex: currentPage },
initialState: { },
manualPagination: true,
manualFilters: true,
manualSortBy: true,
Expand All @@ -99,15 +94,6 @@ const DataTable = ({ canFilter, tablePadding }) => {
setSort(transformTableSortToQuerySort(sortBy));
}, [sortBy])

useEffect(() => {
setPageSize(Number(limit))
}, [limit]);

useEffect(() => {
setCurrentPage(pageIndex);
setOffset((Number(pageIndex)) * limit)
}, [pageIndex])

useEffect(() => {
let timerFunc = setTimeout(() => {
setConditions(transformTableFilterToQueryCondition(filters))
Expand All @@ -116,89 +102,91 @@ const DataTable = ({ canFilter, tablePadding }) => {
}, [filters])

return(
<div className="dc-c-datatable ds-u-border--dark ds-u-border-x--1">
<table
{...getTableProps()}
className="ds-c-table ds-c-table--striped ds-c-table--borderless"
>
<thead className="ds-u-border--dark ds-u-border-y--2">
{headerGroups.map((headerGroup) => (
<>
<tr
{...headerGroup.getHeaderGroupProps()}
className=""
>
{headerGroup.headers.map((column, index) => (
<th
className={`ds-u-border--dark ds-u-fill--white ${index + 1 === columns.length ? '' : 'ds-u-border-right--1'}`}
scope="col"
{...column.getHeaderProps(column.getSortByToggleProps({
title: column.canSort ? `Sort by ${column.Header}` : undefined,
}))}
>
{column.render('Header')}
<span className={`dc-c-sort ${column.isSorted ? column.isSortedDesc ? 'dc-c-sort--desc' : 'dc-c-sort--asc' : 'dc-c-sort--default'}`} />
</th>
))}
</tr>
{canFilter &&
(
<>
<tr>
<th
colSpan={columns.length}
className={`ds-u-padding-top--1 ds-u-padding-bottom--0 ds-u-border-bottom--0`}
>
Filter Columns
</th>
</tr>
<tr>
{headerGroup.headers.map((column) => {
return (
<th {...column.getHeaderProps()}>
{column.canFilter ? column.render('Filter') : null}

</th>
)
<div>
<div className="dc-c-datatable ds-u-border--dark ds-u-border-x--1">
<table
{...getTableProps()}
className="ds-c-table ds-c-table--striped ds-c-table--borderless"
>
<thead className="ds-u-border--dark ds-u-border-y--2">
{headerGroups.map((headerGroup) => (
<>
<tr
{...headerGroup.getHeaderGroupProps()}
className=""
>
{headerGroup.headers.map((column, index) => (
<th
className={`ds-u-border--dark ds-u-fill--white ${index + 1 === columns.length ? '' : 'ds-u-border-right--1'}`}
scope="col"
{...column.getHeaderProps(column.getSortByToggleProps({
title: column.canSort ? `Sort by ${column.Header}` : undefined,
}))}
>
{column.render('Header')}
<span className={`dc-c-sort ${column.isSorted ? column.isSortedDesc ? 'dc-c-sort--desc' : 'dc-c-sort--asc' : 'dc-c-sort--default'}`} />
</th>
))}
</tr>
{canFilter &&
(
<>
<tr>
<th
colSpan={columns.length}
className={`ds-u-padding-top--1 ds-u-padding-bottom--0 ds-u-border-bottom--0`}
>
Filter Columns
</th>
</tr>
<tr>
{headerGroup.headers.map((column) => {
return (
<th {...column.getHeaderProps()}>
{column.canFilter ? column.render('Filter') : null}

</th>
)
})}
</tr>
</>
)
}
</>
))}
</thead>
{(data && !loading)
? (
<tbody {...getTableBodyProps()}>
{rows.map((row, i) => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return <td {...cell.getCellProps()} className={`${tablePadding}`}>{cell.render('Cell')}</td>
})}
</tr>
</>
)
}
</>
))}
</thead>
{(data && !loading)
? (
<tbody {...getTableBodyProps()}>
{rows.map((row, i) => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return <td {...cell.getCellProps()} className={`${tablePadding}`}>{cell.render('Cell')}</td>
})}
</tr>
)
})}
</tbody>
)
: (
<tbody {...getTableBodyProps()}>
<tr>
<td className={`${tablePadding}`} colSpan={columns.length}>
{loading ? 'loading' : 'No data found.'}
</td>
</tr>
</tbody>
)
}
</table>
<DataTablePagination
nextPage={nextPage}
canNextPage={canNextPage}
previousPage={previousPage}
canPreviousPage={canPreviousPage}
)
})}
</tbody>
)
: (
<tbody {...getTableBodyProps()}>
<tr>
<td className={`${tablePadding}`} colSpan={columns.length}>
{loading ? 'loading' : 'No data found.'}
</td>
</tr>
</tbody>
)
}
</table>
</div>
<Pagination
gotoPage={setOffset}
currentPage={offset / limit}
totalItems={totalRows}
itemsPerPage={limit}
/>
</div>
)
Expand Down
5 changes: 2 additions & 3 deletions src/components/DataTableHeader/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const DataTableHeader = ({ setTablePadding }) => {
totalRows,
currentPage,
actions } = React.useContext(ResourceDispatch);
const { setLimit } = actions;
const { setLimit, setOffset } = actions;
return (
<div>
<div className="ds-l-row">
Expand All @@ -26,14 +26,13 @@ const DataTableHeader = ({ setTablePadding }) => {
<div className="ds-l-row">
<div className="ds-l-col--6">
<DataTableRowDetails
currentPage={currentPage}
limit={limit}
offset={offset}
totalRows={totalRows}
/>
</div>
<div className="ds-l-col--6">
<DataTableRowChanger setLimit={setLimit} />
<DataTableRowChanger setLimit={setLimit} setOffset={setOffset} />
</div>
</div>
</div>
Expand Down
5 changes: 3 additions & 2 deletions src/components/DataTableRowDetails/index.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React from 'react';
import PropTypes from 'prop-types';

const DataTableRowDetails = ({ totalRows, limit, offset, currentPage }) => {
const DataTableRowDetails = ({ totalRows, limit, offset }) => {
const ofTotal = () => {
if (limit >= totalRows) { return totalRows; }
if (offset === 0) { return limit; }
return (offset + limit);
}
const startTotal = () => (currentPage * limit + 1)
const page = offset / limit;
const startTotal = () => (page * limit + 1)
return (
<p>{`${startTotal()} - ${ofTotal()} of ${totalRows} rows`}</p>
)
Expand Down
96 changes: 96 additions & 0 deletions src/components/Pagination/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import React, { useEffect } from 'react';
import { Button } from '@cmsgov/design-system';
import { usePagination, buildPageArray } from '@civicactions/data-catalog-services';

const Pagination = ({
currentPage, totalItems, itemsPerPage, gotoPage,
}) => {
const {
pageIndex,
pages,
setPageIndex,
canGoToPrevious,
canGoToNext,
goToNext,
goToPrevious,
} = usePagination(0, totalItems, itemsPerPage);
const pageButtons = buildPageArray(pageIndex, 2, pages);

useEffect(()=> {
gotoPage((Number(pageIndex)) * itemsPerPage)
}, [pageIndex])
return (
<div className="dc-pagination ds-u-display--flex ds-u-flex-direction--row ds-u-justify-content--between ds-u-align-items--center">
<Button
disabled={!canGoToPrevious}
className="dc-pagination--previous"
size="small"
variation="transparent"
onClick={() => goToPrevious()}
>
Previous
</Button>
<ol className="ds-u-display--flex ds-u-flex-direction--row ds-u-padding-x--0">
{pageButtons.map((p) => {
if (p === 'end') {
return (
<li key={p}>
<Button
className={p === currentPage ? "dc-pagination-current" : ""}
size="small"
variation="transparent"
onClick={() => setPageIndex(pages - 1)}
>
{pages}
</Button>
</li>
)
} else if (p === 'start') {
return (
<li key={p}>
<Button
className={p === currentPage ? "dc-pagination-current" : ""}
size="small"
variation="transparent"
onClick={() => setPageIndex(0)}
>
1
</Button>
</li>
)
} else if (p === 'filler') {
return (
<li key={p + (Math.random() * 10)}>
<span>...</span>
</li>
)
} else {
return (
<li key={p}>
<Button
className={p === currentPage ? "dc-pagination-current" : ""}
size="small"
variation="transparent"
onClick={() => setPageIndex(p)}
>
{p + 1}
</Button>
</li>
)
}
})}
</ol>
<Button
disabled={!canGoToNext}
className="dc-pagination--next"
size="small"
variation="transparent"
onClick={() => goToNext()}
>
Next
</Button>
</div>
);
}

export default Pagination;
Empty file.
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export { default as NavLink } from './components/NavLink';
export { default as DataTable } from './components/DataTable';
export { default as DatasetTags } from './components/DatasetTags';
export { default as DatasetDownloads } from './components/DatasetDownloads';
export { default as Pagination } from './components/Pagination';

// Templates
export { default as Footer } from './templates/Footer';
Expand Down
3 changes: 2 additions & 1 deletion src/styles/scss/components/index.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@import "./datatable.scss";
@import "./dataset-tags.scss";
@import "./dataset-downloads.scss";
@import "./dataset-downloads.scss";
@import "./pagination.scss";
Loading

0 comments on commit ad3f8da

Please sign in to comment.