Skip to content
This repository has been archived by the owner on Aug 31, 2022. It is now read-only.

Commit

Permalink
feat: add table component
Browse files Browse the repository at this point in the history
  • Loading branch information
Rickk137 committed Jul 12, 2022
1 parent f57a5fa commit 033c606
Show file tree
Hide file tree
Showing 6 changed files with 369 additions and 6 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@synthetixio/transaction-notifier": "^2.70.1",
"clsx": "^1.1.1",
"lodash": "^4.17.21",
"react-table": "^7.8.0",
"react-transition-group": "^4.4.2"
},
"devDependencies": {
Expand All @@ -52,6 +53,7 @@
"@types/node": "^16.11.13",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@types/react-table": "^7.7.12",
"@types/react-transition-group": "^4.4.4",
"@typescript-eslint/eslint-plugin": "^5.7.0",
"@typescript-eslint/parser": "^5.7.0",
Expand All @@ -71,8 +73,8 @@
"lint-staged": "^12.1.2",
"postcss": "^8.4.5",
"prettier": "^2.5.1",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.0",
"rollup": "^2.61.1",
"rollup-plugin-copy": "^3.4.0",
Expand All @@ -83,7 +85,7 @@
"semantic-release": "19.0.2",
"storybook-dark-mode": "^1.1.0",
"tailwindcss": "^3.0.3",
"typescript": "^4.5.4",
"typescript": "^4.7.4",
"web-vitals": "^2.1.2",
"webpack": "5"
},
Expand Down
89 changes: 89 additions & 0 deletions src/components/Table/Table.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { Column } from 'react-table';

import { Table } from './Table';

export default {
title: 'Table',
component: Table,
decorators: [(Story) => <Story />]
} as ComponentMeta<typeof Table>;

const Template: ComponentStory<typeof Table> = (args) => <Table {...args} />;

export const Primary = Template.bind({});

type Crypto = {
coin: string;
symbol: string;
price: number;
percent1H: number;
percent24H: number;
percent7D: number;
last7Days: string;
};

const data: Crypto[] = [
{
coin: 'Synthetix',
symbol: 'sUSD',
price: 11,
percent1H: -1.3,
percent24H: -3.5,
percent7D: -8.6,
last7Days: 'https://picsum.photos/50'
},
{
coin: 'Synthetix',
symbol: 'SNX',
price: 2.71,
percent1H: -5.3,
percent24H: 9.5,
percent7D: 2.6,
last7Days: 'https://picsum.photos/50'
}
];

const columns: Column<Crypto>[] = [
{
disableSortBy: true,
Header: 'COIN',
accessor: 'symbol',
columnClass: 'ui-text-left ui-pl-14',
Cell: ({ row }) => (
<div className='ui-flex ui-items-center'>
<img
alt=''
className='ui-w-6 ui-h-6 ui-rounded-full ui-mr-3'
src='https://picsum.photos/50'
/>
<span className='ui-tg-caption-bold lg:ui-tg-content-bold'>{row.original.symbol}</span>
<span className='ui-hidden ui-tg-content ui-ml-3 lg:ui-block'>{row.original.coin}</span>
</div>
)
},
{
Header: 'PRICE*',
accessor: (row) => row.price,
sortType: (a, b) => a.original.price - b.original.price
},
{
Header: '1H',
accessor: 'percent1H',
Cell: (row) => row.value,
sortType: (a, b) => a.original.percent1H - b.original.percent1H
},
{
Header: '7H',
accessor: 'percent7D',
Cell: (row) => row.value,
sortType: (a, b) => a.original.percent7D - b.original.percent7D
}
];

Primary.args = {
className: 'ui-shadow-md',
data: data as unknown as Readonly<Record<string, unknown>[]>,
columns: columns as unknown as Readonly<Column<Record<string, unknown>>[]>,
initialState: { pageSize: 2 }
};
133 changes: 133 additions & 0 deletions src/components/Table/Table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/* eslint-disable react/jsx-key */
import clsx from 'clsx';
import { Icon } from 'components/Icon/Icon';
import { Pagination, PaginationLocalization } from 'components/Pagination/Pagination';
import React, { ReactElement, ReactNode, useMemo } from 'react';
import {
Row,
TableHeaderProps,
TableOptions,
useFlexLayout,
usePagination,
useSortBy,
useTable
} from 'react-table';

export interface TableProps<T extends Record<string, unknown>> extends TableOptions<T> {
className?: string;
paginationLocalization?: PaginationLocalization;
onClick?: (row: Row<T>) => void;
}

export const Table = <T extends Record<string, unknown>>(props: TableProps<T>): ReactElement => {
const { className, paginationLocalization, onClick, ...rest } = props;

const defaultColumn = useMemo(
() => ({
width: 150,
minWidth: 100
}),
[]
);

const tableInstance = useTable(
{ ...rest, defaultColumn },
useSortBy,
useFlexLayout,
usePagination
);

const {
headerGroups,
prepareRow,
getTableProps,
getTableBodyProps,
page,
state,
gotoPage,
rows
} = tableInstance;

const { pageIndex, pageSize } = state;

return (
<>
<div className={clsx('ui-overflow-auto', className)}>
<table {...getTableProps()} className='ui-shadow-lg ui-w-full'>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<TableHeader
className={clsx(
'ui-text-gray-500 ui-select-none ui-p-4 ui-tg-caption-bold lg:ui-tg-content-bold ui-transition-colors',
column.columnClass || 'ui-text-right',
{
'hover:ui-text-primary': column.canSort
}
)}
{...column.getHeaderProps(column.getSortByToggleProps())}
>
<>
{column.render('Header')}
<span>
{column.isSorted && (
<Icon
className='ui-text-xs ui-ml-1'
name={column.isSortedDesc ? 'Top' : 'Bottom'}
/>
)}
</span>
</>
</TableHeader>
))}
</tr>
))}
</thead>

<tbody {...getTableBodyProps()}>
{page.map((row) => {
prepareRow(row);
return (
<tr
onClick={() => onClick?.(row)}
{...row.getRowProps()}
className={clsx('ui-text-white ui-border-t ui-border-solid ui-border-gray-700', {
'ui-cursor-pointer': !!onClick
})}
>
{row.cells.map((cell) => (
<td
className={clsx(
'ui-p-4 ui-tg-caption lg:ui-tg-content',
cell.column.cellClass || 'ui-text-right'
)}
{...cell.getCellProps()}
>
{cell.render('Cell') as ReactNode}
</td>
))}
</tr>
);
})}
</tbody>
</table>
</div>
<Pagination
gotoPage={gotoPage}
length={rows.length}
localization={paginationLocalization}
pageIndex={pageIndex}
pageSize={pageSize}
/>
</>
);
};

interface HeaderProps extends TableHeaderProps {
children: React.ReactNode;
}

const TableHeader: React.FC<HeaderProps> = ({ children, ...props }) => {
return <th {...props}>{children}</th>;
};
124 changes: 124 additions & 0 deletions src/types/react-table-config.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/* eslint-disable */
import {
UseColumnOrderInstanceProps,
UseColumnOrderState,
UseExpandedHooks,
UseExpandedInstanceProps,
UseExpandedOptions,
UseExpandedRowProps,
UseExpandedState,
UseFiltersColumnOptions,
UseFiltersColumnProps,
UseFiltersInstanceProps,
UseFiltersOptions,
UseFiltersState,
UseGlobalFiltersColumnOptions,
UseGlobalFiltersInstanceProps,
UseGlobalFiltersOptions,
UseGlobalFiltersState,
UseGroupByCellProps,
UseGroupByColumnOptions,
UseGroupByColumnProps,
UseGroupByHooks,
UseGroupByInstanceProps,
UseGroupByOptions,
UseGroupByRowProps,
UseGroupByState,
UsePaginationInstanceProps,
UsePaginationOptions,
UsePaginationState,
UseResizeColumnsColumnOptions,
UseResizeColumnsColumnProps,
UseResizeColumnsOptions,
UseResizeColumnsState,
UseRowSelectHooks,
UseRowSelectInstanceProps,
UseRowSelectOptions,
UseRowSelectRowProps,
UseRowSelectState,
UseRowStateCellProps,
UseRowStateInstanceProps,
UseRowStateOptions,
UseRowStateRowProps,
UseRowStateState,
UseSortByColumnOptions,
UseSortByColumnProps,
UseSortByHooks,
UseSortByInstanceProps,
UseSortByOptions,
UseSortByState
} from "react-table";

declare module "react-table" {
// take this file as-is, or comment out the sections that don't apply to your plugin configuration

export interface TableOptions<D extends Record<string, unknown>>
extends UseExpandedOptions<D>,
UseFiltersOptions<D>,
UseGlobalFiltersOptions<D>,
UseGroupByOptions<D>,
UsePaginationOptions<D>,
UseResizeColumnsOptions<D>,
UseRowSelectOptions<D>,
UseRowStateOptions<D>,
UseSortByOptions<D>,
// note that having Record here allows you to add anything to the options, this matches the spirit of the
// underlying js library, but might be cleaner if it's replaced by a more specific type that matches your
// feature set, this is a safe default.
Record<string, any> {}

export interface Hooks<D extends Record<string, unknown> = Record<string, unknown>>
extends UseExpandedHooks<D>,
UseGroupByHooks<D>,
UseRowSelectHooks<D>,
UseSortByHooks<D> {}

export interface TableInstance<D extends Record<string, unknown> = Record<string, unknown>>
extends UseColumnOrderInstanceProps<D>,
UseExpandedInstanceProps<D>,
UseFiltersInstanceProps<D>,
UseGlobalFiltersInstanceProps<D>,
UseGroupByInstanceProps<D>,
UsePaginationInstanceProps<D>,
UseRowSelectInstanceProps<D>,
UseRowStateInstanceProps<D>,
UseSortByInstanceProps<D> {}

export interface TableState<D extends Record<string, unknown> = Record<string, unknown>>
extends UseColumnOrderState<D>,
UseExpandedState<D>,
UseFiltersState<D>,
UseGlobalFiltersState<D>,
UseGroupByState<D>,
UsePaginationState<D>,
UseResizeColumnsState<D>,
UseRowSelectState<D>,
UseRowStateState<D>,
UseSortByState<D> {}

export interface ColumnInterface<D extends Record<string, unknown> = Record<string, unknown>>
extends UseFiltersColumnOptions<D>,
UseGlobalFiltersColumnOptions<D>,
UseGroupByColumnOptions<D>,
UseResizeColumnsColumnOptions<D>,
UseSortByColumnOptions<D> {
cellClass?: string;
columnClass?: string;
}

export interface ColumnInstance<D extends Record<string, unknown> = Record<string, unknown>>
extends UseFiltersColumnProps<D>,
UseGroupByColumnProps<D>,
UseResizeColumnsColumnProps<D>,
UseSortByColumnProps<D> {}

export interface Cell<D extends Record<string, unknown> = Record<string, unknown>, V = any>
extends UseGroupByCellProps<D>,
UseRowStateCellProps<D> {}

export interface Row<D extends Record<string, unknown> = Record<string, unknown>>
extends UseExpandedRowProps<D>,
UseGroupByRowProps<D>,
UseRowSelectRowProps<D>,
UseRowStateRowProps<D> {}
}
Loading

0 comments on commit 033c606

Please sign in to comment.