diff --git a/dev/app.js b/dev/app.js index 04c834c0d6..5bc556d963 100644 --- a/dev/app.js +++ b/dev/app.js @@ -277,6 +277,8 @@ search.addWidget( templates: { header: 'Price' }, + min: 100, + max: 500, step: 10, tooltips: { format: function(formattedValue) { diff --git a/src/widgets/range-slider/__tests__/range-slider-test.js b/src/widgets/range-slider/__tests__/range-slider-test.js index dedcaa2a91..902986c48e 100644 --- a/src/widgets/range-slider/__tests__/range-slider-test.js +++ b/src/widgets/range-slider/__tests__/range-slider-test.js @@ -48,7 +48,6 @@ describe('rangeSlider()', () => { rangeSlider.__Rewire__('headerFooterHOC', headerFooter); container = document.createElement('div'); - widget = rangeSlider({container, attributeName: 'aNumAttr', cssClasses: {root: ['root', 'cx']}}); helper = new AlgoliasearchHelper( {search: function() {}}, @@ -58,12 +57,90 @@ describe('rangeSlider()', () => { sinon.spy(helper, 'addNumericRefinement'); sinon.spy(helper, 'clearRefinements'); sinon.spy(helper, 'search'); - widget.init({helper}); + }); + + context('min option', () => { + it('refines when no previous configuration', () => { + widget = rangeSlider({container, attributeName: 'aNumAttr', min: 100}); + expect(widget.getConfiguration()).toEqual({ + disjunctiveFacets: ['aNumAttr'], + numericRefinements: {aNumAttr: {'>=': [100]}} + }); + }); + + it('does not refine when previous configuration', () => { + widget = rangeSlider({container, attributeName: 'aNumAttr', min: 100}); + expect(widget.getConfiguration({numericRefinements: {aNumAttr: {}}})).toEqual({ + disjunctiveFacets: ['aNumAttr'] + }); + }); + + it('works along with max option', () => { + widget = rangeSlider({container, attributeName: 'aNumAttr', min: 100, max: 200}); + expect(widget.getConfiguration()).toEqual({ + disjunctiveFacets: ['aNumAttr'], + numericRefinements: {aNumAttr: {'>=': [100], '<=': [200]}} + }); + }); + + it('sets the right ranges', () => { + results = {}; + widget = rangeSlider({container, attributeName: 'aNumAttr', min: 100, max: 200}); + helper.setState(widget.getConfiguration()); + widget.init({helper}); + widget.render({results, helper}); + let props = { + cssClasses: { + root: 'ais-range-slider', + header: 'ais-range-slider--header', + body: 'ais-range-slider--body', + footer: 'ais-range-slider--footer' + }, + collapsible: false, + onChange: () => {}, + pips: true, + range: {max: 200, min: 100}, + shouldAutoHideContainer: false, + start: [100, 200], + step: 1, + templateProps: { + templates: {footer: '', header: ''}, + templatesConfig: undefined, + transformData: undefined, + useCustomCompileOptions: {footer: false, header: false} + }, + tooltips: true + }; + + expect(ReactDOM.render.calledOnce).toBe(true, 'ReactDOM.render called once'); + expect(autoHideContainer.calledOnce).toBe(true, 'autoHideContainer called once'); + expect(headerFooter.calledOnce).toBe(true, 'headerFooter called once'); + expect(ReactDOM.render.firstCall.args[0]).toEqualJSX(); + }); + }); + + context('max option', () => { + it('refines when no previous configuration', () => { + widget = rangeSlider({container, attributeName: 'aNumAttr', max: 100}); + expect(widget.getConfiguration()).toEqual({ + disjunctiveFacets: ['aNumAttr'], + numericRefinements: {aNumAttr: {'<=': [100]}} + }); + }); + + it('does not refine when previous configuration', () => { + widget = rangeSlider({container, attributeName: 'aNumAttr', max: 100}); + expect(widget.getConfiguration({numericRefinements: {aNumAttr: {}}})).toEqual({ + disjunctiveFacets: ['aNumAttr'] + }); + }); }); context('without result', () => { beforeEach(() => { results = {}; + widget = rangeSlider({container, attributeName: 'aNumAttr', cssClasses: {root: ['root', 'cx']}}); + widget.init({helper}); }); it('calls ReactDOM.render(, container)', () => { @@ -101,6 +178,8 @@ describe('rangeSlider()', () => { context('when rangestats min === stats max', () => { beforeEach(() => { + widget = rangeSlider({container, attributeName: 'aNumAttr', cssClasses: {root: ['root', 'cx']}}); + widget.init({helper}); results = { disjunctiveFacets: [{ name: 'aNumAttr', @@ -150,6 +229,8 @@ describe('rangeSlider()', () => { context('with results', () => { beforeEach(() => { + widget = rangeSlider({container, attributeName: 'aNumAttr', cssClasses: {root: ['root', 'cx']}}); + widget.init({helper}); results = { disjunctiveFacets: [{ name: 'aNumAttr', diff --git a/src/widgets/range-slider/range-slider.js b/src/widgets/range-slider/range-slider.js index c81b8dd9b9..5fd498f7bf 100644 --- a/src/widgets/range-slider/range-slider.js +++ b/src/widgets/range-slider/range-slider.js @@ -38,6 +38,8 @@ let defaultTemplates = { * @param {string|string[]} [options.cssClasses.footer] CSS class to add to the footer element * @param {object|boolean} [options.collapsible=false] Hide the widget body and footer when clicking on header * @param {boolean} [options.collapsible.collapsed] Initial collapsed state of a collapsible widget + * @param {number} [options.min] Minimal slider value, default to automatically computed from the result set + * @param {number} [options.max] Maximal slider value, defaults to automatically computed from the result set * @return {Object} */ const usage = `Usage: @@ -50,7 +52,9 @@ rangeSlider({ [ step=1 ], [ pips=true ], [ autoHideContainer=true ], - [ collapsible=false ] + [ collapsible=false ], + [ min ], + [ max ] }); `; function rangeSlider({ @@ -62,7 +66,9 @@ function rangeSlider({ cssClasses: userCssClasses = {}, step = 1, pips = true, - autoHideContainer = true + autoHideContainer = true, + min: userMin, + max: userMax } = {}) { if (!container || !attributeName) { throw new Error(usage); @@ -82,9 +88,31 @@ function rangeSlider({ }; return { - getConfiguration: () => ({ - disjunctiveFacets: [attributeName] - }), + getConfiguration: (originalConf) => { + const conf = { + disjunctiveFacets: [attributeName] + }; + + if ( + (userMin !== undefined || userMax !== undefined) + && + (!originalConf || + originalConf.numericRefinements && + originalConf.numericRefinements[attributeName] === undefined) + ) { + conf.numericRefinements = {[attributeName]: {}}; + + if (userMin !== undefined) { + conf.numericRefinements[attributeName]['>='] = [userMin]; + } + + if (userMax !== undefined) { + conf.numericRefinements[attributeName]['<='] = [userMax]; + } + } + + return conf; + }, _getCurrentRefinement(helper) { let min = helper.state.getNumericRefinement(attributeName, '>='); let max = helper.state.getNumericRefinement(attributeName, '<='); @@ -125,16 +153,27 @@ function rangeSlider({ }, render({results, helper}) { let facet = find(results.disjunctiveFacets, {name: attributeName}); - let stats = facet !== undefined ? facet.stats : undefined; - let currentRefinement = this._getCurrentRefinement(helper); + let stats; + + if (userMin !== undefined || userMax !== undefined) { + stats = {}; + + if (userMin !== undefined) { + stats.min = userMin; + } - if (stats === undefined) { - stats = { + if (userMax !== undefined) { + stats.max = userMax; + } + } else { + stats = facet !== undefined && facet.stats !== undefined ? facet.stats : { min: null, max: null }; } + let currentRefinement = this._getCurrentRefinement(helper); + if (tooltips.format !== undefined) { tooltips = [{to: tooltips.format}, {to: tooltips.format}]; }