Skip to content

Commit ac74dfb

Browse files
author
vvo
committed
feat: MutlipleChoiceList second pass
New options: - sortBy - cssClass - limit - operator options Also it DOES refine now. There's some funny code inside the click handle on the LI.
1 parent 563b243 commit ac74dfb

File tree

7 files changed

+137
-15
lines changed

7 files changed

+137
-15
lines changed

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,37 @@ search.addWidget(
143143
);
144144
```
145145

146+
### multipleChoiceList
147+
148+
#### API
149+
150+
```js
151+
/**
152+
* Instantiate a list of refinement based on a facet
153+
* @param {String|DOMElement} options.container Valid CSS Selector as a string or DOMElement
154+
* @param {String} options.facetName Name of the attribute for faceting
155+
* @param {String} options.operator How to apply refinements. Possible values: `or`, `and`
156+
* @param {String[]} [options.sortBy=['count:desc']] How to sort refinements. Possible values: `count|isRefined|name:asc|desc`
157+
* @param {String} [options.limit=100] How much facet values to get.
158+
* @param {String|String[]} [options.cssClass=null] CSS class(es) for the main `<ul>` wrapper.
159+
* @param {String|Function} [options.template] Item template, provided with `name`, `count`, `isRefined`
160+
* @return {Object}
161+
*/
162+
```
163+
164+
165+
#### Usage
166+
167+
```html
168+
<div id="brands"></div>
169+
```
170+
171+
```js
172+
search.addWidget(
173+
instantsearch.widgets.multipleChoiceList({
174+
container: '#brands',
175+
facetName: 'brands',
176+
operator: 'or'
177+
})
178+
);
179+
```

components/MultipleChoiceList/index.js

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,74 @@ var React = require('react');
33
var Template = require('../Template/');
44

55
class MultipleChoiceList extends React.Component {
6+
refine(value) {
7+
this.props.toggleRefine(value);
8+
this.props.search();
9+
}
10+
11+
// Click events on DOM tree like LABEL > INPUT will result in two click events
12+
// instead of one. No matter the framework: see
13+
// a label, you will get two click events instead of one.
14+
// No matter the framework, see https://www.google.com/search?q=click+label+twice
15+
//
16+
// Thus making it hard to distinguish activation from deactivation because both click events
17+
// are very close. Debounce is a solution but hacky.
18+
//
19+
// So the code here checks if the click was done on or in a LABEL. If this LABEL
20+
// has a checkbox inside, we ignore the first click event because we will get another one.
21+
handleClick(value, e) {
22+
if (e.target.tagName === 'INPUT') {
23+
this.refine(value);
24+
return;
25+
}
26+
27+
var parent = e.target;
28+
var checkboxInLabel = false;
29+
30+
while (parent !== e.currentTarget) {
31+
if (parent.tagName === 'LABEL' && parent.querySelector('input[type="checkbox"]')) {
32+
checkboxInLabel = true;
33+
return;
34+
}
35+
36+
parent = parent.parentNode;
37+
}
38+
39+
if (checkboxInLabel === false) {
40+
this.refine(value);
41+
}
42+
}
43+
644
render() {
745
var facetValues = this.props.facetValues;
846
var template = this.props.template;
947

1048
return (
11-
<ul>
12-
{facetValues.map(function(facetValue) {
13-
return <li key={facetValue.name}><Template data={facetValue} template={template} /></li>;
49+
<ul className={this.props.cssClass}>
50+
{facetValues.map(facetValue => {
51+
return (
52+
<li key={facetValue.name} onClick={this.handleClick.bind(this, facetValue.name)}>
53+
<Template data={facetValue} template={template} />
54+
</li>
55+
);
1456
})}
1557
</ul>
1658
);
1759
}
1860
}
1961

2062
MultipleChoiceList.propTypes = {
63+
cssClass: React.PropTypes.oneOfType([
64+
React.PropTypes.string,
65+
React.PropTypes.array
66+
]),
2167
facetValues: React.PropTypes.array,
68+
search: React.PropTypes.func.isRequired,
2269
template: React.PropTypes.oneOfType([
2370
React.PropTypes.string,
2471
React.PropTypes.func
2572
]).isRequired,
26-
toggleRefine: React.PropTypes.func
73+
toggleRefine: React.PropTypes.func.isRequired
2774
};
2875

2976
module.exports = MultipleChoiceList;

example/app.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@ search.addWidget(
4444
search.addWidget(
4545
instantsearch.widgets.multipleChoiceList({
4646
container: '#brands',
47-
facetName: 'brand'
47+
facetName: 'brand',
48+
operator: 'or',
49+
limit: 10,
50+
cssClass: 'nav nav-stacked',
51+
template: require('./templates/or.html')
4852
})
4953
);
5054

example/templates/or.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<label class="checkbox">
2+
<input type="checkbox" value="{{name}}" {{#isRefined}}checked{{/isRefined}} />{{name}} <span class="badge pull-right">{{count}}</span>
3+
</label>

webpack.config.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ module.exports = {
99
module: {
1010
loaders: [{
1111
test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader'
12-
}, {
13-
test: /\.html$/, exclude: /node_modules/, loader: 'raw'
1412
}]
1513
}
1614
};

widgets/multiple-choice-list/index.js

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,61 @@
11
var React = require('react');
2+
var cx = require('classnames');
23

34
var utils = require('../../lib/widgetUtils.js');
4-
var defaultTemplate = require('./template.html');
55

6-
function multipleChoiceList({container = null, facetName = null, template = defaultTemplate}) {
6+
var defaultTemplate = `<label>
7+
<input type="checkbox" value="{{name}}" {{#isRefined}}checked{{/isRefined}} />{{name}} <span>{{count}}</span>
8+
</label>`;
9+
10+
/**
11+
* Instantiate a list of refinement based on a facet
12+
* @param {String|DOMElement} options.container Valid CSS Selector as a string or DOMElement
13+
* @param {String} options.facetName Name of the attribute for faceting
14+
* @param {String} options.operator How to apply refinements. Possible values: `or`, `and`
15+
* @param {String[]} [options.sortBy=['count:desc']] How to sort refinements. Possible values: `count|isRefined|name:asc|desc`
16+
* @param {String} [options.limit=100] How much facet values to get.
17+
* @param {String|String[]} [options.cssClass=null] CSS class(es) for the main `<ul>` wrapper.
18+
* @param {String|Function} [options.template] Item template, provided with `name`, `count`, `isRefined`
19+
* @return {Object}
20+
*/
21+
function multipleChoiceList({
22+
container = null,
23+
facetName = null,
24+
operator = null,
25+
sortBy = ['count:desc'],
26+
limit = 100,
27+
cssClass = null,
28+
template = defaultTemplate
29+
}) {
30+
var usage = 'Usage: multipleChoiceList({container, facetName, operator[sortBy, limit, cssClass, template]})';
31+
732
var MultipleChoiceList = require('../../components/MultipleChoiceList');
833
var containerNode = utils.getContainerNode(container);
934

10-
if (container === null || facetName === null) {
11-
throw new Error('Usage: multipleChoiceList({container, facetName[, template]})');
35+
if (container === null ||
36+
facetName === null ||
37+
operator === null) {
38+
throw new Error(usage);
39+
}
40+
41+
operator = operator.toLowerCase();
42+
if (operator !== 'and' && operator !== 'or') {
43+
throw new Error(usage);
1244
}
1345

1446
return {
15-
getConfiguration: () => ({disjunctiveFacets: [facetName]}),
47+
getConfiguration: () => ({
48+
maxValuesPerFacet: limit,
49+
[operator === 'and' ? 'facets' : 'disjunctiveFacets']: [facetName]
50+
}),
1651
render: function(results, state, helper) {
1752
React.render(
1853
<MultipleChoiceList
19-
facetValues={results.getFacetValues(facetName, {sortBy: ['name:asc']})}
20-
toggleRefine={helper.toggleRefine.bind(helper, facetName)}
54+
cssClass={cx(cssClass)}
55+
facetValues={results.getFacetValues(facetName, {sortBy: sortBy})}
56+
search={helper.search.bind(helper)}
2157
template={template}
58+
toggleRefine={helper.toggleRefine.bind(helper, facetName)}
2259
/>,
2360
containerNode
2461
);

widgets/multiple-choice-list/template.html

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)