Skip to content

Commit 0e343ca

Browse files
filter box MVP
1 parent f1122fc commit 0e343ca

7 files changed

+206
-65
lines changed

spec/pivot_spec.js

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ const raggedFixtureData = [
2525
describe(" utils", function() {
2626

2727
describe(".PivotData()", function() {
28-
const aggregator = utils.aggregators["Sum over Sum"](["a","b"]);
2928

3029
describe("with no options", function() {
3130
const aoaInput = [ ["a","b"], [1,2], [3,4] ];
@@ -39,7 +38,8 @@ describe(" utils", function() {
3938

4039
describe("with array-of-array input", function() {
4140
const aoaInput = [ ["a","b"], [1,2], [3,4] ];
42-
const pd = new utils.PivotData({data: aoaInput, aggregator});
41+
const pd = new utils.PivotData({
42+
data: aoaInput, aggregatorName: "Sum over Sum", vals: ["a","b"]});
4343

4444
it("has the correct grand total value", () =>
4545
expect(pd.getAggregator([],[]).value())
@@ -49,7 +49,8 @@ describe(" utils", function() {
4949

5050
describe("with array-of-object input", function() {
5151
const aosInput = [ {a:1, b:2}, {a:3, b:4} ];
52-
const pd = new utils.PivotData({data: aosInput, aggregator});
52+
const pd = new utils.PivotData({
53+
data: aosInput, aggregatorName: "Sum over Sum", vals: ["a","b"]});
5354

5455
it("has the correct grand total value", () =>
5556
expect(pd.getAggregator([],[]).value())
@@ -59,7 +60,8 @@ describe(" utils", function() {
5960

6061
describe("with ragged array-of-object input", function() {
6162
const raggedAosInput = [ {a:1}, {b:4}, {a: 3, b: 2} ];
62-
const pd = new utils.PivotData({data: raggedAosInput, aggregator});
63+
const pd = new utils.PivotData({
64+
data: raggedAosInput, aggregatorName: "Sum over Sum", vals: ["a","b"]});
6365

6466
it("has the correct grand total value", () =>
6567
expect(pd.getAggregator([],[]).value())
@@ -72,7 +74,8 @@ describe(" utils", function() {
7274
record({a:1, b:2});
7375
record({a:3, b:4});
7476
};
75-
const pd = new utils.PivotData({data: functionInput, aggregator});
77+
const pd = new utils.PivotData({
78+
data: functionInput, aggregatorName: "Sum over Sum", vals: ["a","b"]});
7679

7780
it("has the correct grand total value", () =>
7881
expect(pd.getAggregator([],[]).value())
@@ -142,125 +145,132 @@ describe(" utils", function() {
142145

143146
describe(".aggregatorTemplates", function() {
144147

145-
const getVal = (agg) => new utils.PivotData({data: fixtureData, aggregator: agg}).getAggregator([],[]).value();
148+
const getVal = (agg, vals) => {
149+
return new utils.PivotData({
150+
data: fixtureData,
151+
aggregators: {agg},
152+
aggregatorName: "agg",
153+
vals
154+
}).getAggregator([],[]).value();
155+
}
146156
const tpl = utils.aggregatorTemplates;
147157

148158
describe(".count", () =>
149159
it("works", () =>
150-
expect(getVal(tpl.count()()))
160+
expect(getVal(tpl.count(), []))
151161
.toBe(4)
152162
)
153163
);
154164

155165
describe(".countUnique", () =>
156166
it("works", () =>
157-
expect(getVal(tpl.countUnique()(['gender'])))
167+
expect(getVal(tpl.countUnique(), ['gender']))
158168
.toBe(2)
159169
)
160170
);
161171

162172
describe(".listUnique", () =>
163173
it("works", () =>
164-
expect(getVal(tpl.listUnique()(['gender'])))
174+
expect(getVal(tpl.listUnique(), ['gender']))
165175
.toBe('male,female')
166176
)
167177
);
168178

169179
describe(".average", () =>
170180
it("works", () =>
171-
expect(getVal(tpl.average()(['trials'])))
181+
expect(getVal(tpl.average(), ['trials']))
172182
.toBe(103)
173183
)
174184
);
175185

176186
describe(".sum", () =>
177187
it("works", () =>
178-
expect(getVal(tpl.sum()(['trials'])))
188+
expect(getVal(tpl.sum(), ['trials']))
179189
.toBe(412)
180190
)
181191
);
182192

183193
describe(".min", () =>
184194
it("works", () =>
185-
expect(getVal(tpl.min()(['trials'])))
195+
expect(getVal(tpl.min(), ['trials']))
186196
.toBe(95)
187197
)
188198
);
189199

190200
describe(".max", () =>
191201
it("works", () =>
192-
expect(getVal(tpl.max()(['trials'])))
202+
expect(getVal(tpl.max(), ['trials']))
193203
.toBe(112)
194204
)
195205
);
196206

197207
describe(".first", () =>
198208
it("works", () =>
199-
expect(getVal(tpl.first()(['name'])))
209+
expect(getVal(tpl.first(), ['name']))
200210
.toBe('Carol')
201211
)
202212
);
203213

204214
describe(".last", () =>
205215
it("works", () =>
206-
expect(getVal(tpl.last()(['name'])))
216+
expect(getVal(tpl.last(), ['name']))
207217
.toBe('Nick')
208218
)
209219
);
210220

211221
describe(".average", () =>
212222
it("works", () =>
213-
expect(getVal(tpl.average()(['trials'])))
223+
expect(getVal(tpl.average(), ['trials']))
214224
.toBe(103)
215225
)
216226
);
217227

218228
describe(".median", () =>
219229
it("works", () =>
220-
expect(getVal(tpl.median()(['trials'])))
230+
expect(getVal(tpl.median(), ['trials']))
221231
.toBe(102.5)
222232
)
223233
);
224234

225235
describe(".quantile", () =>
226236
it("works", function() {
227-
expect(getVal(tpl.quantile(0)(['trials'])))
237+
expect(getVal(tpl.quantile(0), ['trials']))
228238
.toBe(95);
229-
expect(getVal(tpl.quantile(0.1)(['trials'])))
239+
expect(getVal(tpl.quantile(0.1), ['trials']))
230240
.toBe(98.5);
231-
expect(getVal(tpl.quantile(0.25)(['trials'])))
241+
expect(getVal(tpl.quantile(0.25), ['trials']))
232242
.toBe(98.5);
233-
expect(getVal(tpl.quantile(1/3)(['trials'])))
243+
expect(getVal(tpl.quantile(1/3), ['trials']))
234244
.toBe(102);
235-
expect(getVal(tpl.quantile(1)(['trials'])))
245+
expect(getVal(tpl.quantile(1), ['trials']))
236246
.toBe(112);
237247
})
238248
);
239249

240250
describe(".var", () =>
241251
it("works", () =>
242-
expect(getVal(tpl.var()(['trials'])))
252+
expect(getVal(tpl.var(), ['trials']))
243253
.toBe(48.666666666666686)
244254
)
245255
);
246256

247257
describe(".stdev", () =>
248258
it("works", () =>
249-
expect(getVal(tpl.stdev()(['trials'])))
259+
expect(getVal(tpl.stdev(), ['trials']))
250260
.toBe(6.976149845485451)
251261
)
252262
);
253263

254264
describe(".sumOverSum", () =>
255265
it("works", () =>
256-
expect(getVal(tpl.sumOverSum()(['successes', 'trials'])))
266+
expect(getVal(tpl.sumOverSum(), ['successes', 'trials']))
257267
.toBe((12+25+30+14)/(95+102+103+112))
258268
)
259269
);
260270

261271
describe(".fractionOf", () =>
262272
it("works", () =>
263-
expect(getVal(tpl.fractionOf(tpl.sum())(['trials'])))
273+
expect(getVal(tpl.fractionOf(tpl.sum()), ['trials']))
264274
.toBe(1)
265275
)
266276
);

src/DnDCell.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,35 @@ class DnDCell extends React.Component {
77
render() {
88
return <Sortable
99
options={{
10-
group: 'shared',
11-
ghostClass: 'pvtPlaceholder'
10+
group: 'shared', ghostClass: 'pvtPlaceholder', filter: '.pvtFilterBox',
11+
preventOnFilter: false
1212
}}
1313
tag="td"
1414
className={this.props.classes}
1515
onChange={this.props.onChange}
1616
>
17-
{this.props.items.map(x => <DraggableAttribute name={x} key={x} />)}
17+
{this.props.items.map(x => <DraggableAttribute name={x} key={x}
18+
attrValues={this.props.attrValues[x]}
19+
valueFilter={this.props.valueFilter[x]}
20+
addValueToFilter={this.props.addValueToFilter}
21+
removeValueFromFilter={this.props.removeValueFromFilter}
22+
/>)}
1823
</Sortable>;
1924
}
2025
}
2126

27+
DnDCell.defaultProps = {
28+
valueFilter: {}, attrValues: {}
29+
};
30+
2231
DnDCell.propTypes = {
2332
items: PropTypes.arrayOf(PropTypes.string).isRequired,
2433
classes: PropTypes.string.isRequired,
25-
onChange: PropTypes.func.isRequired
34+
onChange: PropTypes.func.isRequired,
35+
addValueToFilter: PropTypes.func.isRequired,
36+
removeValueFromFilter: PropTypes.func.isRequired,
37+
attrValues: PropTypes.objectOf(PropTypes.objectOf(PropTypes.number)),
38+
valueFilter: PropTypes.objectOf(PropTypes.objectOf(PropTypes.bool))
2639
};
2740

2841
export default DnDCell;

src/DraggableAttribute.js

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,81 @@ import React from 'react';
22
import PropTypes from 'prop-types';
33

44
class DraggableAttribute extends React.Component {
5+
constructor(props) {
6+
super(props);
7+
this.state = {open: false, top: 0, left: 0, filterText: ''};
8+
}
9+
10+
onCheckboxChange(value, checked) {
11+
if (checked) {
12+
this.props.removeValueFromFilter(this.props.name, value);
13+
}
14+
else {
15+
this.props.addValueToFilter(this.props.name, value);
16+
}
17+
}
18+
19+
getFilterBox() {
20+
return (
21+
<div className="pvtFilterBox" style={{
22+
display: 'block', cursor: 'initial',
23+
top: this.state.top + 'px', left: this.state.left + 'px'}}
24+
>
25+
<a onClick={() => this.setState({open: false})}
26+
style={{position: 'absolute', right: '5px', top: '5px', fontSize: '18px', cursor: 'pointer'}}
27+
>×</a>
28+
<h4>{this.props.name}</h4>
29+
<p>
30+
<input type="text" placeholder="Filter values" className="pvtSearch"
31+
value={this.state.filterText}
32+
onChange={e => this.setState({filterText: e.target.value})}
33+
/>
34+
</p>
35+
36+
<div className="pvtCheckContainer">
37+
{Object.keys(this.props.attrValues)
38+
.filter(x => x.toLowerCase().trim().includes(this.state.filterText.toLowerCase().trim()))
39+
.map(x =>
40+
<p key={x}>
41+
<label>
42+
<input type="checkbox"
43+
onChange={e => this.onCheckboxChange(x, e.target.checked)}
44+
checked={!(x in this.props.valueFilter)}
45+
/>
46+
{x}
47+
</label>
48+
</p>)}
49+
</div>
50+
</div>);
51+
}
52+
553
render() {
54+
const filtered = Object.keys(this.props.valueFilter).length !== 0 ? 'pvtFilteredAttribute' : '';
655
return <li data-id={this.props.name}>
7-
<span className="pvtAttr">{this.props.name}</span>
56+
<span className={'pvtAttr ' + filtered}>
57+
{this.props.name}
58+
<span className="pvtTriangle"
59+
onClick={e => this.setState({open: !this.state.open, top: e.clientY, left: e.clientX})}
60+
></span>
61+
</span>
62+
63+
{this.state.open ? this.getFilterBox() : null}
64+
865
</li>;
966
}
1067
}
1168

69+
70+
DraggableAttribute.defaultProps = {
71+
valueFilter: {}
72+
};
73+
1274
DraggableAttribute.propTypes = {
13-
name: PropTypes.string.isRequired
75+
name: PropTypes.string.isRequired,
76+
addValueToFilter: PropTypes.func.isRequired,
77+
removeValueFromFilter: PropTypes.func.isRequired,
78+
attrValues: PropTypes.objectOf(PropTypes.number).isRequired,
79+
valueFilter: PropTypes.objectOf(PropTypes.bool)
1480
};
1581

1682
export default DraggableAttribute;

0 commit comments

Comments
 (0)