Skip to content

Commit 4eb5019

Browse files
gforgewcjordan
authored andcommitted
Adding examples using context (schrodinger#80)
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
1 parent c3fb4c4 commit 4eb5019

File tree

7 files changed

+514
-15
lines changed

7 files changed

+514
-15
lines changed

examples/ContextExample.js

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
/**
2+
* Copyright Schrodinger, LLC
3+
*/
4+
5+
"use strict";
6+
7+
import React from 'react';
8+
import { Table, Column, Cell } from 'fixed-data-table-2';
9+
import { DataCtxt, AddFilter } from './helpers/HOC';
10+
import FakeObjectDataListStore from './helpers/FakeObjectDataListStore';
11+
import examplePropTypes from './helpers/examplePropTypes';
12+
13+
const FilterablePagingTable = AddFilter(DataCtxt(Table));
14+
15+
/**
16+
* The PagedData class simulates real paginated data where data is fetched
17+
* as requested in chunks.
18+
*/
19+
class PagedData {
20+
constructor(size = 2000) {
21+
this._dataList = new FakeObjectDataListStore(size);
22+
// When fetching we need to fetch the index missing + additional x-elements.
23+
// This specifies that we add 10% of the total size when fetching, the
24+
// maximum number of data-requests will then be 10.
25+
this._fetchSize = Math.ceil(size / 10);
26+
this._end = 50;
27+
this._pending = false;
28+
this._callbacks = [];
29+
this.runCallbacks = this.runCallbacks.bind(this);
30+
}
31+
32+
/**
33+
* The callbacks are used to trigger events as new data arrives.
34+
*
35+
* In most cases the callback is a method that updates the state, e.g.
36+
* updates a version number without direct impact on the component but that
37+
* will trigger an component refresh/update.
38+
*
39+
* @param callback {function} The fallback function to be called
40+
* @param id {string} The string that identifies the given callback.
41+
* This allows a callback to be overwritten when creating new objects that
42+
* use this data as reference.
43+
* @return void
44+
*/
45+
setCallback(callback, id = 'base') {
46+
const newCallback = { id, fun: callback };
47+
48+
let found = false;
49+
const newCallbacks = [];
50+
for (const cb of this._callbacks) {
51+
if (cb.id === id) {
52+
found = true;
53+
newCallbacks.push(newCallback);
54+
} else {
55+
newCallbacks.push(cb);
56+
}
57+
}
58+
59+
if (!found) {
60+
newCallbacks.push(newCallback);
61+
}
62+
63+
this._callbacks = newCallbacks;
64+
}
65+
66+
/**
67+
* Runs callbacks in the order that they've been added.
68+
*
69+
* The function is triggered when the fetchRange() Promise resolves.
70+
*
71+
* @return {void}
72+
*/
73+
runCallbacks() {
74+
for (const cb of this._callbacks) {
75+
cb.fun();
76+
}
77+
}
78+
79+
getSize() {
80+
return this._dataList.getSize();
81+
}
82+
83+
fetchRange(end) {
84+
if (this._pending) {
85+
return;
86+
}
87+
88+
this._pending = true;
89+
new Promise(resolve => setTimeout(resolve, 1000))
90+
.then(() => {
91+
this._pending = false;
92+
this._end = end;
93+
this.runCallbacks();
94+
});
95+
}
96+
97+
getObjectAt(index) {
98+
if (index >= this._end) {
99+
this.fetchRange(Math.min(this._dataList.getSize(),
100+
index + this._fetchSize));
101+
return null;
102+
}
103+
104+
return this._dataList.getObjectAt(index);
105+
}
106+
}
107+
108+
/**
109+
* The PendingCell allows shallow comparison and avoiding updating
110+
* components that haven't changed, see Reacts performance post:
111+
* https://facebook.github.io/react/docs/optimizing-performance.html
112+
*/
113+
class PendingCell extends React.PureComponent {
114+
render() {
115+
const { data, rowIndex, columnKey, dataVersion, ...props } = this.props;
116+
const rowObject = data.getObjectAt(rowIndex);
117+
return (
118+
<Cell {...props}>
119+
{rowObject ? rowObject[columnKey] : 'pending'}
120+
</Cell>
121+
);
122+
}
123+
}
124+
125+
/**
126+
* A cell that is aware of its context
127+
*
128+
* This cell is aware of its context and retrieves the data and its version
129+
* before passing it on to an ordinary cell.
130+
*
131+
* @param {object} props Standard props
132+
* @param {object} data A data object with getObjectAt() defined
133+
* @param {number} version A number indicating the current version of the displayed data
134+
*/
135+
const PagedCell = (props, { data, version }) => (
136+
<PendingCell
137+
data={data}
138+
dataVersion={version}
139+
{...props}
140+
/>);
141+
142+
PagedCell.contextTypes = {
143+
data: examplePropTypes.CtxtDataListStore,
144+
version: React.PropTypes.number,
145+
};
146+
147+
class ContextExample extends React.Component {
148+
constructor(props) {
149+
super(props);
150+
151+
this.state = {
152+
data: new PagedData(2000),
153+
filters: {
154+
firstName: '',
155+
lastName: '',
156+
},
157+
};
158+
159+
this._onFilterChange = this._onFilterChange.bind(this);
160+
}
161+
162+
_onFilterChange(name, value) {
163+
const filters = this.state.filters;
164+
filters[name] = value;
165+
this.setState({
166+
filters,
167+
});
168+
}
169+
170+
render() {
171+
const { data, filters } = this.state;
172+
173+
return (
174+
<div>
175+
<strong>Filter by:</strong>&nbsp;
176+
<input
177+
onChange={e => this._onFilterChange('firstName', e.target.value)}
178+
placeholder="First Name"
179+
/>&nbsp;
180+
<input
181+
onChange={e => this._onFilterChange('lastName', e.target.value)}
182+
placeholder="Last Name"
183+
/>
184+
<br />
185+
<FilterablePagingTable
186+
rowHeight={50}
187+
data={data}
188+
filters={filters}
189+
headerHeight={50}
190+
width={1000}
191+
height={500}
192+
{...this.props}
193+
>
194+
<Column
195+
columnKey="firstName"
196+
header={<Cell>First</Cell>}
197+
cell={<PagedCell />}
198+
fixed={true}
199+
width={100}
200+
/>
201+
<Column
202+
columnKey="lastName"
203+
header={<Cell>Last Name</Cell>}
204+
cell={<PagedCell />}
205+
fixed={true}
206+
width={100}
207+
/>
208+
<Column
209+
columnKey="city"
210+
header={<Cell>City</Cell>}
211+
cell={<PagedCell />}
212+
width={100}
213+
/>
214+
<Column
215+
columnKey="street"
216+
header={<Cell>Street</Cell>}
217+
cell={<PagedCell />}
218+
width={200}
219+
/>
220+
<Column
221+
columnKey="zipCode"
222+
header={<Cell>Zip Code</Cell>}
223+
cell={<PagedCell />}
224+
width={200}
225+
/>
226+
</FilterablePagingTable>
227+
</div>
228+
);
229+
}
230+
}
231+
232+
module.exports = ContextExample;

0 commit comments

Comments
 (0)