A few HOC examples for react-table.

Not really integrated with the whole codesandbox.io approach.
This commit is contained in:
Gary Menzel
2017-11-12 16:32:29 +11:00
parent cb0000e588
commit 710891f0c0
8 changed files with 6945 additions and 3 deletions

6502
docs/public/au_500_tree.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -7,8 +7,17 @@ import '../../react-table.css'
import Readme from './stories/Readme.js'
import { TreeTable, CheckboxTable } from './examples/index'
const exampleStories = [
// examples
{ name: 'TreeTable', component: TreeTable },
{ name: 'CheckboxTable', component: CheckboxTable },
];
const stories = [
{ name: 'Readme', component: Readme },
{ name: 'Simple Table', component: CodeSandbox('X6npLXPRW') },
{
name: 'Cell Renderers & Custom Components',
@@ -51,6 +60,10 @@ const stories = [
name: 'Multiple Pagers (Top and Bottom)',
component: CodeSandbox('VEZ8OgvX'),
},
// other examples
...exampleStories,
]
export default class App extends React.Component {
@@ -63,7 +76,7 @@ export default class App extends React.Component {
height: '100%',
}}
pathPrefix='story/'
StoryWrapper={props => (
StoryWrapper={props =>
<defaultProps.StoryWrapper
css={{
padding: 0,
@@ -95,8 +108,7 @@ export default class App extends React.Component {
position: 'relative',
}}
/>
</defaultProps.StoryWrapper>
)}
</defaultProps.StoryWrapper>}
stories={stories}
/>
)

View File

@@ -0,0 +1,88 @@
import React from 'react';
export default (Component) => {
const wrapper = class RTCheckboxTable extends React.Component {
// we only need a Component so we can get the 'ref' - pure components can't get a 'ref'
rowSelector = (row) =>
{
if(!row || !row.hasOwnProperty(this.props.keyField)) return null;
const checked = this.props.isSelected(row[this.props.keyField]);
return (
<input
type='checkbox'
checked={checked}
onClick={(e)=>{
const { shiftKey } = e;
e.stopPropagation();
this.props.toggleSelection(row[this.props.keyField],shiftKey,row);
}}
onChange={()=>{}}
value=''
/>
);
}
headSelector = (row) =>
{
const checked = this.props.selectAll;
return (
<input
type='checkbox'
checked={checked}
onClick={(e)=>{
e.stopPropagation();
this.props.toggleAll();
}}
onChange={()=>{}}
value=''
/>
);
}
// this is so we can expose the underlying ReactTable to get at the sortedData for selectAll
getWrappedInstance = ()=>this.wrappedInstance
render()
{
const { columns:originalCols, isSelected, toggleSelection, toggleAll, keyField, selectAll, ...rest } = this.props;
const { rowSelector, headSelector, } = this;
const select = {
id: '_selector',
accessor: ()=>'x', // this value is not important
Header: headSelector,
Cell: (ci) => { return rowSelector(ci.original); },
width: 30,
filterable: false,
sortable: false,
resizable: false,
style: { textAlign: 'center' },
}
const columns = [
select,
...originalCols,
];
const extra = {
columns,
};
return (
<Component {...rest} {...extra} ref={(r)=>this.wrappedInstance=r}/>
)
}
}
wrapper.displayName = 'RTCheckboxTable';
wrapper.defaultProps =
{
keyField: '_id',
isSelected: (key)=>{ console.log('No isSelected handler provided:',{key})},
selectAll: false,
toggleSelection: (key, shift, row)=>{ console.log('No toggleSelection handler provided:', { key, shift, row }) },
toggleAll: () => { console.log('No toggleAll handler provided.') },
}
return wrapper;
}

View File

@@ -0,0 +1,160 @@
import React from 'react';
import shortid from 'shortid';
import ReactTable from '../../../../lib/index'
import '../../../../react-table.css'
import checkboxTableHOC from './checkboxHOC';
const CheckboxTable = checkboxTableHOC(ReactTable);
async function getData()
{
const result = await ( await fetch('/au_500_tree.json') ).json();
// we are adding a unique ID to the data for tracking the selected records
return result.map((item)=>{
const _id = shortid.generate();
return {
_id,
...item,
}
});
}
function getColumns(data)
{
const columns = [];
const sample = data[0];
for(let key in sample)
{
if(key==='_id') continue;
columns.push({
accessor: key,
Header: key,
})
}
return columns;
}
export class ComponentTest extends React.Component {
constructor(props) {
super(props);
this.state =
{
data: null,
columns: null,
selection: [],
selectAll: false,
};
}
componentDidMount()
{
getData().then((data)=>{
const columns = getColumns(data);
this.setState({ data, columns });
});
}
toggleSelection = (key,shift,row) => {
/*
Implementation of how to manage the selection state is up to the developer.
This implementation uses an array stored in the component state.
Other implementations could use object keys, a Javascript Set, or Redux... etc.
*/
// start off with the existing state
let selection = [
...this.state.selection
];
const keyIndex = selection.indexOf(key);
// check to see if the key exists
if(keyIndex>=0) {
// it does exist so we will remove it using destructing
selection = [
...selection.slice(0,keyIndex),
...selection.slice(keyIndex+1)
]
} else {
// it does not exist so add it
selection.push(key);
}
// update the state
this.setState({selection});
}
toggleAll = () => {
/*
'toggleAll' is a tricky concept with any filterable table
do you just select ALL the records that are in your data?
OR
do you only select ALL the records that are in the current filtered data?
The latter makes more sense because 'selection' is a visual thing for the user.
This is especially true if you are going to implement a set of external functions
that act on the selected information (you would not want to DELETE the wrong thing!).
So, to that end, access to the internals of ReactTable are required to get what is
currently visible in the table (either on the current page or any other page).
The HOC provides a method call 'getWrappedInstance' to get a ref to the wrapped
ReactTable and then get the internal state and the 'sortedData'.
That can then be iterrated to get all the currently visible records and set
the selection state.
*/
const selectAll = this.state.selectAll?false:true;
const selection = [];
if(selectAll)
{
// we need to get at the internals of ReactTable
const wrappedInstance = this.checkboxTable.getWrappedInstance();
// the 'sortedData' property contains the currently accessible records based on the filter and sort
const currentRecords = wrappedInstance.getResolvedState().sortedData;
// we just push all the IDs onto the selection array
currentRecords.forEach((item)=>{
selection.push(item._original._id);
})
}
this.setState({selectAll,selection})
}
isSelected = (key) => {
/*
Instead of passing our external selection state we provide an 'isSelected'
callback and detect the selection state ourselves. This allows any implementation
for selection (either an array, object keys, or even a Javascript Set object).
*/
return this.state.selection.includes(key);
}
logSelection = () => {
console.log('selection:',this.state.selection);
}
render(){
const { toggleSelection, toggleAll, isSelected, logSelection } = this;
const { data, columns, selectAll } = this.state;
const extraProps =
{
selectAll,
isSelected,
toggleAll,
toggleSelection,
}
return (
<div style={{ padding: '10px'}}>
<h1>react-table - Checkbox Table</h1>
<button onClick={logSelection}>Log Selection to Console</button>
{` (${this.state.selection.length}) selected`}
{
data?
<CheckboxTable
data={data}
columns={columns}
ref={(r)=>this.checkboxTable = r}
className="-striped -highlight"
{...extraProps}
/>
:null
}
</div>
);
}
}
// export default treeTableHOC(ComponentTest);
export default ComponentTest;

View File

@@ -0,0 +1,8 @@
import TreeTable from './treetable';
import CheckboxTable from './checkbox';
export {
TreeTable,
CheckboxTable,
}

View File

@@ -0,0 +1,80 @@
import React from 'react';
import ReactTable from '../../../../lib/index'
import '../../../../react-table.css'
import treeTableHOC from './treeTableHOC';
async function getData()
{
const result = await ( await fetch('/au_500_tree.json') ).json();
return result;
}
function getColumns(data,pivotBy)
{
const columns = [];
const sample = data[0];
for(let key in sample)
{
columns.push({
accessor: key,
Header: key,
})
}
return columns;
}
export class ComponentTest extends React.Component {
constructor(props) {
super(props);
this.state =
{
data: null,
columns: null,
pivotBy: null, // ["firstName", "lastName"],
};
}
componentDidMount()
{
getData().then((data)=>{
// console.log('cwm data:',data);
const pivotBy = ['state','post','city'];
const columns = getColumns(data,pivotBy);
// console.log('cwm cols:',columns);
this.setState({data,columns,pivotBy});
});
}
showState = ()=>
{
console.log('state:',this.reactTable.getResolvedState());
}
render(){
const { data, columns, pivotBy } = this.state;
const extraProps =
{
data,
columns,
pivotBy,
}
const TreeTable = treeTableHOC(ReactTable);
return (
<div style={{ padding: '10px'}}>
<h1>react-table - Tree Table</h1>
{
data?
<TreeTable
ref={(r)=>this.reactTable=r}
className="-striped -highlight"
defaultPageSize={5}
{...extraProps}
/>
:null
}
</div>
);
}
}
export default ComponentTest;

View File

@@ -0,0 +1,58 @@
import React from 'react';
export default (Component) => {
const wrapper = (componentProps) => {
const TrComponent = (props) => {
const { ri, ...rest } = props;
if(ri && ri.groupedByPivot) {
const cell = props.children[ri.level];
cell.props.style.flex = 'unset';
cell.props.style.width = '100%';
cell.props.style.maxWidth = 'unset';
cell.props.style.paddingLeft = `${componentProps.treeTableIndent*ri.level}px`;
cell.props.style.backgroundColor = '#DDD';
cell.props.style.borderBottom = '1px solid rgba(128,128,128,0.2)';
return <div {...rest}>{cell}</div>;
}
return <Component.defaultProps.TrComponent {...rest} />;
}
const getTrProps = (state,ri,ci,instance) => {
return {ri};
}
const { columns, ...rest } = componentProps;
const extra = {
columns: columns.map((col)=>{
let column = col;
if(rest.pivotBy && rest.pivotBy.includes(col.accessor))
{
column = {
accessor: col.accessor,
width: `${componentProps.treeTableIndent}px`,
show: false,
Header: '',
}
}
return column;
}),
TrComponent,
getTrProps,
};
return (
<Component {...rest} {...extra} />
)
}
wrapper.displayName = 'RTTreeTable';
wrapper.defaultProps =
{
treeTableRowBackground: '#EEE',
treeTableIndent: 10,
}
return wrapper;
}

View File

@@ -0,0 +1,34 @@
import namor from "namor";
const range = len => {
const arr = [];
for (let i = 0; i < len; i++) {
arr.push(i);
}
return arr;
};
const newPerson = () => {
const statusChance = Math.random();
return {
firstName: namor.generate({ words: 1, numbers: 0 }),
lastName: namor.generate({ words: 1, numbers: 0 }),
age: Math.floor(Math.random() * 30),
visits: Math.floor(Math.random() * 100),
progress: Math.floor(Math.random() * 100),
status:
statusChance > 0.66
? "relationship"
: statusChance > 0.33 ? "complicated" : "single"
};
};
export function makeData(len = 5553) {
return range(len).map(d => {
return {
...newPerson(),
children: range(10).map(newPerson)
};
});
}