Skip to content

Commit

Permalink
Adding examples using context (schrodinger#80)
Browse files Browse the repository at this point in the history
Created from the pagination example
Removed the cryptic "end"-state and _dataVersion
Changed _updateData() to refresh() to better represent the function's purpose
Moved HOC into HOC helpers and made the component more extendeable by adding a custom PropType that only checks for the presence of the setCallback function
Fixed a working filtered, paginated example with context
  • Loading branch information
gforge authored and wcjordan committed Nov 28, 2016
1 parent c3fb4c4 commit 4eb5019
Show file tree
Hide file tree
Showing 7 changed files with 514 additions and 15 deletions.
232 changes: 232 additions & 0 deletions examples/ContextExample.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
/**
* Copyright Schrodinger, LLC
*/

"use strict";

import React from 'react';
import { Table, Column, Cell } from 'fixed-data-table-2';
import { DataCtxt, AddFilter } from './helpers/HOC';
import FakeObjectDataListStore from './helpers/FakeObjectDataListStore';
import examplePropTypes from './helpers/examplePropTypes';

const FilterablePagingTable = AddFilter(DataCtxt(Table));

/**
* The PagedData class simulates real paginated data where data is fetched
* as requested in chunks.
*/
class PagedData {
constructor(size = 2000) {
this._dataList = new FakeObjectDataListStore(size);
// When fetching we need to fetch the index missing + additional x-elements.
// This specifies that we add 10% of the total size when fetching, the
// maximum number of data-requests will then be 10.
this._fetchSize = Math.ceil(size / 10);
this._end = 50;
this._pending = false;
this._callbacks = [];
this.runCallbacks = this.runCallbacks.bind(this);
}

/**
* The callbacks are used to trigger events as new data arrives.
*
* In most cases the callback is a method that updates the state, e.g.
* updates a version number without direct impact on the component but that
* will trigger an component refresh/update.
*
* @param callback {function} The fallback function to be called
* @param id {string} The string that identifies the given callback.
* This allows a callback to be overwritten when creating new objects that
* use this data as reference.
* @return void
*/
setCallback(callback, id = 'base') {
const newCallback = { id, fun: callback };

let found = false;
const newCallbacks = [];
for (const cb of this._callbacks) {
if (cb.id === id) {
found = true;
newCallbacks.push(newCallback);
} else {
newCallbacks.push(cb);
}
}

if (!found) {
newCallbacks.push(newCallback);
}

this._callbacks = newCallbacks;
}

/**
* Runs callbacks in the order that they've been added.
*
* The function is triggered when the fetchRange() Promise resolves.
*
* @return {void}
*/
runCallbacks() {
for (const cb of this._callbacks) {
cb.fun();
}
}

getSize() {
return this._dataList.getSize();
}

fetchRange(end) {
if (this._pending) {
return;
}

this._pending = true;
new Promise(resolve => setTimeout(resolve, 1000))
.then(() => {
this._pending = false;
this._end = end;
this.runCallbacks();
});
}

getObjectAt(index) {
if (index >= this._end) {
this.fetchRange(Math.min(this._dataList.getSize(),
index + this._fetchSize));
return null;
}

return this._dataList.getObjectAt(index);
}
}

/**
* The PendingCell allows shallow comparison and avoiding updating
* components that haven't changed, see Reacts performance post:
* https://facebook.github.io/react/docs/optimizing-performance.html
*/
class PendingCell extends React.PureComponent {
render() {
const { data, rowIndex, columnKey, dataVersion, ...props } = this.props;
const rowObject = data.getObjectAt(rowIndex);
return (
<Cell {...props}>
{rowObject ? rowObject[columnKey] : 'pending'}
</Cell>
);
}
}

/**
* A cell that is aware of its context
*
* This cell is aware of its context and retrieves the data and its version
* before passing it on to an ordinary cell.
*
* @param {object} props Standard props
* @param {object} data A data object with getObjectAt() defined
* @param {number} version A number indicating the current version of the displayed data
*/
const PagedCell = (props, { data, version }) => (
<PendingCell
data={data}
dataVersion={version}
{...props}
/>);

PagedCell.contextTypes = {
data: examplePropTypes.CtxtDataListStore,
version: React.PropTypes.number,
};

class ContextExample extends React.Component {
constructor(props) {
super(props);

this.state = {
data: new PagedData(2000),
filters: {
firstName: '',
lastName: '',
},
};

this._onFilterChange = this._onFilterChange.bind(this);
}

_onFilterChange(name, value) {
const filters = this.state.filters;
filters[name] = value;
this.setState({
filters,
});
}

render() {
const { data, filters } = this.state;

return (
<div>
<strong>Filter by:</strong>&nbsp;
<input
onChange={e => this._onFilterChange('firstName', e.target.value)}
placeholder="First Name"
/>&nbsp;
<input
onChange={e => this._onFilterChange('lastName', e.target.value)}
placeholder="Last Name"
/>
<br />
<FilterablePagingTable
rowHeight={50}
data={data}
filters={filters}
headerHeight={50}
width={1000}
height={500}
{...this.props}
>
<Column
columnKey="firstName"
header={<Cell>First</Cell>}
cell={<PagedCell />}
fixed={true}
width={100}
/>
<Column
columnKey="lastName"
header={<Cell>Last Name</Cell>}
cell={<PagedCell />}
fixed={true}
width={100}
/>
<Column
columnKey="city"
header={<Cell>City</Cell>}
cell={<PagedCell />}
width={100}
/>
<Column
columnKey="street"
header={<Cell>Street</Cell>}
cell={<PagedCell />}
width={200}
/>
<Column
columnKey="zipCode"
header={<Cell>Zip Code</Cell>}
cell={<PagedCell />}
width={200}
/>
</FilterablePagingTable>
</div>
);
}
}

module.exports = ContextExample;
Loading

0 comments on commit 4eb5019

Please sign in to comment.