Created
May 26, 2020 16:06
-
-
Save ianwremmel/7572fde73db1144aa42df39e1736a4e6 to your computer and use it in GitHub Desktop.
Declarative React Table
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import {Account} from '../../../mocks/account'; | |
import {Table} from '../table/table'; | |
export type AccountListProps = { | |
accounts: Account[]; | |
}; | |
export const AccountList = ({accounts}: AccountListProps) => ( | |
<Table | |
data={accounts} | |
// in order to bind ColumnHeader's type-argument, we need to use a render | |
// function rather than passing children | |
setup={({ColumnHeader}) => ( | |
<> | |
<ColumnHeader label="User ID" name="userId" /> | |
<ColumnHeader label="User Name" name="username" /> | |
<ColumnHeader label="Email" name="email" /> | |
</> | |
)} | |
/> | |
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, {useContext} from 'react'; | |
import {TableContext} from './table'; | |
export type SetupProps<T extends object> = { | |
ColumnHeader: React.ComponentType<ColumnHeaderProps<T>>; | |
}; | |
export type ColumnHeaderProps<T extends object> = { | |
name: keyof T; | |
label: string; | |
setup: (props: SetupProps<T>) => JSX.Element; | |
}; | |
export const ColumnHeader = <T extends object>({ | |
label, | |
name, | |
}: ColumnHeaderProps<T>) => { | |
const {addColumn} = useContext(TableContext); | |
addColumn(name, label); | |
return null; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, {useContext} from 'react'; | |
import {useTable} from 'react-table'; | |
import {TableContext} from './table'; | |
export type TableRendererProps<P extends object> = { | |
data: P[]; | |
}; | |
export const TableRenderer = <P extends object>({ | |
data, | |
}: TableRendererProps<P>) => { | |
const {columns} = useContext(TableContext); | |
const { | |
getTableProps, | |
getTableBodyProps, | |
headerGroups, | |
rows, | |
prepareRow, | |
} = useTable({columns, data}); | |
return ( | |
// all of the props getters here include a key, but eslint can't know that. | |
/* eslint-disable react/jsx-key */ | |
<> | |
<table {...getTableProps()} className="table table-striped"> | |
<thead> | |
{headerGroups.map((headerGroup) => ( | |
<tr {...headerGroup.getHeaderGroupProps()}> | |
{headerGroup.headers.map((column) => ( | |
<th {...column.getHeaderProps()}>{column.render('Header')}</th> | |
))} | |
</tr> | |
))} | |
</thead> | |
<tbody {...getTableBodyProps()}> | |
{rows.map((row) => { | |
prepareRow(row); | |
return ( | |
<tr {...row.getRowProps()}> | |
{row.cells.map((cell) => ( | |
<td {...cell.getCellProps()}>{cell.render('Cell')}</td> | |
))} | |
</tr> | |
); | |
})} | |
</tbody> | |
</table> | |
</> | |
/* eslint-enable react/jsx-key */ | |
); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import {Column} from 'react-table'; | |
import {ColumnHeader, ColumnHeaderProps} from './column-header'; | |
import {TableRenderer} from './renderer'; | |
type KeyType = number | string | symbol; | |
interface TableContext { | |
addColumn: (key: string, label: string) => void; | |
columns: Column[]; | |
} | |
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore | |
// @ts-ignore - there's no useful default we can provide | |
export const TableContext = React.createContext<TableContext>(null); | |
export type RenderProps<P extends object> = { | |
ColumnHeader: React.ComponentType<ColumnHeaderProps<P>>; | |
}; | |
export type TableProps<P extends object> = { | |
data: P[]; | |
setup: (props: RenderProps<P>) => JSX.Element; | |
}; | |
export const Table = <P extends object>({ | |
data, | |
setup: render, | |
}: TableProps<P>) => { | |
const columns: Column<P>[] = []; | |
const addColumn = (key: keyof P, label: string) => | |
columns.push({ | |
Header: label, | |
accessor: key, | |
}); | |
return ( | |
<TableContext.Provider | |
value={{ | |
addColumn, | |
columns, | |
}} | |
> | |
{/* This _might_ be cleaner if we pass render to TableRender; if render | |
were invoked in TableRenderer, I think we could call it without shoving it | |
inside JSX */} | |
{render({ColumnHeader})} | |
<TableRenderer data={data} /> | |
</TableContext.Provider> | |
); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment