Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: react-table --> tanstack #72

Merged
merged 39 commits into from
Mar 7, 2024
Merged
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
f18d4c2
wip: to tanstack
eldu Feb 15, 2024
39e1b7a
wip: to tanstack
eldu Feb 15, 2024
84cf201
refactor: js --> tsx
eldu Feb 16, 2024
b3004d9
refactor: Use some basic React Bootstrap components
eldu Feb 27, 2024
b2c54a7
Merge branch 'build-update' into refactor-tanstack
eldu Feb 27, 2024
ae57867
fix: some dependencies that were trouble
eldu Feb 27, 2024
b36e2f0
style: Table Controls
eldu Feb 27, 2024
ed1186b
fix: missing package lock
eldu Feb 27, 2024
df9a796
refactor: remove _table.scss
eldu Feb 29, 2024
2ab4f6f
style: fix theme colors
eldu Feb 29, 2024
f9e2b30
style: closer to original table
eldu Feb 29, 2024
9474cbd
refactor: font awesome icon
eldu Feb 29, 2024
8af8f41
refactor: tanstack columns
eldu Feb 29, 2024
ac59cf5
refactor: sorting icon
eldu Feb 29, 2024
2ad9a31
style: refactor out _addPub.scss
eldu Feb 29, 2024
0934920
build: remove reactstrap
eldu Feb 29, 2024
fb02993
style: clean up some more style
eldu Feb 29, 2024
024da4a
style: clean up some more style
eldu Feb 29, 2024
d486873
style: fix table controls
eldu Feb 29, 2024
8515c6a
style: refactor to same same
eldu Feb 29, 2024
dc7cc26
style: fix div
eldu Feb 29, 2024
00816cf
wip: why is this table resizing so hard
eldu Mar 1, 2024
bac55c1
fix: most of my table resizing sadness
eldu Mar 1, 2024
3c8b9e7
fix: pagination
eldu Mar 1, 2024
8bf6240
refactor: move out components
eldu Mar 1, 2024
c19aa86
refactor: more clean up
eldu Mar 1, 2024
c1c147f
refactor: more clean up
eldu Mar 1, 2024
9c6229a
refactor: more clean up
eldu Mar 1, 2024
8e10aec
refactor: remove unneeded flex
eldu Mar 1, 2024
11cd4e7
Merge branch 'build-update' into refactor-tanstack
eldu Mar 1, 2024
5af92a5
fix: package-lock.json
eldu Mar 1, 2024
8dff126
refactor: clean up
eldu Mar 1, 2024
53fdaae
refactor: sorting icons
eldu Mar 1, 2024
f696930
fix: clicking header
eldu Mar 1, 2024
0cedba2
fix: quick fix for footer. trying to not touch the rest ! :scream:
eldu Mar 1, 2024
d16c5e1
refactor: pagination clean up
eldu Mar 1, 2024
00f03be
refactor: some more style clean up
eldu Mar 4, 2024
bfea3cb
refactor: some more style clean up
eldu Mar 4, 2024
2b9d6d3
docs: add issue number for word cloud
eldu Mar 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31,968 changes: 2,629 additions & 29,339 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 2 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -18,6 +18,8 @@
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/react-fontawesome": "^0.2.0",
"@reduxjs/toolkit": "^2.0.1",
"@tanstack/match-sorter-utils": "^8.11.8",
"@tanstack/react-table": "^8.12.0",
"bootstrap": "^5.3.2",
"firebase": "^10.7.2",
"formik": "^2.4.5",
@@ -29,27 +31,21 @@
"react-router-dom": "^6.21.2",
"react-scripts": "^5.0.1",
"react-spinners": "^0.13.8",
"react-table": "^6.10.0",
"react-vega": "^7.6.0",
"redux": "^5.0.1",
"sass": "^1.69.7",
"seamless-immutable": "^7.1.4",
"table": "^5.4.1",
"vega": "^5.27.0",
"vega-lite": "^5.16.3",
"yup": "^1.3.3"
},
"devDependencies": {
"@brown-ccv/eslint-config": "^0.3.0",
"@brown-ccv/prettier-config": "^0.3.0",
"chromedriver": "^83.0.0",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.2",
"firebase-admin": "^12.0.0",
"firebase-tools": "^13.0.3",
"husky": "^8.0.3",
"lint-staged": "^15.2.0",
"nightwatch": "^1.3.6",
"prettier": "^3.2.4"
},
"eslintConfig": {
18 changes: 8 additions & 10 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -13,16 +13,14 @@ export function App() {
return (
<div aria-live="polite">
<Navbar />
<div className="App">
<BrowserRouter>
<main>
<Routes>
<Route exact path="/" element={<ContentPage />} />
</Routes>
</main>
</BrowserRouter>
<Footer />
</div>
<BrowserRouter>
<main className="main">
<Routes>
<Route exact path="/" element={<ContentPage />} />
</Routes>
</main>
</BrowserRouter>
<Footer />
</div>
);
}
6 changes: 3 additions & 3 deletions src/components/AddPublicationModal.tsx
Original file line number Diff line number Diff line change
@@ -51,7 +51,7 @@ export function AddPublicationModal() {

return (
<>
<Button variant="contained" color="primary" onClick={handleShow}>
<Button variant="primary" onClick={handleShow}>
Add Publication
</Button>

@@ -149,7 +149,7 @@ const SearchDoiForm = ({
>
Enter Manually
</Button>
<LoadingButton loading={loading} type="submit" variant="contained" form={searchFormId}>
<LoadingButton loading={loading} type="submit" variant="primary" form={searchFormId}>
Search
</LoadingButton>
</Modal.Footer>
@@ -324,7 +324,7 @@ const ManualForm = ({
>
Back
</Button>
<LoadingButton loading={loading} type="submit" variant="contained" form={manualFormId}>
<LoadingButton loading={loading} type="submit" variant="primary" form={manualFormId}>
Submit
</LoadingButton>
</Modal.Footer>
53 changes: 53 additions & 0 deletions src/components/ColumnFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react';
import { DebouncedInput } from './DebouncedInput.tsx';

export function ColumnFilter({ column, table }) {
const firstValue = table.getPreFilteredRowModel().flatRows[0]?.getValue(column.id);

const columnFilterValue = column.getFilterValue();

const sortedUniqueValues = React.useMemo(
() =>
typeof firstValue === 'number'
? []
: Array.from(column.getFacetedUniqueValues().keys()).sort(),
// eslint-disable-next-line react-hooks/exhaustive-deps
[column.getFacetedUniqueValues()]
);

return typeof firstValue === 'number' ? (
<div className="d-flex">
<DebouncedInput
type="number"
min={Number(column.getFacetedMinMaxValues()?.[0] ?? '')}
max={Number(column.getFacetedMinMaxValues()?.[1] ?? '')}
value={(columnFilterValue as [number, number])?.[0] ?? ''}
onChange={(value) => column.setFilterValue((old: [number, number]) => [value, old?.[1]])}
placeholder="min"
/>
<DebouncedInput
type="number"
min={Number(column.getFacetedMinMaxValues()?.[0] ?? '')}
max={Number(column.getFacetedMinMaxValues()?.[1] ?? '')}
value={(columnFilterValue as [number, number])?.[1] ?? ''}
onChange={(value) => column.setFilterValue((old: [number, number]) => [old?.[0], value])}
placeholder="max"
/>
</div>
) : (
<>
<datalist id={column.id + 'list'}>
{sortedUniqueValues.slice(0, 5000).map((value) => (
<option value={value} key={value} />
))}
</datalist>
<DebouncedInput
type="text"
value={(columnFilterValue ?? '') as string}
onChange={(value) => column.setFilterValue(value)}
list={column.id + 'list'}
/>
<div className="h-1" />
</>
);
}
29 changes: 14 additions & 15 deletions src/components/ContentPage.jsx
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faBook } from '@fortawesome/free-solid-svg-icons';
import { useSelector } from 'react-redux';
import { selectPublications, selectUser } from '../store/slice/appState';
import { PubsTable } from './PubsTable';
import { PublicationsTable } from './PublicationsTable.tsx';
import Spinner from './Spinner';
import { AddPublicationModal } from './AddPublicationModal.tsx';
import { YearChart } from './YearChart.tsx';
@@ -13,34 +13,33 @@ export function ContentPage() {
const user = useSelector(selectUser);

return (
<div className="ContentPage main-content">
<div className="main-content d-flex flex-column align-items-center">
{user ? (
<div align="right">
<div className="align-self-end">
<AddPublicationModal />
</div>
) : null}
<div className="d-flex flex-row justify-content-center align-items-center">
<div className="pub-title bg-dark rounded-circle p-2 mx-2">
<FontAwesomeIcon icon={faBook} color="white" />
</div>
<h1 className="pl-2">Publications</h1>

<div className="d-flex flex-row align-items-center justify-content-center">
<FontAwesomeIcon icon={faBook} color="dark" size="2xl" />
<h1 className="mx-2">Publications</h1>
</div>

<Spinner loading={publications.length === 0} className="spinner" size={100} />

{publications.length !== 0 && (
<div id="main-content">
<div className="PubsTable-CP">
<PubsTable />
</div>
<>
<PublicationsTable />

{/* TODO: Word Cloud */}
<h3 className="word-cloud-title pt-4 mt-4">What are these publications all about? </h3>
{/* TODO: Word Cloud #58 */}
<h2 className="title pt-4 m-4 is-2 text-center">
What are these publications all about?
</h2>
{/*<div className="viz d-flex justify-content-center pt-5">*/}
{/* <WordCloud />*/}
<YearChart />
{/*</div>*/}
</div>
</>
)}
</div>
);
34 changes: 34 additions & 0 deletions src/components/DebouncedInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// A debounced input react component
import React from 'react';
import Form from 'react-bootstrap/Form';

export function DebouncedInput({
value: initialValue,
onChange,
debounce = 500,
...props
}: {
value: string | number;
onChange: (value: string | number) => void;
debounce?: number;
} & Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'>) {
const [value, setValue] = React.useState(initialValue);

React.useEffect(() => {
setValue(initialValue);
}, [initialValue]);

React.useEffect(
() => {
const timeout = setTimeout(() => {
onChange(value);
}, debounce);

return () => clearTimeout(timeout);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[value]
);

return <Form.Control {...props} value={value} onChange={(e) => setValue(e.target.value)} />;
}
205 changes: 205 additions & 0 deletions src/components/PublicationsTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import React from 'react';
import { useSelector } from 'react-redux';

import {
ColumnFiltersState,
createColumnHelper,
flexRender,
getCoreRowModel,
getFacetedMinMaxValues,
getFacetedRowModel,
getFacetedUniqueValues,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
PaginationState,
useReactTable,
} from '@tanstack/react-table';

import Table from 'react-bootstrap/Table';
import Container from 'react-bootstrap/Container';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import Button from 'react-bootstrap/Button';
import Form from 'react-bootstrap/Form';

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faArrowDownWideShort, faArrowUpShortWide } from '@fortawesome/free-solid-svg-icons';

import { selectPublications } from '../store/slice/appState';
import { Publication } from '../../types';
import { ColumnFilter } from './ColumnFilter.tsx';

export function PublicationsTable() {
const publications = useSelector(selectPublications);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 5,
});

const columnHelper = createColumnHelper<Publication>();

const columns = [
columnHelper.accessor('title', {
header: 'Title',
cell: (info) => (
<a href={info.row.original.url} target="_blank" rel="noopener noreferrer">
{info.getValue()}
</a>
),
}),
columnHelper.accessor('author', {
header: 'Author(s)',
cell: (info) => info.getValue(),
}),
columnHelper.accessor('year', {
header: 'Year',
cell: (info) => info.getValue(),
size: 100,
}),
];

const table = useReactTable({
data: publications,
columns,
state: {
columnFilters,
pagination,
},
columnResizeMode: 'onChange',
onColumnFiltersChange: setColumnFilters,
onPaginationChange: setPagination,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getSortedRowModel: getSortedRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getFacetedRowModel: getFacetedRowModel(),
getFacetedUniqueValues: getFacetedUniqueValues(),
getFacetedMinMaxValues: getFacetedMinMaxValues(),
});

return (
<Container fluid>
<Row>
<Table className="align-middle text-break text-center" bordered>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<th
key={header.id}
colSpan={header.colSpan}
className="h3 bg-secondary"
style={{
position: 'relative',
width: header.getSize(),
}}
>
{header.isPlaceholder ? null : (
<>
<div
className={
header.column.getCanSort() ? 'cursor-pointer select-none' : ''
}
onClick={header.column.getToggleSortingHandler()}
>
{/* Header */}
{flexRender(header.column.columnDef.header, header.getContext())}
{/* Sorting Icons */}
{{
asc: <FontAwesomeIcon icon={faArrowUpShortWide} />,
desc: <FontAwesomeIcon icon={faArrowDownWideShort} />,
}[header.column.getIsSorted() as string] ?? null}
</div>
{/* Column Filter */}
{header.column.getCanFilter() ? (
<ColumnFilter column={header.column} table={table} />
) : null}
<div
onDoubleClick={() => header.column.resetSize()}
onMouseDown={header.getResizeHandler()}
onTouchStart={header.getResizeHandler()}
className={`resizer ${header.column.getIsResizing() ? 'is-resizing' : ''}`}
/>
</>
)}
</th>
);
})}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => {
return (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => {
return (
<td key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
);
})}
</tr>
);
})}
</tbody>
</Table>
</Row>
<Row>
<Col sm={12} md={4} lg={4} className="d-grid">
<Button
variant="warning"
size="lg"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
</Col>
<Col sm={12} md={4} lg={4} className="d-flex justify-content-around">
<div className="d-flex align-items-center">
<span className="mx-2">Page</span>
<Form.Control
type="number"
value={table.getState().pagination.pageIndex + 1}
onChange={(e) => {
const page = e.target.value ? Number(e.target.value) - 1 : 0;
table.setPageIndex(page);
}}
min={1}
max={table.getPageCount()}
/>
<span className="mx-2 text-nowrap">of {table.getPageCount()}</span>
</div>
<div className="d-flex align-items-center">
<Form.Select
value={table.getState().pagination.pageSize}
onChange={(e) => {
table.setPageSize(Number(e.target.value));
}}
>
{[5, 10, 20, 25, 50, 100].map((pageSize) => (
<option key={pageSize} value={pageSize}>
{pageSize} rows
</option>
))}
</Form.Select>
</div>
</Col>
<Col sm={12} md={4} lg={4} className="d-grid">
<Button
variant="secondary"
size="lg"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</Col>
</Row>
</Container>
);
}
59 changes: 0 additions & 59 deletions src/components/PubsTable.js

This file was deleted.

4 changes: 3 additions & 1 deletion src/components/react-ccv-components/Footer.js
Original file line number Diff line number Diff line change
@@ -8,7 +8,9 @@ const BrownFooter = () => (
<footer id="brown-footer" className="d-print-none">
<div className="marginator">
<section>
<BrownFooterLogo />
<div className="p-5">
<BrownFooterLogo />
</div>
<div id="give-to-brown">
<a href="http://giving.brown.edu" className="giving-footer">
Giving to Brown
5 changes: 4 additions & 1 deletion src/styles/_variables.scss
Original file line number Diff line number Diff line change
@@ -16,11 +16,14 @@ $dark-brown: #4e3629;
$brown-100: #a79a94;
$brown-200: #745d54;

$theme-colors: (
$custom-colors: (
'brown': $brown,
'beige': $beige,
'sand': $sand,
'light-brown': $light-brown,
'medium-brown': $medium-brown,
'dark-brown': $dark-brown,
);

$primary: $blue;
$secondary: $yellow;
34 changes: 0 additions & 34 deletions src/styles/components/_addPub.scss

This file was deleted.

21 changes: 0 additions & 21 deletions src/styles/components/_app.scss

This file was deleted.

36 changes: 0 additions & 36 deletions src/styles/components/_table.scss

This file was deleted.

40 changes: 33 additions & 7 deletions src/styles/custom.scss
Original file line number Diff line number Diff line change
@@ -1,18 +1,44 @@
// Import Bootstrap
@import '~bootstrap/scss/bootstrap.scss';

// Override Bootstrap Variables
@import 'variables';

@import '~bootstrap/scss/bootstrap.scss';
$theme-colors: map-merge($theme-colors, $custom-colors);

// Import Custom Components
@import './src/styles/components/_brown_fonts.scss';
@import './src/styles/components/_brown_icons.scss';
@import './src/styles/components/_brown_footer.scss';
@import './src/styles/components/_app.scss';
@import './src/styles/components/_table.scss';
@import './src/styles/components/_addPub.scss';

.main {
min-height: 90vh;
padding: 2vw 3vw;
}

.form-group.required > .form-label:after {
content: '*';
content: ' *';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For my own understanding, what does adding a space do here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if you open up the form modal, all the required fields have " *" next to it if it's a required field. The space in this case is just a space. So instead of DOI* we get DOI *

Screenshot 2024-03-05 at 4 42 23 PM

color: red;
}

.resizer {
position: absolute;
right: 0;
top: 0;
height: 100%;
width: 5px;
background: $brown;
cursor: col-resize;
user-select: none;
touch-action: none;
opacity: 0;
}

@media (hover: hover) {
.resizer:hover {
opacity: 1;
}
}

.resizer.is-resizing {
background: $green;
opacity: 1;
}