diff --git a/README.md b/README.md
index c74d0423612..6a65a4e3efa 100644
--- a/README.md
+++ b/README.md
@@ -1047,20 +1047,27 @@ search.addWidget(
* Instantiate a price ranges on a numerical facet
* @param {string|DOMElement} options.container Valid CSS Selector as a string or DOMElement
* @param {string} options.facetName Name of the attribute for faceting
- * @param {Object} [options.cssClasses] CSS classes to add to the wrapping elements: root, range
- * @param {string|string[]} [options.cssClasses.root] CSS class to add to the root element
- * @param {string|string[]} [options.cssClasses.header] CSS class to add to the header element
- * @param {string|string[]} [options.cssClasses.body] CSS class to add to the body element
- * @param {string|string[]} [options.cssClasses.footer] CSS class to add to the footer element
- * @param {string|string[]} [options.cssClasses.range] CSS class to add to the range element
- * @param {string|string[]} [options.cssClasses.input] CSS class to add to the min/max input elements
- * @param {string|string[]} [options.cssClasses.button] CSS class to add to the button element
+ * @param {Object} [options.cssClasses] CSS classes to add
+ * @param {string} [options.cssClasses.root] CSS class to add to the root element
+ * @param {string} [options.cssClasses.header] CSS class to add to the header element
+ * @param {string} [options.cssClasses.body] CSS class to add to the body element
+ * @param {string} [options.cssClasses.list] CSS class to add to the wrapping list element
+ * @param {string} [options.cssClasses.item] CSS class to add to each item element
+ * @param {string} [options.cssClasses.active] CSS class to add to the active item element
+ * @param {string} [options.cssClasses.link] CSS class to add to each link element
+ * @param {string} [options.cssClasses.form] CSS class to add to the form element
+ * @param {string} [options.cssClasses.label] CSS class to add to each wrapping label of the form
+ * @param {string} [options.cssClasses.input] CSS class to add to each input of the form
+ * @param {string} [options.cssClasses.currency] CSS class to add to each currency element of the form
+ * @param {string} [options.cssClasses.separator] CSS class to add to the separator of the form
+ * @param {string} [options.cssClasses.button] CSS class to add to the submit button of the form
+ * @param {string} [options.cssClasses.footer] CSS class to add to the footer element
* @param {Object} [options.templates] Templates to use for the widget
- * @param {string|Function} [options.templates.range] Range template
+ * @param {string|Function} [options.templates.item] Item template
* @param {Object} [options.labels] Labels to use for the widget
- * @param {string|Function} [options.labels.button] Button label
* @param {string|Function} [options.labels.currency] Currency label
- * @param {string|Function} [options.labels.to] To label
+ * @param {string|Function} [options.labels.separator] Separator labe, between min and max
+ * @param {string|Function} [options.labels.button] Button label
* @param {boolean} [hideContainerWhenNoResults=true] Hide the container when no results match
* @return {Object}
*/
@@ -1080,10 +1087,61 @@ search.addWidget(
#### Styling
```html
-
+
```
```css
+.ais-price-ranges {
+}
+.ais-price-ranges--header {
+}
+.ais-price-ranges--body {
+}
+.ais-price-ranges--list {
+}
+.ais-price-ranges--item {
+}
+.ais-price-ranges--item__active {
+}
+.ais-price-ranges--link {
+}
+.ais-price-ranges--form {
+}
+.ais-price-ranges--label {
+}
+.ais-price-ranges--currency {
+}
+.ais-price-ranges--input {
+}
+.ais-price-ranges--separator {
+}
+.ais-price-ranges--button {
+}
+.ais-price-ranges--footer {
+}
```
diff --git a/components/PriceRanges.js b/components/PriceRanges.js
deleted file mode 100644
index f5104ab523b..00000000000
--- a/components/PriceRanges.js
+++ /dev/null
@@ -1,83 +0,0 @@
-var React = require('react');
-
-var Template = require('./Template');
-var cx = require('classnames');
-
-class PriceRange extends React.Component {
- refine(from, to, event) {
- event.preventDefault();
- this.refs.from.value = this.refs.to.value = '';
- this.props.refine(from, to);
- }
-
- _handleSubmit(e) {
- this.refine(+this.refs.from.value || undefined, +this.refs.to.value || undefined, e);
- }
-
- render() {
- return (
-
- {this.props.facetValues.map(facetValue => {
- var key = facetValue.from + '_' + facetValue.to;
- var url;
- if (this.props.createURL) {
- url = this.props.createURL(facetValue.from, facetValue.to, facetValue.isRefined);
- } else {
- url = '#';
- }
- return (
-
-
-
- );
- })}
-
-
- );
- }
-}
-
-PriceRange.propTypes = {
- createURL: React.PropTypes.func.isRequired,
- cssClasses: React.PropTypes.shape({
- active: React.PropTypes.string,
- form: React.PropTypes.string,
- range: React.PropTypes.string,
- input: React.PropTypes.string,
- button: React.PropTypes.string
- }),
- facetValues: React.PropTypes.array,
- labels: React.PropTypes.shape({
- button: React.PropTypes.string,
- currency: React.PropTypes.string,
- to: React.PropTypes.string
- }),
- refine: React.PropTypes.func.isRequired,
- templateProps: React.PropTypes.object.isRequired
-};
-
-module.exports = PriceRange;
diff --git a/components/PriceRanges/PriceRanges.js b/components/PriceRanges/PriceRanges.js
new file mode 100644
index 00000000000..ff88c6a88b6
--- /dev/null
+++ b/components/PriceRanges/PriceRanges.js
@@ -0,0 +1,98 @@
+var React = require('react');
+
+var Template = require('../Template');
+var PriceRangesForm = require('./PriceRangesForm');
+var cx = require('classnames');
+
+class PriceRanges extends React.Component {
+ getForm() {
+ return (
+
+ );
+ }
+
+ getURLFromFacetValue(facetValue) {
+ if (!this.props.createURL) {
+ return '#';
+ }
+ return this.props.createURL(facetValue.from, facetValue.to, facetValue.isRefined);
+ }
+
+ getItemFromFacetValue(facetValue) {
+ let cssClassItem = cx(
+ this.props.cssClasses.item,
+ {[this.props.cssClasses.active]: facetValue.isRefined}
+ );
+ let url = this.getURLFromFacetValue(facetValue);
+ let key = facetValue.from + '_' + facetValue.to;
+ let handleClick = this.refine.bind(this, facetValue.from, facetValue.to);
+ return (
+
+ );
+ }
+
+ refine(from, to, event) {
+ event.preventDefault();
+ this.setState({
+ formFromValue: null,
+ formToValue: null
+ });
+ this.props.refine(from, to);
+ }
+
+ render() {
+ let form = this.getForm();
+ return (
+
+
+ {this.props.facetValues.map(facetValue => {
+ return this.getItemFromFacetValue(facetValue);
+ })}
+
+ {form}
+
+ );
+ }
+}
+
+PriceRanges.propTypes = {
+ createURL: React.PropTypes.func.isRequired,
+ cssClasses: React.PropTypes.shape({
+ active: React.PropTypes.string,
+ button: React.PropTypes.string,
+ form: React.PropTypes.string,
+ input: React.PropTypes.string,
+ item: React.PropTypes.string,
+ label: React.PropTypes.string,
+ link: React.PropTypes.string,
+ list: React.PropTypes.string,
+ separator: React.PropTypes.string
+ }),
+ facetValues: React.PropTypes.array,
+ labels: React.PropTypes.shape({
+ button: React.PropTypes.string,
+ currency: React.PropTypes.string,
+ to: React.PropTypes.string
+ }),
+ refine: React.PropTypes.func.isRequired,
+ templateProps: React.PropTypes.object.isRequired
+};
+
+PriceRanges.defaultProps = {
+ cssClasses: {}
+};
+
+module.exports = PriceRanges;
diff --git a/components/PriceRanges/PriceRangesForm.js b/components/PriceRanges/PriceRangesForm.js
new file mode 100644
index 00000000000..6a65b2daab8
--- /dev/null
+++ b/components/PriceRanges/PriceRangesForm.js
@@ -0,0 +1,59 @@
+var React = require('react');
+
+class PriceRangesForm extends React.Component {
+ getInput(type) {
+ return (
+
+ {this.props.labels.currency}
+
+
+ );
+ }
+
+ handleSubmit(event) {
+ let from = +this.refs.from.value || undefined;
+ let to = +this.refs.to.value || undefined;
+ this.props.refine(from, to, event);
+ }
+
+ render() {
+ let fromInput = this.getInput('from');
+ let toInput = this.getInput('to');
+ let onSubmit = this.handleSubmit.bind(this);
+ return (
+
+ );
+ }
+}
+
+PriceRangesForm.propTypes = {
+ cssClasses: React.PropTypes.shape({
+ button: React.PropTypes.string,
+ currency: React.PropTypes.string,
+ form: React.PropTypes.string,
+ input: React.PropTypes.string,
+ label: React.PropTypes.string,
+ separator: React.PropTypes.string
+ }),
+ labels: React.PropTypes.shape({
+ button: React.PropTypes.string,
+ currency: React.PropTypes.string,
+ separator: React.PropTypes.string
+ }),
+ refine: React.PropTypes.func.isRequired
+};
+
+
+PriceRangesForm.defaultProps = {
+ cssClasses: {},
+ labels: {}
+};
+
+
+module.exports = PriceRangesForm;
+
diff --git a/components/PriceRanges/__tests__/PriceRanges-test.js b/components/PriceRanges/__tests__/PriceRanges-test.js
new file mode 100644
index 00000000000..fdacf882b02
--- /dev/null
+++ b/components/PriceRanges/__tests__/PriceRanges-test.js
@@ -0,0 +1,257 @@
+/* eslint-env mocha */
+
+import React from 'react';
+import expect from 'expect';
+import TestUtils from 'react-addons-test-utils';
+import sinon from 'sinon';
+import jsdom from 'mocha-jsdom';
+
+import expectJSX from 'expect-jsx';
+expect.extend(expectJSX);
+
+import Template from '../../Template';
+import PriceRanges from '../PriceRanges';
+import PriceRangesForm from '../PriceRangesForm';
+
+describe('PriceRanges', () => {
+ var renderer;
+ var stubbedMethods;
+
+ jsdom({useEach: true});
+
+ beforeEach(() => {
+ stubbedMethods = [];
+ let {createRenderer} = TestUtils;
+ renderer = createRenderer();
+ });
+
+ afterEach(() => {
+ // Restore all stubbed methods
+ stubbedMethods.forEach((name) => {
+ PriceRanges.prototype[name].restore();
+ });
+ });
+
+ function render(extraProps = {}) {
+ var props = {
+ ...extraProps
+ };
+ renderer.render( );
+ return renderer.getRenderOutput();
+ }
+
+ function getComponentWithMockRendering(extraProps) {
+ let props = {
+ ...extraProps
+ };
+ return TestUtils.renderIntoDocument( );
+ }
+
+ function stubMethod(methodName, returnValue = null) {
+ stubbedMethods.push(methodName);
+ return sinon.stub(PriceRanges.prototype, methodName).returns(returnValue);
+ }
+
+ context('individual methods', () => {
+ beforeEach(() => {
+ stubMethod('render');
+ });
+
+ context('getURLFromFacetValue', () => {
+ it('should be a # if no createURL method passed', () => {
+ // Given
+ let component = getComponentWithMockRendering({
+ createURL: null
+ });
+
+ // When
+ let url = component.getURLFromFacetValue();
+
+ // Then
+ expect(url).toEqual('#');
+ });
+ it('should call the createURL method passed with the facetValue', () => {
+ // Given
+ let mockCreateURL = sinon.spy();
+ let component = getComponentWithMockRendering({
+ createURL: mockCreateURL
+ });
+ let facetValue = {
+ from: 6,
+ to: 8,
+ isRefined: true
+ };
+
+ // When
+ component.getURLFromFacetValue(facetValue);
+
+ // Then
+ expect(mockCreateURL.called).toBe(true);
+ expect(mockCreateURL.calledWith(6, 8, true)).toBe(true);
+ });
+ });
+
+ context('getItemFromFacetValue', () => {
+ var props;
+ var facetValue;
+
+ beforeEach(() => {
+ stubMethod('getURLFromFacetValue', 'url');
+ props = {
+ cssClasses: {
+ item: 'item',
+ link: 'link',
+ active: 'active'
+ }
+ };
+ facetValue = {
+ from: 1,
+ to: 10,
+ isRefined: false
+ };
+ });
+
+ it('should display one range item correctly', () => {
+ // Given
+ let component = getComponentWithMockRendering(props);
+
+ // When
+ let item = component.getItemFromFacetValue(facetValue);
+
+ // Then
+ expect(item).toEqualJSX(
+
+ );
+ });
+ it('should display one active range item correctly', () => {
+ // Given
+ let component = getComponentWithMockRendering(props);
+ facetValue.isRefined = true;
+
+ // When
+ let item = component.getItemFromFacetValue(facetValue);
+
+ // Then
+ expect(item).toEqualJSX(
+
+ );
+ });
+ });
+
+ context('refine', () => {
+ it('should call refine from props', () => {
+ // Given
+ let mockEvent = {preventDefault: sinon.spy()};
+ let props = {
+ refine: sinon.spy()
+ };
+ let component = getComponentWithMockRendering(props);
+
+ // When
+ component.refine(1, 10, mockEvent);
+
+ // Then
+ expect(mockEvent.preventDefault.called).toBe(true);
+ expect(props.refine.calledWith(1, 10)).toBe(true);
+ expect(component.state.formFromValue).toBe(null);
+ expect(component.state.formToValue).toBe(null);
+ });
+ });
+
+ context('getForm', () => {
+ it('should call the PriceRangesForm', () => {
+ // Given
+ let props = {
+ cssClasses: 'cssClasses',
+ labels: 'labels',
+ refine: 'refine'
+ };
+ let component = getComponentWithMockRendering(props);
+
+ // When
+ let form = component.getForm();
+
+ // Then
+ expect(form).toEqualJSX(
+ {}}
+ />
+ );
+ });
+ });
+ });
+
+ context('render', () => {
+ it('should have the right number of items', () => {
+ // Given
+ let mockedGetItem = stubMethod('getItemFromFacetValue');
+ let props = {
+ facetValues: [{}, {}, {}, {}]
+ };
+
+ // When
+ render(props);
+
+ // Then
+ expect(mockedGetItem.called).toBe(true);
+ expect(mockedGetItem.callCount).toBe(4);
+ });
+ it('should wrap the output in a list CSS class', () => {
+ // Given
+ stubMethod('getItemFromFacetValue', );
+ stubMethod('getForm', );
+ let props = {
+ cssClasses: {
+ list: 'list'
+ },
+ facetValues: [{}, {}, {}, {}]
+ };
+
+ // When
+ let out = render(props);
+
+ // Then
+ expect(out).toEqualJSX(
+
+ );
+ });
+ it('starts a refine on click', () => {
+ // Given
+ let mockRefined = stubMethod('refine');
+ let props = {
+ facetValues: [{from: 1, to: 10, isRefined: false}],
+ templateProps: {
+ templates: {
+ item: 'item'
+ }
+ }
+ };
+ let component = TestUtils.renderIntoDocument( );
+ let link = component.refs['1_10'];
+
+ // When
+ TestUtils.Simulate.click(link);
+
+ // Then
+ expect(mockRefined.called).toBe(true);
+ });
+ });
+});
diff --git a/components/PriceRanges/__tests__/PriceRangesForm-test.js b/components/PriceRanges/__tests__/PriceRangesForm-test.js
new file mode 100644
index 00000000000..26e5166816c
--- /dev/null
+++ b/components/PriceRanges/__tests__/PriceRangesForm-test.js
@@ -0,0 +1,85 @@
+/* eslint-env mocha */
+
+import React from 'react';
+import expect from 'expect';
+import TestUtils from 'react-addons-test-utils';
+import sinon from 'sinon';
+import jsdom from 'mocha-jsdom';
+
+import expectJSX from 'expect-jsx';
+expect.extend(expectJSX);
+
+import PriceRangesForm from '../PriceRangesForm';
+
+describe('PriceRangesForm', () => {
+ var renderer;
+
+ jsdom({useEach: true});
+
+ beforeEach(() => {
+ let {createRenderer} = TestUtils;
+ renderer = createRenderer();
+ });
+
+ function render(extraProps = {}) {
+ var props = {
+ ...extraProps
+ };
+ renderer.render( );
+ return renderer.getRenderOutput();
+ }
+
+ context('display', () => {
+ it('should pass all css classes and labels', () => {
+ let out = render({
+ labels: {
+ currency: '$',
+ separator: 'to',
+ button: 'Go'
+ },
+ cssClasses: {
+ form: 'form',
+ label: 'label',
+ input: 'input',
+ currency: 'currency',
+ separator: 'separator',
+ button: 'button'
+ }
+ });
+ expect(out).toEqualJSX(
+
+ );
+ });
+ });
+
+ context('submit', () => {
+ it('starts a refine on submit', () => {
+ // Given
+ let refine = sinon.spy();
+ let handleSubmitMock = sinon.spy(PriceRangesForm.prototype, 'handleSubmit');
+ let component = TestUtils.renderIntoDocument( );
+
+ // When
+ component.refs.from.value = 10;
+ TestUtils.Simulate.change(component.refs.from);
+ component.refs.to.value = 20;
+ TestUtils.Simulate.change(component.refs.to);
+ TestUtils.Simulate.submit(component.refs.form);
+
+ // Then
+ expect(handleSubmitMock.calledOnce).toBe(true);
+ expect(refine.calledWith(10, 20)).toBe(true);
+ });
+ });
+});
diff --git a/components/Template.js b/components/Template.js
index 1e523bba9d5..c30a7bc8b19 100644
--- a/components/Template.js
+++ b/components/Template.js
@@ -54,7 +54,10 @@ Template.propTypes = {
};
Template.defaultProps = {
- data: {}
+ data: {},
+ useCustomCompileOptions: {},
+ templates: {},
+ templatesConfig: {}
};
function transformData(fn, templateKey, originalData) {
diff --git a/components/__tests__/PriceRanges-test.js b/components/__tests__/PriceRanges-test.js
deleted file mode 100644
index cc7af44f75f..00000000000
--- a/components/__tests__/PriceRanges-test.js
+++ /dev/null
@@ -1,122 +0,0 @@
-/* eslint-env mocha */
-
-import React from 'react';
-import expect from 'expect';
-import TestUtils from 'react-addons-test-utils';
-import sinon from 'sinon';
-import jsdom from 'mocha-jsdom';
-
-import expectJSX from 'expect-jsx';
-expect.extend(expectJSX);
-
-import PriceRanges from '../PriceRanges';
-import generateRanges from '../../widgets/price-ranges/generate-ranges.js';
-
-describe('PriceRanges', () => {
- var renderer;
-
- jsdom({useEach: true});
-
- beforeEach(() => {
- let {createRenderer} = TestUtils;
- renderer = createRenderer();
- });
-
- context('with stats', () => {
- var out;
- var facetValues;
- var props;
-
- beforeEach(() => {
- facetValues = generateRanges({
- min: 1.99,
- max: 4999.98,
- avg: 243.349,
- sum: 2433490.0
- });
-
- props = {
- templateProps: {},
- facetValues,
- createURL: sinon.spy(),
- cssClasses: {
- range: 'range-class',
- form: 'form-class',
- button: 'button-class',
- input: 'input-class'
- },
- labels: {
- currency: 'USD',
- to: 'to',
- button: 'Go'
- },
- refine: sinon.spy()
- };
-
- renderer.render( );
- out = renderer.getRenderOutput();
- });
-
- it('should have the right number of children', () => {
- expect(out.props.children.length).toEqual(2);
- expect(out.props.children[0].length).toEqual(facetValues.length);
- });
-
- it('should have the range class', () => {
- out.props.children[0].forEach((c) => {
- expect(c.props.className).toEqual('range-class');
- });
- });
-
- it('creates the URL', () => {
- expect(props.createURL.called).toBe(true);
- expect(props.createURL.callCount).toEqual(8);
- expect(props.createURL.firstCall.args).toEqual([undefined, 1, undefined]);
- expect(props.createURL.secondCall.args).toEqual([1, 80, undefined]);
- expect(props.createURL.thirdCall.args).toEqual([80, 160, undefined]);
- expect(props.createURL.lastCall.args).toEqual([4980, undefined, undefined]);
- });
-
- it('should have the form class', () => {
- expect(out.props.children.length).toEqual(2);
- expect(out.props.children[1].props.className).toEqual('form-class');
- });
-
- it('should display the inputs with the associated class & labels', () => {
- expect(out.props.children.length).toEqual(2);
- expect(out.props.children[1]).toEqualJSX(
-
- );
- });
-
- it('refine on submit', () => {
- // cannot currently use shallow rendering to test refs
- props.templateProps = {
- templates: {header: '', range: '', footer: ''},
- templatesConfig: {},
- transformData: undefined,
- useCustomCompileOptions: {header: false, footer: false, range: false}
- };
-
- let eventData = {preventDefault: sinon.spy()};
- let component = TestUtils.renderIntoDocument( );
- component.refs.from.value = 10;
- component.refs.to.value = 20;
- TestUtils.Simulate.change(component.refs.from);
- TestUtils.Simulate.change(component.refs.to);
- TestUtils.Simulate.submit(component.refs.form, eventData);
- expect(props.refine.firstCall.args).toEqual([10, 20]);
- expect(eventData.preventDefault.calledOnce).toBe(true);
- });
- });
-});
diff --git a/themes/default/_price-ranges.scss b/themes/default/_price-ranges.scss
index b0bfd6404a0..c94822565ad 100644
--- a/themes/default/_price-ranges.scss
+++ b/themes/default/_price-ranges.scss
@@ -5,10 +5,18 @@
@include modifier(header);
@include modifier(body);
@include modifier(footer);
- @include modifier(form);
- @include modifier(range) {
+ @include modifier(list);
+ @include modifier(item) {
@include element(active);
}
- @include modifier(input-group);
+
+ @include modifier(link);
+ @include modifier(form);
+ @include modifier(label);
+ @include modifier(currency);
+ @include modifier(input);
+ @include modifier(separator);
@include modifier(button);
}
+
+
diff --git a/widgets/price-ranges/__tests__/price-ranges-test.js b/widgets/price-ranges/__tests__/price-ranges-test.js
index 34c49e6f1f0..64caead2f74 100644
--- a/widgets/price-ranges/__tests__/price-ranges-test.js
+++ b/widgets/price-ranges/__tests__/price-ranges-test.js
@@ -10,7 +10,7 @@ expect.extend(expectJSX);
import priceRanges from '../price-ranges';
import generateRanges from '../generate-ranges';
-import PriceRanges from '../../../components/PriceRanges';
+import PriceRanges from '../../../components/PriceRanges/PriceRanges';
describe('priceRanges()', () => {
var ReactDOM;
@@ -64,21 +64,26 @@ describe('priceRanges()', () => {
props = {
createURL: sinon.spy(),
cssClasses: {
- active: 'ais-price-ranges--range__active',
+ active: 'ais-price-ranges--item__active',
body: 'ais-price-ranges--body',
button: 'ais-price-ranges--button',
+ currency: 'ais-price-ranges--currency',
footer: 'ais-price-ranges--footer',
+ form: 'ais-price-ranges--form',
header: 'ais-price-ranges--header',
input: 'ais-price-ranges--input',
- form: 'ais-price-ranges--form',
- range: 'ais-price-ranges--range',
- root: 'ais-price-ranges'
+ item: 'ais-price-ranges--item',
+ label: 'ais-price-ranges--label',
+ list: 'ais-price-ranges--list',
+ link: 'ais-price-ranges--link',
+ root: 'ais-price-ranges',
+ separator: 'ais-price-ranges--separator'
},
shouldAutoHideContainer: false,
facetValues: generateRanges(results.getFacetStats()),
labels: {
currency: '$',
- to: 'to',
+ separator: 'to',
button: 'Go'
},
refine() {},
@@ -86,7 +91,7 @@ describe('priceRanges()', () => {
templates: require('../defaultTemplates'),
templatesConfig: undefined,
transformData: undefined,
- useCustomCompileOptions: {header: false, footer: false, range: false}
+ useCustomCompileOptions: {header: false, footer: false, item: false}
}
};
});
diff --git a/widgets/price-ranges/defaultTemplates.js b/widgets/price-ranges/defaultTemplates.js
index 5b082595514..8a736570a29 100644
--- a/widgets/price-ranges/defaultTemplates.js
+++ b/widgets/price-ranges/defaultTemplates.js
@@ -1,6 +1,6 @@
module.exports = {
header: ``,
- range: `
+ item: `
{{#from}}
{{^to}}
≥
@@ -16,6 +16,6 @@ module.exports = {
{{/from}}
\${{to}}
{{/to}}
- {{count}} `,
+ `,
footer: ``
};
diff --git a/widgets/price-ranges/price-ranges.js b/widgets/price-ranges/price-ranges.js
index 0f74f5b5d78..8a5fff7d8ec 100644
--- a/widgets/price-ranges/price-ranges.js
+++ b/widgets/price-ranges/price-ranges.js
@@ -1,61 +1,66 @@
-var React = require('react');
-var ReactDOM = require('react-dom');
+let React = require('react');
+let ReactDOM = require('react-dom');
-var utils = require('../../lib/utils.js');
+let utils = require('../../lib/utils.js');
-var generateRanges = require('./generate-ranges.js');
+let generateRanges = require('./generate-ranges.js');
-var defaultTemplates = require('./defaultTemplates');
-var autoHideContainer = require('../../decorators/autoHideContainer');
-var headerFooter = require('../../decorators/headerFooter');
+let defaultTemplates = require('./defaultTemplates');
+let autoHideContainer = require('../../decorators/autoHideContainer');
+let headerFooter = require('../../decorators/headerFooter');
-var bem = utils.bemHelper('ais-price-ranges');
-var cx = require('classnames/dedupe');
+let bem = utils.bemHelper('ais-price-ranges');
+let cx = require('classnames/dedupe');
/**
* Instantiate a price ranges on a numerical facet
* @param {string|DOMElement} options.container Valid CSS Selector as a string or DOMElement
* @param {string} options.facetName Name of the attribute for faceting
- * @param {Object} [options.cssClasses] CSS classes to add to the wrapping elements: root, range
- * @param {string|string[]} [options.cssClasses.root] CSS class to add to the root element
- * @param {string|string[]} [options.cssClasses.header] CSS class to add to the header element
- * @param {string|string[]} [options.cssClasses.body] CSS class to add to the body element
- * @param {string|string[]} [options.cssClasses.footer] CSS class to add to the footer element
- * @param {string|string[]} [options.cssClasses.form] CSS class to add to the form element
- * @param {string|string[]} [options.cssClasses.range] CSS class to add to the range element
- * @param {string|string[]} [options.cssClasses.active] CSS class to add to the active range element
- * @param {string|string[]} [options.cssClasses.input] CSS class to add to the min/max input elements
- * @param {string|string[]} [options.cssClasses.button] CSS class to add to the button element
+ * @param {Object} [options.cssClasses] CSS classes to add
+ * @param {string} [options.cssClasses.root] CSS class to add to the root element
+ * @param {string} [options.cssClasses.header] CSS class to add to the header element
+ * @param {string} [options.cssClasses.body] CSS class to add to the body element
+ * @param {string} [options.cssClasses.list] CSS class to add to the wrapping list element
+ * @param {string} [options.cssClasses.item] CSS class to add to each item element
+ * @param {string} [options.cssClasses.active] CSS class to add to the active item element
+ * @param {string} [options.cssClasses.link] CSS class to add to each link element
+ * @param {string} [options.cssClasses.form] CSS class to add to the form element
+ * @param {string} [options.cssClasses.label] CSS class to add to each wrapping label of the form
+ * @param {string} [options.cssClasses.input] CSS class to add to each input of the form
+ * @param {string} [options.cssClasses.currency] CSS class to add to each currency element of the form
+ * @param {string} [options.cssClasses.separator] CSS class to add to the separator of the form
+ * @param {string} [options.cssClasses.button] CSS class to add to the submit button of the form
+ * @param {string} [options.cssClasses.footer] CSS class to add to the footer element
* @param {Object} [options.templates] Templates to use for the widget
- * @param {string|Function} [options.templates.range] Range template
+ * @param {string|Function} [options.templates.item] Item template
* @param {Object} [options.labels] Labels to use for the widget
- * @param {string|Function} [options.labels.button] Button label
* @param {string|Function} [options.labels.currency] Currency label
- * @param {string|Function} [options.labels.to] To label
+ * @param {string|Function} [options.labels.separator] Separator labe, between min and max
+ * @param {string|Function} [options.labels.button] Button label
* @param {boolean} [hideContainerWhenNoResults=true] Hide the container when no results match
* @return {Object}
*/
function priceRanges({
- container = null,
- facetName = null,
+ container,
+ facetName,
cssClasses = {},
templates = defaultTemplates,
labels = {
currency: '$',
button: 'Go',
- to: 'to'
+ separator: 'to'
},
hideContainerWhenNoResults = true
}) {
- var containerNode = utils.getContainerNode(container);
- var usage = 'Usage: priceRanges({container, facetName, [cssClasses, templates, labels, hideContainerWhenNoResults]})';
+ let containerNode = utils.getContainerNode(container);
+ let usage = 'Usage: priceRanges({container, facetName, [cssClasses.{root,header,body,list,item,active,link,form,label,input,currency,separator,button,footer}, templates.{header,item,footer}, labels.{currency,separator,button}, hideContainerWhenNoResults]})';
- var PriceRanges = headerFooter(require('../../components/PriceRanges'));
+ let PriceRanges = headerFooter(require('../../components/PriceRanges/PriceRanges'));
if (hideContainerWhenNoResults === true) {
PriceRanges = autoHideContainer(PriceRanges);
}
- if (container === null || facetName === null) {
+ if (!container || !facetName) {
throw new Error(usage);
}
@@ -65,14 +70,14 @@ function priceRanges({
}),
_generateRanges: function(results) {
- var stats = results.getFacetStats(facetName);
+ let stats = results.getFacetStats(facetName);
return generateRanges(stats);
},
_extractRefinedRange: function(helper) {
- var refinements = helper.getRefinements(facetName);
- var from;
- var to;
+ let refinements = helper.getRefinements(facetName);
+ let from;
+ let to;
if (refinements.length === 0) {
return [];
@@ -89,7 +94,7 @@ function priceRanges({
},
_refine: function(helper, from, to) {
- var facetValues = this._extractRefinedRange(helper);
+ let facetValues = this._extractRefinedRange(helper);
helper.clearRefinements(facetName);
if (facetValues.length === 0 || facetValues[0].from !== from || facetValues[0].to !== to) {
@@ -105,8 +110,8 @@ function priceRanges({
},
render: function({results, helper, templatesConfig, state, createURL}) {
- var hasNoResults = results.nbHits === 0;
- var facetValues;
+ let hasNoResults = results.nbHits === 0;
+ let facetValues;
if (results.hits.length > 0) {
facetValues = this._extractRefinedRange(helper);
@@ -118,7 +123,7 @@ function priceRanges({
facetValues = [];
}
- var templateProps = utils.prepareTemplateProps({
+ let templateProps = utils.prepareTemplateProps({
defaultTemplates,
templatesConfig,
templates
@@ -128,18 +133,23 @@ function priceRanges({
root: cx(bem(null), cssClasses.root),
header: cx(bem('header'), cssClasses.header),
body: cx(bem('body'), cssClasses.body),
- footer: cx(bem('footer'), cssClasses.footer),
- range: cx(bem('range'), cssClasses.range),
- active: cx(bem('range', 'active'), cssClasses.active),
- input: cx(bem('input'), cssClasses.input),
+ list: cx(bem('list'), cssClasses.list),
+ link: cx(bem('link'), cssClasses.link),
+ item: cx(bem('item'), cssClasses.item),
+ active: cx(bem('item', 'active'), cssClasses.active),
form: cx(bem('form'), cssClasses.form),
- button: cx(bem('button'), cssClasses.button)
+ label: cx(bem('label'), cssClasses.label),
+ input: cx(bem('input'), cssClasses.input),
+ currency: cx(bem('currency'), cssClasses.currency),
+ button: cx(bem('button'), cssClasses.button),
+ separator: cx(bem('separator'), cssClasses.separator),
+ footer: cx(bem('footer'), cssClasses.footer)
};
ReactDOM.render(
{
- var newState = state.clearRefinements(facetName);
+ let newState = state.clearRefinements(facetName);
if (!isRefined) {
if (typeof from !== 'undefined') {
newState = newState.addNumericRefinement(facetName, '>', from - 1);