From ad3f8da9c0ce3abaf8f621156c4abd1531cb2d48 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 20 Jan 2021 18:49:33 -0500 Subject: [PATCH] Custom hook pagination (#3) * 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 --- src/components/DataTable/index.jsx | 188 ++++++++---------- src/components/DataTableHeader/index.jsx | 5 +- src/components/DataTableRowDetails/index.jsx | 5 +- src/components/Pagination/index.jsx | 96 +++++++++ src/components/Pagination/pagination.test.jsx | 0 src/index.js | 1 + src/styles/scss/components/index.scss | 3 +- src/styles/scss/components/pagination.scss | 51 +++++ 8 files changed, 243 insertions(+), 106 deletions(-) create mode 100644 src/components/Pagination/index.jsx create mode 100644 src/components/Pagination/pagination.test.jsx create mode 100644 src/styles/scss/components/pagination.scss diff --git a/src/components/DataTable/index.jsx b/src/components/DataTable/index.jsx index 546132e1..eac3bd97 100644 --- a/src/components/DataTable/index.jsx +++ b/src/components/DataTable/index.jsx @@ -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 { @@ -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; @@ -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, @@ -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)) @@ -116,89 +102,91 @@ const DataTable = ({ canFilter, tablePadding }) => { }, [filters]) return( -
- - - {headerGroups.map((headerGroup) => ( - <> - - {headerGroup.headers.map((column, index) => ( - - ))} - - {canFilter && - ( - <> - - - - - {headerGroup.headers.map((column) => { - return ( - - ) +
+
+
- {column.render('Header')} - -
- Filter Columns -
- {column.canFilter ? column.render('Filter') : null} - -
+ + {headerGroups.map((headerGroup) => ( + <> + + {headerGroup.headers.map((column, index) => ( + + ))} + + {canFilter && + ( + <> + + + + + {headerGroup.headers.map((column) => { + return ( + + ) + })} + + + ) + } + + ))} + + {(data && !loading) + ? ( + + {rows.map((row, i) => { + prepareRow(row) + return ( + + {row.cells.map(cell => { + return })} - - ) - } - - ))} - - {(data && !loading) - ? ( - - {rows.map((row, i) => { - prepareRow(row) - return ( - - {row.cells.map(cell => { - return - })} - - ) - })} - - ) - : ( - - - - - - ) - } -
+ {column.render('Header')} + +
+ Filter Columns +
+ {column.canFilter ? column.render('Filter') : null} + +
{cell.render('Cell')}
{cell.render('Cell')}
- {loading ? 'loading' : 'No data found.'} -
- + ) + : ( + + + + {loading ? 'loading' : 'No data found.'} + + + + ) + } + +
+ ) diff --git a/src/components/DataTableHeader/index.jsx b/src/components/DataTableHeader/index.jsx index 7d89bbbb..d1858201 100644 --- a/src/components/DataTableHeader/index.jsx +++ b/src/components/DataTableHeader/index.jsx @@ -12,7 +12,7 @@ const DataTableHeader = ({ setTablePadding }) => { totalRows, currentPage, actions } = React.useContext(ResourceDispatch); - const { setLimit } = actions; + const { setLimit, setOffset } = actions; return (
@@ -26,14 +26,13 @@ const DataTableHeader = ({ setTablePadding }) => {
- +
diff --git a/src/components/DataTableRowDetails/index.jsx b/src/components/DataTableRowDetails/index.jsx index b050ce8e..29bd1426 100644 --- a/src/components/DataTableRowDetails/index.jsx +++ b/src/components/DataTableRowDetails/index.jsx @@ -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 (

{`${startTotal()} - ${ofTotal()} of ${totalRows} rows`}

) diff --git a/src/components/Pagination/index.jsx b/src/components/Pagination/index.jsx new file mode 100644 index 00000000..a8d2a5ce --- /dev/null +++ b/src/components/Pagination/index.jsx @@ -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 ( +
+ +
    + {pageButtons.map((p) => { + if (p === 'end') { + return ( +
  1. + +
  2. + ) + } else if (p === 'start') { + return ( +
  3. + +
  4. + ) + } else if (p === 'filler') { + return ( +
  5. + ... +
  6. + ) + } else { + return ( +
  7. + +
  8. + ) + } + })} +
+ +
+ ); +} + +export default Pagination; diff --git a/src/components/Pagination/pagination.test.jsx b/src/components/Pagination/pagination.test.jsx new file mode 100644 index 00000000..e69de29b diff --git a/src/index.js b/src/index.js index f0d09028..de5ef0fc 100644 --- a/src/index.js +++ b/src/index.js @@ -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'; diff --git a/src/styles/scss/components/index.scss b/src/styles/scss/components/index.scss index 1bb9cff6..58495b6d 100644 --- a/src/styles/scss/components/index.scss +++ b/src/styles/scss/components/index.scss @@ -1,3 +1,4 @@ @import "./datatable.scss"; @import "./dataset-tags.scss"; -@import "./dataset-downloads.scss"; \ No newline at end of file +@import "./dataset-downloads.scss"; +@import "./pagination.scss"; \ No newline at end of file diff --git a/src/styles/scss/components/pagination.scss b/src/styles/scss/components/pagination.scss new file mode 100644 index 00000000..5e763ca5 --- /dev/null +++ b/src/styles/scss/components/pagination.scss @@ -0,0 +1,51 @@ +@import "~@cmsgov/design-system/dist/scss/settings/variables/color"; + +.dc-pagination { + ol { + list-style: none; + } + button { + text-decoration: none; + } + .dc-pagination-current { + font-weight: bold; + color: $color-base; + text-decoration: underline; + } + .dc-pagination--previous, + .dc-pagination--next { + height: 100%; + &:disabled { + background: transparent!important; + &::before, + &::after { + color: $color-base; + } + } + &::before, + &::after { + font-family: 'FontAwesome'; + color: $color-primary; + font-size: 16px; + padding-right: 10px; + display: inline-block; + line-height: 16px; + height: 16px; + width: 16px; + margin-left: 10px; + text-align: center; + vertical-align: middle; + font-weight: lighter; + } + } + .dc-pagination--previous { + &::before { + content: '\f053'; + } + } + .dc-pagination--next { + &::after { + content: '\f054'; + } + } +}