Skip to content

Commit 6073d94

Browse files
committed
feat(connectors): connectRangeSlider (iteration2)
1 parent 12a7935 commit 6073d94

File tree

4 files changed

+265
-218
lines changed

4 files changed

+265
-218
lines changed

src/connectors/range-slider/__tests__/connectRangeSlider-test.js

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
2-
31
import sinon from 'sinon';
42

53
import jsHelper from 'algoliasearch-helper';
@@ -36,6 +34,7 @@ describe('connectRangeSlider', () => {
3634
state: helper.state,
3735
createURL: () => '#',
3836
onHistoryChange: () => {},
37+
instantSearchInstance: {templatesConfig: undefined},
3938
});
4039

4140
{ // should call the rendering once with isFirstRendering to true
@@ -44,20 +43,16 @@ describe('connectRangeSlider', () => {
4443
expect(isFirstRendering).toBe(true);
4544

4645
// should provide good values for the first rendering
47-
const {range, collapsible, start,
48-
shouldAutoHideContainer, containerNode} = rendering.lastCall.args[0];
46+
const {range, start} = rendering.lastCall.args[0];
4947
expect(range).toEqual({min: 0, max: 0});
5048
expect(start).toEqual([-Infinity, Infinity]);
51-
expect(collapsible).toBe(false);
52-
expect(shouldAutoHideContainer).toBe(true);
53-
expect(containerNode).toBe(container);
5449
}
5550

5651
widget.render({
5752
results: new SearchResults(helper.state, [{
5853
hits: [{test: 'oneTime'}],
5954
facets: {price: {10: 1, 20: 1, 30: 1}},
60-
facets_stats: { // eslint-disable-line
55+
facets_stats: { // eslint-disable-line
6156
price: {
6257
avg: 20,
6358
max: 30,
@@ -80,13 +75,9 @@ describe('connectRangeSlider', () => {
8075
expect(isFirstRendering).toBe(false);
8176

8277
// should provide good values for the first rendering
83-
const {range, collapsible, start,
84-
shouldAutoHideContainer, containerNode} = rendering.lastCall.args[0];
78+
const {range, start} = rendering.lastCall.args[0];
8579
expect(range).toEqual({min: 10, max: 30});
8680
expect(start).toEqual([-Infinity, Infinity]);
87-
expect(collapsible).toBe(false);
88-
expect(shouldAutoHideContainer).toBe(false);
89-
expect(containerNode).toBe(container);
9081
}
9182
});
9283

@@ -158,7 +149,7 @@ describe('connectRangeSlider', () => {
158149
results: new SearchResults(helper.state, [{
159150
hits: [{test: 'oneTime'}],
160151
facets: {price: {10: 1, 20: 1, 30: 1}},
161-
facets_stats: { // eslint-disable-line
152+
facets_stats: { // eslint-disable-line
162153
price: {
163154
avg: 20,
164155
max: 30,
Lines changed: 126 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
1-
import {
2-
bemHelper,
3-
prepareTemplateProps,
4-
getContainerNode,
5-
} from '../../lib/utils.js';
6-
import find from 'lodash/find';
7-
import cx from 'classnames';
8-
9-
const bem = bemHelper('ais-range-slider');
10-
const defaultTemplates = {
11-
header: '',
12-
footer: '',
13-
};
1+
import {checkRendering} from '../../lib/utils.js';
2+
3+
const usage = `Usage:
4+
var customRangeSlider = connectRangeSlider(function render(params, isFirstRendering) {
5+
// params = {
6+
// refine,
7+
// range,
8+
// start,
9+
// instantSearchInstance,
10+
// }
11+
});
12+
search.addWidget(
13+
customRangeSlider({
14+
attributeName,
15+
min,
16+
max,
17+
precision
18+
});
19+
);
20+
Full documentation available at https://community.algolia.com/instantsearch.js/connectors/connectRangeSlider.html
21+
`;
1422

1523
/**
1624
* Instantiate a slider based on a numeric attribute.
@@ -40,165 +48,118 @@ const defaultTemplates = {
4048
* @param {number} [options.max] Maximal slider value, defaults to automatically computed from the result set
4149
* @return {Object}
4250
*/
43-
const usage = `Usage:
44-
rangeSlider({
45-
container,
46-
attributeName,
47-
[ tooltips=true ],
48-
[ templates.{header, footer} ],
49-
[ cssClasses.{root, header, body, footer} ],
50-
[ step=1 ],
51-
[ pips=true ],
52-
[ autoHideContainer=true ],
53-
[ collapsible=false ],
54-
[ min ],
55-
[ max ]
56-
});
57-
`;
58-
const connectRangeSlider = rangeSliderRendering => ({
59-
container,
51+
52+
export default function connectRangeSlider(renderFn) {
53+
checkRendering(renderFn, usage);
54+
55+
return ({
6056
attributeName,
61-
tooltips = true,
62-
templates = defaultTemplates,
63-
collapsible = false,
64-
cssClasses: userCssClasses = {},
65-
step = 1,
66-
pips = true,
67-
autoHideContainer = true,
6857
min: userMin,
6958
max: userMax,
7059
precision = 2,
71-
} = {}) => {
72-
if (!container || !attributeName) {
73-
throw new Error(usage);
74-
}
60+
}) => {
61+
if (!attributeName) {
62+
throw new Error(usage);
63+
}
64+
65+
const formatToNumber = v => Number(Number(v).toFixed(precision));
66+
67+
const sliderFormatter = {
68+
from: v => v,
69+
to: v => formatToNumber(v).toLocaleString(),
70+
};
71+
72+
return {
73+
getConfiguration: originalConf => {
74+
const conf = {
75+
disjunctiveFacets: [attributeName],
76+
};
77+
78+
const hasUserBounds = userMin !== undefined || userMax !== undefined;
79+
const boundsNotAlreadyDefined = !originalConf ||
80+
originalConf.numericRefinements &&
81+
originalConf.numericRefinements[attributeName] === undefined;
82+
83+
if (hasUserBounds && boundsNotAlreadyDefined) {
84+
conf.numericRefinements = {[attributeName]: {}};
85+
if (userMin !== undefined) conf.numericRefinements[attributeName]['>='] = [userMin];
86+
if (userMax !== undefined) conf.numericRefinements[attributeName]['<='] = [userMax];
87+
}
7588

76-
const formatToNumber = v => Number(Number(v).toFixed(precision));
89+
return conf;
90+
},
7791

78-
const sliderFormatter = {
79-
from: v => v,
80-
to: v => formatToNumber(v).toLocaleString(),
81-
};
92+
_getCurrentRefinement(helper) {
93+
let min = helper.state.getNumericRefinement(attributeName, '>=');
94+
let max = helper.state.getNumericRefinement(attributeName, '<=');
8295

83-
const containerNode = getContainerNode(container);
84-
85-
const cssClasses = {
86-
root: cx(bem(null), userCssClasses.root),
87-
header: cx(bem('header'), userCssClasses.header),
88-
body: cx(bem('body'), userCssClasses.body),
89-
footer: cx(bem('footer'), userCssClasses.footer),
90-
};
91-
92-
return {
93-
getConfiguration: originalConf => {
94-
const conf = {
95-
disjunctiveFacets: [attributeName],
96-
};
97-
98-
const hasUserBounds = userMin !== undefined || userMax !== undefined;
99-
const boundsNotAlreadyDefined = !originalConf ||
100-
originalConf.numericRefinements &&
101-
originalConf.numericRefinements[attributeName] === undefined;
102-
103-
if (hasUserBounds && boundsNotAlreadyDefined) {
104-
conf.numericRefinements = {[attributeName]: {}};
105-
if (userMin !== undefined) conf.numericRefinements[attributeName]['>='] = [userMin];
106-
if (userMax !== undefined) conf.numericRefinements[attributeName]['<='] = [userMax];
107-
}
108-
109-
return conf;
110-
},
111-
_getCurrentRefinement(helper) {
112-
let min = helper.state.getNumericRefinement(attributeName, '>=');
113-
let max = helper.state.getNumericRefinement(attributeName, '<=');
114-
115-
if (min && min.length) {
116-
min = min[0];
117-
} else {
118-
min = -Infinity;
119-
}
120-
121-
if (max && max.length) {
122-
max = max[0];
123-
} else {
124-
max = Infinity;
125-
}
126-
127-
return {
128-
min,
129-
max,
130-
};
131-
},
132-
init({helper, templatesConfig}) {
133-
this._templateProps = prepareTemplateProps({
134-
defaultTemplates,
135-
templatesConfig,
136-
templates,
137-
});
138-
this._refine = bounds => newValues => {
139-
helper.clearRefinements(attributeName);
140-
if (!bounds.min || newValues[0] > bounds.min) {
141-
helper.addNumericRefinement(attributeName, '>=', formatToNumber(newValues[0]));
96+
if (min && min.length) {
97+
min = min[0];
98+
} else {
99+
min = -Infinity;
142100
}
143-
if (!bounds.max || newValues[1] < bounds.max) {
144-
helper.addNumericRefinement(attributeName, '<=', formatToNumber(newValues[1]));
101+
102+
if (max && max.length) {
103+
max = max[0];
104+
} else {
105+
max = Infinity;
145106
}
146-
helper.search();
147-
};
148-
149-
const stats = {
150-
min: userMin || null,
151-
max: userMax || null,
152-
};
153-
const currentRefinement = this._getCurrentRefinement(helper);
154-
155-
rangeSliderRendering({
156-
collapsible,
157-
cssClasses,
158-
refine: this._refine(stats),
159-
pips,
160-
range: {min: Math.floor(stats.min), max: Math.ceil(stats.max)},
161-
shouldAutoHideContainer: autoHideContainer && stats.min === stats.max,
162-
start: [currentRefinement.min, currentRefinement.max],
163-
step,
164-
templateProps: this._templateProps,
165-
tooltips,
166-
format: sliderFormatter,
167-
containerNode,
168-
}, true);
169-
},
170-
render({results, helper}) {
171-
const facet = find(results.disjunctiveFacets, {name: attributeName});
172-
const stats = facet !== undefined && facet.stats !== undefined ? facet.stats : {
173-
min: null,
174-
max: null,
175-
};
176-
177-
if (userMin !== undefined) stats.min = userMin;
178-
if (userMax !== undefined) stats.max = userMax;
179-
180-
const currentRefinement = this._getCurrentRefinement(helper);
181-
182-
if (tooltips.format !== undefined) {
183-
tooltips = [{to: tooltips.format}, {to: tooltips.format}];
184-
}
185-
186-
rangeSliderRendering({
187-
collapsible,
188-
cssClasses,
189-
refine: this._refine(stats),
190-
pips,
191-
range: {min: Math.floor(stats.min), max: Math.ceil(stats.max)},
192-
shouldAutoHideContainer: autoHideContainer && stats.min === stats.max,
193-
start: [currentRefinement.min, currentRefinement.max],
194-
step,
195-
templateProps: this._templateProps,
196-
tooltips,
197-
format: sliderFormatter,
198-
containerNode,
199-
}, false);
200-
},
201-
};
202-
};
203107

204-
export default connectRangeSlider;
108+
return {
109+
min,
110+
max,
111+
};
112+
},
113+
114+
init({helper, instantSearchInstance}) {
115+
this._instantSearchInstance = instantSearchInstance;
116+
117+
this._refine = bounds => newValues => {
118+
helper.clearRefinements(attributeName);
119+
if (!bounds.min || newValues[0] > bounds.min) {
120+
helper.addNumericRefinement(attributeName, '>=', formatToNumber(newValues[0]));
121+
}
122+
if (!bounds.max || newValues[1] < bounds.max) {
123+
helper.addNumericRefinement(attributeName, '<=', formatToNumber(newValues[1]));
124+
}
125+
helper.search();
126+
};
127+
128+
const stats = {
129+
min: userMin || null,
130+
max: userMax || null,
131+
};
132+
const currentRefinement = this._getCurrentRefinement(helper);
133+
134+
renderFn({
135+
refine: this._refine(stats),
136+
range: {min: Math.floor(stats.min), max: Math.ceil(stats.max)},
137+
start: [currentRefinement.min, currentRefinement.max],
138+
format: sliderFormatter,
139+
instantSearchInstance: this._instantSearchInstance,
140+
}, true);
141+
},
142+
143+
render({results, helper}) {
144+
const facet = (results.disjunctiveFacets || []).find(({name}) => name === attributeName);
145+
const stats = facet !== undefined && facet.stats !== undefined ? facet.stats : {
146+
min: null,
147+
max: null,
148+
};
149+
150+
if (userMin !== undefined) stats.min = userMin;
151+
if (userMax !== undefined) stats.max = userMax;
152+
153+
const currentRefinement = this._getCurrentRefinement(helper);
154+
155+
renderFn({
156+
refine: this._refine(stats),
157+
range: {min: Math.floor(stats.min), max: Math.ceil(stats.max)},
158+
start: [currentRefinement.min, currentRefinement.max],
159+
format: sliderFormatter,
160+
instantSearchInstance: this._instantSearchInstance,
161+
}, false);
162+
},
163+
};
164+
};
165+
}

0 commit comments

Comments
 (0)