diff --git a/dev/app.js b/dev/app.js
index cad6cff70d..f3d2e74cd4 100644
--- a/dev/app.js
+++ b/dev/app.js
@@ -288,6 +288,7 @@ search.addWidget(
templates: {
header: 'Numeric refinement list (price)',
},
+ //autoHideContainer: false,
})
);
diff --git a/src/connectors/numeric-refinement-list/__tests__/connectNumericRefinementList-test.js b/src/connectors/numeric-refinement-list/__tests__/connectNumericRefinementList-test.js
index dc53d7da6a..c18ec04f4d 100644
--- a/src/connectors/numeric-refinement-list/__tests__/connectNumericRefinementList-test.js
+++ b/src/connectors/numeric-refinement-list/__tests__/connectNumericRefinementList-test.js
@@ -41,11 +41,6 @@ describe('connectNumericRefinementList', () => {
// test if isFirstRendering is true during init
expect(rendering.lastCall.args[1]).toBe(true);
- const firstRenderingOptions = rendering.lastCall.args[0];
- expect(firstRenderingOptions.shouldAutoHideContainer).toBe(true);
- expect(firstRenderingOptions.collapsible).toBe(false);
- expect(firstRenderingOptions.containerNode).toBe(container);
-
widget.render({
results: new SearchResults(helper.state, [{nbHits: 0}]),
state: helper.state,
@@ -56,11 +51,6 @@ describe('connectNumericRefinementList', () => {
// test that rendering has been called during init with isFirstRendering = false
expect(rendering.callCount).toBe(2);
expect(rendering.lastCall.args[1]).toBe(false);
-
- const secondRenderingOptions = rendering.lastCall.args[0];
- expect(secondRenderingOptions.shouldAutoHideContainer).toBe(true);
- expect(secondRenderingOptions.collapsible).toBe(false);
- expect(secondRenderingOptions.containerNode).toBe(container);
});
it('Provide a function to update the refinements at each step', () => {
@@ -222,4 +212,67 @@ describe('connectNumericRefinementList', () => {
toggleRefinement = renderingParameters.toggleRefinement;
});
});
+
+ it('when the state is cleared, the "no value" value should be refined', () => {
+ const container = document.createElement('div');
+ const rendering = sinon.stub();
+ const makeWidget = connectNumericRefinementList(rendering);
+ const listOptions = [
+ {name: 'below 10', end: 10},
+ {name: '10 - 20', start: 10, end: 20},
+ {name: 'more than 20', start: 20},
+ {name: '42', start: 42, end: 42},
+ {name: 'void'},
+ ];
+ const widget = makeWidget({
+ container,
+ attributeName: 'numerics',
+ options: listOptions,
+ });
+
+ const helper = jsHelper(fakeClient);
+ helper.search = sinon.stub();
+
+ widget.init({
+ helper,
+ state: helper.state,
+ createURL: () => '#',
+ onHistoryChange: () => {},
+ });
+
+ const toggleRefinement = rendering.lastCall.args[0].toggleRefinement;
+ // a user selects a value in the refinement list
+ toggleRefinement(listOptions[0].name);
+
+ widget.render({
+ results: new SearchResults(helper.state, [{}]),
+ state: helper.state,
+ helper,
+ createURL: () => '#',
+ });
+
+ // No option should be selected
+ const expectedResults0 = [...listOptions].map(o => ({...o, isRefined: false, attributeName: 'numerics'}));
+ expectedResults0[0].isRefined = true;
+
+ const renderingParameters0 = rendering.lastCall.args[0];
+ expect(renderingParameters0.facetValues).toEqual(expectedResults0);
+
+ // All the refinements are cleared by a third party
+ helper.removeNumericRefinement('numerics');
+
+ widget.render({
+ results: new SearchResults(helper.state, [{}]),
+ state: helper.state,
+ helper,
+ createURL: () => '#',
+ });
+
+ // No option should be selected
+ const expectedResults1 = [...listOptions].map(o => ({...o, isRefined: false, attributeName: 'numerics'}));
+ expectedResults1[4].isRefined = true;
+
+ const renderingParameters1 = rendering.lastCall.args[0];
+ expect(renderingParameters1.facetValues).toEqual(expectedResults1);
+ });
});
diff --git a/src/connectors/numeric-refinement-list/connectNumericRefinementList.js b/src/connectors/numeric-refinement-list/connectNumericRefinementList.js
index 2ef43d3820..f6296d5bb4 100644
--- a/src/connectors/numeric-refinement-list/connectNumericRefinementList.js
+++ b/src/connectors/numeric-refinement-list/connectNumericRefinementList.js
@@ -1,14 +1,5 @@
-import {
- bemHelper,
- prepareTemplateProps,
- getContainerNode,
-} from '../../lib/utils.js';
-import cx from 'classnames';
import find from 'lodash/find';
import includes from 'lodash/includes';
-import defaultTemplates from './defaultTemplates.js';
-
-const bem = bemHelper('ais-refinement-list');
/**
* Instantiate a list of refinements based on a facet
@@ -40,53 +31,20 @@ const bem = bemHelper('ais-refinement-list');
* @return {Object}
*/
const usage = `Usage:
-numericRefinementList({
- container,
+connectNumericRefinementList(renderer)({
attributeName,
- options,
- [ cssClasses.{root,header,body,footer,list,item,active,label,radio,count} ],
- [ templates.{header,item,footer} ],
- [ transformData.{item} ],
- [ autoHideContainer ],
- [ collapsible=false ]
+ options
})`;
const connectNumericRefinementList = numericRefinementListRendering => ({
- container,
attributeName,
options,
- cssClasses: userCssClasses = {},
- templates = defaultTemplates,
- collapsible = false,
- transformData,
- autoHideContainer = true,
}) => {
- if (!container || !attributeName || !options) {
+ if (!attributeName || !options) {
throw new Error(usage);
}
- const containerNode = getContainerNode(container);
-
- const cssClasses = {
- root: cx(bem(null), userCssClasses.root),
- header: cx(bem('header'), userCssClasses.header),
- body: cx(bem('body'), userCssClasses.body),
- footer: cx(bem('footer'), userCssClasses.footer),
- list: cx(bem('list'), userCssClasses.list),
- item: cx(bem('item'), userCssClasses.item),
- label: cx(bem('label'), userCssClasses.label),
- radio: cx(bem('radio'), userCssClasses.radio),
- active: cx(bem('item', 'active'), userCssClasses.active),
- };
-
return {
- init({templatesConfig, helper, createURL}) {
- this._templateProps = prepareTemplateProps({
- transformData,
- defaultTemplates,
- templatesConfig,
- templates,
- });
-
+ init({helper, createURL, instantSearchInstance}) {
this._toggleRefinement = facetValue => {
const refinedState = refine(helper.state, attributeName, options, facetValue);
helper.setState(refinedState).search();
@@ -103,17 +61,14 @@ const connectNumericRefinementList = numericRefinementListRendering => ({
);
numericRefinementListRendering({
- collapsible,
createURL: this._createURL(helper.state),
- cssClasses,
facetValues,
- shouldAutoHideContainer: autoHideContainer,
- templateProps: this._templateProps,
+ noResults: true,
toggleRefinement: this._toggleRefinement,
- containerNode,
+ instantSearchInstance,
}, true);
},
- render({results, state}) {
+ render({results, state, instantSearchInstance}) {
const facetValues = options.map(facetValue =>
({
...facetValue,
@@ -122,15 +77,14 @@ const connectNumericRefinementList = numericRefinementListRendering => ({
})
);
+ const noResults = results.nbHits === 0;
+
numericRefinementListRendering({
- collapsible,
createURL: this._createURL(state),
- cssClasses,
facetValues,
- shouldAutoHideContainer: autoHideContainer && results.nbHits === 0,
- templateProps: this._templateProps,
+ noResults,
toggleRefinement: this._toggleRefinement,
- containerNode,
+ instantSearchInstance,
}, false);
},
};
diff --git a/src/widgets/numeric-refinement-list/__tests__/numeric-refinement-list-test.js b/src/widgets/numeric-refinement-list/__tests__/numeric-refinement-list-test.js
index 106363bbde..dc02250a2a 100644
--- a/src/widgets/numeric-refinement-list/__tests__/numeric-refinement-list-test.js
+++ b/src/widgets/numeric-refinement-list/__tests__/numeric-refinement-list-test.js
@@ -77,7 +77,7 @@ describe('numericRefinementList()', () => {
helper.state.clearRefinements = sinon.stub().returns(helper.state);
helper.state.addNumericRefinement = sinon.stub().returns(helper.state);
createURL = () => '#';
- widget.init({helper});
+ widget.init({helper, instantSearchInstance: {}});
});
it('calls twice ReactDOM.render(, container)', () => {
@@ -186,7 +186,7 @@ describe('numericRefinementList()', () => {
});
// The lifeccycle impose all the steps
- testWidget.init({helper, createURL: () => ''});
+ testWidget.init({helper, createURL: () => '', instantSearchInstance: {}});
// When
testWidget.render({state, results, createURL});
diff --git a/src/connectors/numeric-refinement-list/defaultTemplates.js b/src/widgets/numeric-refinement-list/defaultTemplates.js
similarity index 100%
rename from src/connectors/numeric-refinement-list/defaultTemplates.js
rename to src/widgets/numeric-refinement-list/defaultTemplates.js
diff --git a/src/widgets/numeric-refinement-list/numeric-refinement-list.js b/src/widgets/numeric-refinement-list/numeric-refinement-list.js
index 362be8e15f..46db848ef4 100644
--- a/src/widgets/numeric-refinement-list/numeric-refinement-list.js
+++ b/src/widgets/numeric-refinement-list/numeric-refinement-list.js
@@ -4,6 +4,56 @@ import RefinementList from '../../components/RefinementList/RefinementList.js';
import connectNumericRefinementList from '../../connectors/numeric-refinement-list/connectNumericRefinementList.js';
+import defaultTemplates from './defaultTemplates.js';
+
+import {
+ bemHelper,
+ prepareTemplateProps,
+ getContainerNode,
+} from '../../lib/utils.js';
+import cx from 'classnames';
+
+const bem = bemHelper('ais-refinement-list');
+
+const renderer = ({
+ containerNode,
+ collapsible,
+ autoHideContainer,
+ cssClasses,
+ renderState,
+ transformData,
+ templates,
+}) => ({
+ createURL,
+ instantSearchInstance,
+ toggleRefinement,
+ facetValues,
+ noResults,
+}, isFirstRendering) => {
+ if (isFirstRendering) {
+ renderState.templateProps = prepareTemplateProps({
+ transformData,
+ defaultTemplates,
+ templatesConfig: instantSearchInstance.templatesConfig,
+ templates,
+ });
+ return;
+ }
+
+ ReactDOM.render(
+ ,
+ containerNode
+ );
+};
+
/**
* Instantiate a list of refinements based on a facet
* @function numericRefinementList
@@ -33,29 +83,62 @@ import connectNumericRefinementList from '../../connectors/numeric-refinement-li
* @param {boolean} [options.collapsible.collapsed] Initial collapsed state of a collapsible widget
* @return {Object}
*/
-export default connectNumericRefinementList(defaultRendering);
+const usage = `Usage:
+numericRefinementList({
+ container,
+ attributeName,
+ options,
+ [ cssClasses.{root,header,body,footer,list,item,active,label,radio,count} ],
+ [ templates.{header,item,footer} ],
+ [ transformData.{item} ],
+ [ autoHideContainer ],
+ [ collapsible=false ]
+})`;
-function defaultRendering({
- collapsible,
- createURL,
- cssClasses,
- facetValues,
- shouldAutoHideContainer,
- templateProps,
- toggleRefinement,
- containerNode,
-}, isFirstRendering) {
- if (isFirstRendering) return;
- ReactDOM.render(
- ,
- containerNode
- );
+export default function numericRefinementList({
+ container,
+ attributeName,
+ options,
+ cssClasses: userCssClasses = {},
+ templates = defaultTemplates,
+ collapsible = false,
+ transformData,
+ autoHideContainer = true,
+}) {
+ if (!container || !attributeName || !options) {
+ throw new Error(usage);
+ }
+
+ const containerNode = getContainerNode(container);
+
+ const cssClasses = {
+ root: cx(bem(null), userCssClasses.root),
+ header: cx(bem('header'), userCssClasses.header),
+ body: cx(bem('body'), userCssClasses.body),
+ footer: cx(bem('footer'), userCssClasses.footer),
+ list: cx(bem('list'), userCssClasses.list),
+ item: cx(bem('item'), userCssClasses.item),
+ label: cx(bem('label'), userCssClasses.label),
+ radio: cx(bem('radio'), userCssClasses.radio),
+ active: cx(bem('item', 'active'), userCssClasses.active),
+ };
+
+ const specializedRenderer = renderer({
+ containerNode,
+ collapsible,
+ autoHideContainer,
+ cssClasses,
+ renderState: {},
+ transformData,
+ templates,
+ });
+ try {
+ const makeNumericRefinementList = connectNumericRefinementList(specializedRenderer);
+ return makeNumericRefinementList({
+ attributeName,
+ options,
+ });
+ } catch (e) {
+ throw new Error(usage);
+ }
}