Skip to content

Commit

Permalink
React-Table 8 Upgrade (#184)
Browse files Browse the repository at this point in the history
* React-Table 8 Upgrade Initial Commit: Basic table renders with first page of data. No pagination, sorting, or filtering yet.

* Add functional pagination, sorting, filtering

* Restore Data Table Header and all functionality. Some style upgrades

* Fix various functionality bugs

* Fix keyboard a11y

* update lockfile

* Add aria-label to column resizer

* Bump to 1.17.0

* Update node version
  • Loading branch information
brdunfield authored Dec 13, 2023
1 parent bfe6dd5 commit e3078e0
Show file tree
Hide file tree
Showing 18 changed files with 6,615 additions and 3,865 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- run:
name: Install Dependencies and Run Jest Tests
command: |
nvm install 10
nvm install 18.17.1
npm install
npm rebuild node-sass
node --version
Expand Down
11 changes: 5 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@civicactions/data-catalog-components",
"version": "1.16.0",
"version": "1.17.0",
"description": "React Components for Open Data Catalogs.",
"main": "dist/index.js",
"source": "src/index.ts",
Expand All @@ -24,24 +24,24 @@
"@fortawesome/free-brands-svg-icons": "^5.11.2",
"@fortawesome/free-solid-svg-icons": "^5.11.2",
"@fortawesome/react-fontawesome": "^0.2.0",
"@tanstack/react-table": "^8.10.7",
"axios": "^1.6.2",
"bootstrap": "^4.2.1",
"excerpts": "0.0.3",
"html-to-react": "^1.7.0",
"immutability-helper": "^3.0.2",
"lodash": "^4.17.15",
"prop-types": "^15.6.2",
"qs": "^6.11.2",
"query-string": "^6.8.3",
"react-aria-modal": "^5.0.2",
"react-content-loader": "^6.0.1",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-js-pagination": "^3.0.2",
"react-router-dom": "^6.10.0",
"react-table": "^7.0.4",
"reactstrap": "^9.2.1",
"swagger-ui-react": "^4.15.5",
"validator": "^13.11.0",
"reactstrap": "^9.2.1"
"validator": "^13.11.0"
},
"devDependencies": {
"@babel/preset-env": "^7.22.5",
Expand All @@ -51,7 +51,6 @@
"@parcel/transformer-sass": "^2.10.3",
"@parcel/transformer-typescript-types": "^2.8.3",
"parcel": "^2.8.3",
"react-test-renderer": "^16.9.0",
"sass": "^1.56.1",
"sass-loader": "^7.1.0",
"url-loader": "^1.1.2"
Expand Down
22 changes: 22 additions & 0 deletions src/components/ColumnFilter/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from "react";

function ColumnFilter({
column: { getFilterValue, setFilterValue, Header },
resourceState
}) {

return (
<input
aria-label={Header}
value={getFilterValue() || ''}
onChange={(e) => {
e.preventDefault();
e.stopPropagation();
setFilterValue(e.target.value || undefined); // Set undefined to remove the filter entirely
}}
placeholder={`Search ${resourceState.count} records...`}
/>
);
};

export default ColumnFilter;
3 changes: 2 additions & 1 deletion src/components/DataTable/ManageColumns/Card.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ const Card = ({
},
});
const [{ isDragging }, drag] = useDrag({
item: { type: ItemTypes.CARD, id, index },
type: ItemTypes.CARD,
item: { id, index },
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
Expand Down
29 changes: 12 additions & 17 deletions src/components/DataTable/ManageColumns/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, { useContext, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import update from 'immutability-helper';
import Card from './Card';

import { ResourceDispatch } from '../../../services/resource/resource_defaults';
Expand All @@ -20,10 +19,11 @@ const defaultCard = (card, index, moveCard) => (
<input
id={card.id}
type="checkbox"
{...card.getToggleHiddenProps()}
defaultChecked={card.getIsVisible()}
onChange={() => card.toggleVisibility()}
/>
{' '}
{card.Header}
{card.columnDef.header}
</label>
</Card>
);
Expand All @@ -34,39 +34,34 @@ const ManageColumns = ({
const { reactTable } = useContext(ResourceDispatch);
const [cards, setCards] = useState(null);
React.useEffect(() => {
if (reactTable.allColumns.length && cards === null) {
setCards(reactTable.allColumns);
if (reactTable.getAllColumns().length && cards === null) {
setCards(reactTable.getAllColumns());
}
}, [reactTable.allColumns]);
}, [reactTable.getAllColumns()]);
const moveCard = React.useCallback(
(dragIndex, hoverIndex) => {
const dragCard = reactTable.allColumns[dragIndex];
const newCards = cards.toSpliced(hoverIndex, 0, cards.splice(dragIndex,1)[0]);

setCards(update(cards, {
$splice: [
[dragIndex, 1],
[hoverIndex, 0, dragCard],
],
}));
setCards(newCards);
},
[cards, reactTable.allColumns],
[cards, reactTable.getAllColumns()],
);
useEffect(() => {
if (cards) {
reactTable.setColumnOrder(cards.map((d) => d.id));
}
}, [cards]);

return (
return (cards && cards.length) && (
<div>
<Modal
title="Manage Columns"
nodeId="___gatsby"
openText="Manage Columns"
>
<DndProvider backend={HTML5Backend}>
{reactTable.allColumns
&& reactTable.allColumns.map((column, i) => renderCard(column, i, moveCard))}
{cards
&& cards.map((column, i) => renderCard(column, i, moveCard))}
</DndProvider>
</Modal>
</div>
Expand Down
181 changes: 80 additions & 101 deletions src/components/Resource/index.jsx
Original file line number Diff line number Diff line change
@@ -1,59 +1,61 @@
import React, { useReducer, useEffect } from 'react';
import React, { useReducer, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import FileDownload from "../FileDownload";
import DataTable from '../../templates/DataTable';

import resourceReducer from '../../services/resource/resource_reducer';

import {
useTable,
usePagination,
useFilters,
useSortBy,
useFlexLayout,
useResizeColumns,
useColumnOrder,
} from 'react-table';
queryResourceData,
getDKANDatastore,
} from '../../services/resource/resource_functions';
import useDatastore from '../../services/useDatastore';

import {
ResourceDispatch,
defaultResourceState,
} from '../../services/resource/resource_defaults';

import resourceReducer from '../../services/resource/resource_reducer';

import {
queryResourceData,
getDKANDatastore,
} from '../../services/resource/resource_functions';
useReactTable,
flexRender,
getCoreRowModel,
createColumnHelper,
getSortedRowModel,
getPaginationRowModel
} from '@tanstack/react-table';
import DataTableHeader from '../../templates/DataTableHeader';

const Resource = ({
apiURL,
children,
resource,
id,
showDBColumnNames,
transformQueryData: handleTransformQueryData,
format,
downloadURL,
accessURL
}) => {
const [resourceState, dispatch] = useReducer(
resourceReducer,
defaultResourceState,
);
const [columnOrder, setColumnOrder] = useState([]);

const columnHelper = createColumnHelper();

useEffect(() => {
dispatch({ type: 'GET_STORE' });
async function getStore() {
if (resourceState.store === null) {
dispatch(await getDKANDatastore(apiURL, resource, resourceState.pageSize, showDBColumnNames));
dispatch(await getDKANDatastore(apiURL, id, resourceState.pageSize, showDBColumnNames));
}
}
getStore();
}, []);


useEffect(() => {
dispatch({ type: 'GET_STORE' });

// async function getStore() {
// if (resourceState.store === null) {
// dispatch(await getDKANDatastore(apiURL, resource, resourceState.pageSize, true));
// }
// }
async function queryStore() {
let resourceData;

Expand All @@ -70,12 +72,7 @@ const Resource = ({
if (resourceState.updateQuery) {
queryStore();
}

// if (resourceState.store !== null) {

// } else {
// getStore();
// }

}, [
resourceState.updateQuery,
resourceState.currentPage,
Expand All @@ -84,85 +81,67 @@ const Resource = ({
resourceState.sort,
]);

const { columns, currentPage } = resourceState;
const { columns } = resourceState;
const data = resourceState.values;

// Define a default UI for filtering
function DefaultColumnFilter({
column: { filterValue, preFilteredRows, setFilter, Header },
}) {
const count = preFilteredRows ? preFilteredRows.length : 0;

return (
<input
aria-label={Header}
value={filterValue || ''}
onChange={(e) => {
setFilter(e.target.value || undefined); // Set undefined to remove the filter entirely
}}
placeholder={`Search ${count} records...`}
/>
);
}


const filterTypes = React.useMemo(
() => ({
// Add a new fuzzyTextFilterFn filter type.
// fuzzyText: fuzzyTextFilterFn,
// Or, override the default text filter to use
// "startWith"
text: (rows, id, filterValue) => (
rows.filter((row) => {
const rowValue = row.values[id];
return rowValue !== undefined
? String(rowValue)
.toLowerCase()
.startsWith(String(filterValue).toLowerCase())
: true;
const table_columns = columns.map((col) => {
if (col.cell) {
return (
columnHelper.accessor(col.accessor, {
header: col.header,
cell: col.cell,
minSize: 215
})
),
}),
[],
);


const defaultColumn = React.useMemo(
() => ({
// Let's set up our default Filter UI
Filter: DefaultColumnFilter,
minWidth: 30,
// width: 150,
maxWidth: 400,
}),
[],
);


const reactTable = useTable(
)
}
return (
columnHelper.accessor(col.accessor, {
header: col.header,
minSize: 215
})
)
});

const reactTable = useReactTable(
{
columns,
data,
initialState: { pageIndex: currentPage },
manualPagination: true,
manualSortBy: true,
manualFilters: true,
pageCount: Number(Math.ceil(resourceState.rowsTotal / resourceState.pageSize)),
defaultColumn,
filterTypes,
},
useFilters,
useFlexLayout,
useResizeColumns,
useColumnOrder,
useSortBy,
usePagination,
data: data,
columns: table_columns,
manualSorting: true,
manualFiltering: true,
columnResizeMode: 'onChange',
onColumnOrderChange: setColumnOrder,
initialState: {
pagination: {
pageSize: resourceState.pageSize,
},
},
state: {
columnOrder: columnOrder
},
pageCount: Number(Math.ceil(resourceState.count / resourceState.pageSize)),
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
debugTable: false,
autoResetPageIndex: false,
}
);

return (
<ResourceDispatch.Provider value={{ resourceState, dispatch, reactTable }}>
{ children }
</ResourceDispatch.Provider>
<div id="resource">
{data.length ? (
<ResourceDispatch.Provider value={{dispatch, reactTable, resourceState}}>
<FileDownload
title={"test"}
label={downloadURL}
format={format}
downloadURL={downloadURL ? downloadURL : accessURL}
/>
<DataTableHeader />
<DataTable />
</ResourceDispatch.Provider>
) : ('')
}
</div>
);
};

Expand Down
Loading

0 comments on commit e3078e0

Please sign in to comment.