Skip to content

Commit

Permalink
demo(table): using tanstack
Browse files Browse the repository at this point in the history
  • Loading branch information
theJohnnyMe committed May 14, 2024
1 parent 48749bd commit 7f0b056
Show file tree
Hide file tree
Showing 7 changed files with 283 additions and 0 deletions.
48 changes: 48 additions & 0 deletions package-lock.json

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

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
"access": "public"
},
"dependencies": {
"@faker-js/faker": "^8.4.1",
"@scania/tegel-react": "1.8.1",
"@tanstack/react-table": "^8.17.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
Expand Down
6 changes: 6 additions & 0 deletions src/components/Navigation/SideMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ const SideMenu = ({ style, className, pathname, toggleMobileNav, sideMenuRef }:
Text
</Link>
</TdsSideMenuItem>
<TdsSideMenuItem selected={pathname === '/tanstack'}>
<Link to={'/tanstack'} onClick={() => toggleMobileNav()}>
<TdsIcon name="warning" size="24"></TdsIcon>
Tanstack POC
</Link>
</TdsSideMenuItem>
<div slot="end">
<TdsSideMenuDropdown selected={pathname.includes('settings')}>
<TdsSideMenuUser
Expand Down
10 changes: 10 additions & 0 deletions src/customEvents.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,13 @@ interface HTMLElementEventMap {
tdsFocus: TdsFocusEvent;
tdsChange: TdsChangeEvent;
}

declare module 'demoData' {
export function makeData(size: number): any[];
export interface Person {
firstName: string;
lastName: string;
age: number;
// add other properties as needed
}
}
5 changes: 5 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import StepperPage from './pages/StepperPage';
import NotificationPage from './pages/NotificationPage';
import MainLayout from './MainLayout';
import NotFound from './pages/NotFoundPage/NotFound';
import Tanstack from './pages/Tanstack';
import { defineCustomElements } from '@scania/tegel-react';

export const mainRoutes: RouteObject[] = [
Expand Down Expand Up @@ -79,6 +80,10 @@ export const mainRoutes: RouteObject[] = [
path: 'text',
element: <TextPage />,
},
{
path: 'tanstack',
element: <Tanstack />,
},
{
path: 'text/subpage-with-a-very-long-title',
element: <TextSubpage />,
Expand Down
166 changes: 166 additions & 0 deletions src/pages/Tanstack.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import React from 'react';

import {
ColumnDef,
flexRender,
getCoreRowModel,
getSortedRowModel,
SortingFn,
SortingState,
useReactTable,
} from '@tanstack/react-table';
import { makeData, Person } from './demoData';
import {
TdsTable,
TdsTableHeader,
TdsHeaderCell,
TdsTableBody,
TdsTableBodyRow,
TdsBodyCell,
} from '@scania/tegel-react';

//custom sorting logic for one of our enum columns
const sortStatusFn: SortingFn<Person> = (rowA, rowB, _columnId) => {
const statusA = rowA.original.status;
const statusB = rowB.original.status;
const statusOrder = ['single', 'complicated', 'relationship'];
return statusOrder.indexOf(statusA) - statusOrder.indexOf(statusB);
};

const Tanstack = () => {
const rerender = React.useReducer(() => ({}), {})[1];

const [sorting, setSorting] = React.useState<SortingState>([]);

const columns = React.useMemo<ColumnDef<Person>[]>(
() => [
{
accessorKey: 'firstName',
cell: (info) => info.getValue(),
//this column will sort in ascending order by default since it is a string column
},
{
accessorFn: (row) => row.lastName,
id: 'lastName',
cell: (info) => info.getValue(),
header: () => <span>Last Name</span>,
sortUndefined: 'last', //force undefined values to the end
sortDescFirst: false, //first sort order will be ascending (nullable values can mess up auto detection of sort order)
},
{
accessorKey: 'age',
header: () => 'Age',
//this column will sort in descending order by default since it is a number column
},
{
accessorKey: 'visits',
header: () => <span>Visits</span>,
sortUndefined: 'last', //force undefined values to the end
},
{
accessorKey: 'status',
header: 'Status',
sortingFn: sortStatusFn, //use our custom sorting function for this enum column
},
{
accessorKey: 'progress',
header: 'Profile Progress',
// enableSorting: false, //disable sorting for this column
},
{
accessorKey: 'rank',
header: 'Rank',
invertSorting: true, //invert the sorting order (golf score-like where smaller is better)
},
],
[],
);

const [data, setData] = React.useState(() => makeData(1_000));
const refreshData = () => setData(() => makeData(100_000)); //stress test with 100k rows

const table = useReactTable({
columns,
data,
debugTable: true,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(), //client-side sorting
onSortingChange: setSorting, //optionally control sorting state in your own scope for easy access
// sortingFns: {
// sortStatusFn, //or provide our custom sorting function globally for all columns to be able to use
// },
//no need to pass pageCount or rowCount with client-side pagination as it is calculated automatically
state: {
sorting,
},
// autoResetPageIndex: false, // turn off page index reset when sorting or filtering - default on/true
// enableMultiSort: false, //Don't allow shift key to sort multiple columns - default on/true
// enableSorting: false, // - default on/true
// enableSortingRemoval: false, //Don't allow - default on/true
// isMultiSortEvent: (e) => true, //Make all clicks multi-sort - default requires `shift` key
// maxMultiSortColCount: 3, // only allow 3 columns to be sorted at once - default is Infinity
});

//access sorting state from the table instance
console.log(table.getState().sorting);

return (
<div className="p-2">
<div className="h-2" />
<TdsTable verticalDividers={true} responsive={true} compactDesign={true}>
{table.getHeaderGroups().map((headerGroup) => (
<TdsTableHeader key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TdsHeaderCell key={header.id}>
{header.isPlaceholder ? null : (
<div
className={header.column.getCanSort() ? 'cursor-pointer select-none' : ''}
onClick={header.column.getToggleSortingHandler()}
title={
header.column.getCanSort()
? header.column.getNextSortingOrder() === 'asc'
? 'Sort ascending'
: header.column.getNextSortingOrder() === 'desc'
? 'Sort descending'
: 'Clear sort'
: undefined
}
>
{flexRender(header.column.columnDef.header, header.getContext())}
{{
asc: ' 🔼',
desc: ' 🔽',
}[header.column.getIsSorted() as string] ?? null}
</div>
)}
</TdsHeaderCell>
);
})}
</TdsTableHeader>
))}

<TdsTableBody>
{table
.getRowModel()
.rows.slice(0, 10)
.map((row) => {
return (
<TdsTableBodyRow key={row.id}>
{row.getVisibleCells().map((cell) => {
return (
<TdsBodyCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TdsBodyCell>
);
})}
</TdsTableBodyRow>
);
})}
</TdsTableBody>
</TdsTable>
</div>
);
};

export default Tanstack;
46 changes: 46 additions & 0 deletions src/pages/demoData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { faker } from '@faker-js/faker';

export type Person = {
firstName: string;
lastName: string | undefined;
age: number;
visits: number | undefined;
progress: number;
status: 'relationship' | 'complicated' | 'single';
rank: number;
subRows?: Person[];
};

const range = (len: number) => {
const arr: number[] = [];
for (let i = 0; i < len; i++) {
arr.push(i);
}
return arr;
};

const newPerson = (): Person => {
return {
firstName: faker.person.firstName(),
lastName: Math.random() < 0.1 ? undefined : faker.person.lastName(),
age: faker.number.int(40),
visits: Math.random() < 0.1 ? undefined : faker.number.int(1000),
progress: faker.number.int(100),
status: faker.helpers.shuffle<Person['status']>(['relationship', 'complicated', 'single'])[0]!,
rank: faker.number.int(100),
};
};

export function makeData(...lens: number[]) {
const makeDataLevel = (depth = 0): Person[] => {
const len = lens[depth]!;
return range(len).map((_d): Person => {
return {
...newPerson(),
subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined,
};
});
};

return makeDataLevel();
}

0 comments on commit 7f0b056

Please sign in to comment.