diff --git a/src/connectors/clear-refinements/__tests__/connectClearRefinements-test.js b/src/connectors/clear-refinements/__tests__/connectClearRefinements-test.js
index a20580fe8f..8038c7fc43 100644
--- a/src/connectors/clear-refinements/__tests__/connectClearRefinements-test.js
+++ b/src/connectors/clear-refinements/__tests__/connectClearRefinements-test.js
@@ -1,4 +1,8 @@
import jsHelper, { SearchResults } from 'algoliasearch-helper';
+import {
+ createInitOptions,
+ createRenderOptions,
+} from '../../../../test/mock/createWidget';
import connectClearRefinements from '../connectClearRefinements';
describe('connectClearRefinements', () => {
@@ -48,10 +52,8 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/clear-refin
describe('Lifecycle', () => {
it('renders during init and render', () => {
- const helper = jsHelper({});
+ const helper = jsHelper({}, 'indexName');
helper.search = () => {};
- // test that the dummyRendering is called with the isFirstRendering
- // flag set accordingly
const rendering = jest.fn();
const makeWidget = connectClearRefinements(rendering);
const widget = makeWidget({
@@ -59,46 +61,59 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/clear-refin
});
expect(widget.getConfiguration).toBe(undefined);
- // test if widget is not rendered yet at this point
expect(rendering).toHaveBeenCalledTimes(0);
- widget.init({
- helper,
- state: helper.state,
- createURL: () => '#',
- });
+ widget.init(
+ createInitOptions({
+ helper,
+ state: helper.state,
+ })
+ );
// test that rendering has been called during init with isFirstRendering = true
expect(rendering).toHaveBeenCalledTimes(1);
- // test if isFirstRendering is true during init
- expect(rendering.mock.calls[0][1]).toBe(true);
- const firstRenderingOptions = rendering.mock.calls[0][0];
+ const [
+ firstRenderingOptions,
+ isFirstRenderAtInit,
+ ] = rendering.mock.calls[0];
+
+ expect(isFirstRenderAtInit).toBe(true);
+ expect(firstRenderingOptions.createURL).toBeInstanceOf(Function);
+ expect(firstRenderingOptions.refine).toBeInstanceOf(Function);
expect(firstRenderingOptions.hasRefinements).toBe(false);
expect(firstRenderingOptions.widgetParams).toEqual({
foo: 'bar', // dummy param to test `widgetParams`
});
- widget.render({
- results: new SearchResults(helper.state, [{}]),
- state: helper.state,
- helper,
- createURL: () => '#',
- });
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ })
+ );
// test that rendering has been called during init with isFirstRendering = false
expect(rendering).toHaveBeenCalledTimes(2);
- expect(rendering.mock.calls[1][1]).toBe(false);
- const secondRenderingOptions = rendering.mock.calls[1][0];
+ const [
+ secondRenderingOptions,
+ isFirstRenderAtRender,
+ ] = rendering.mock.calls[1];
+
+ expect(isFirstRenderAtRender).toBe(false);
+ expect(secondRenderingOptions.createURL).toBeInstanceOf(Function);
+ expect(secondRenderingOptions.refine).toBeInstanceOf(Function);
expect(secondRenderingOptions.hasRefinements).toBe(false);
});
it('does not throw without the unmount function', () => {
- const helper = jsHelper({});
+ const helper = jsHelper({}, 'indexName');
const rendering = () => {};
const makeWidget = connectClearRefinements(rendering);
const widget = makeWidget();
+
expect(() =>
widget.dispose({ helper, state: helper.state })
).not.toThrow();
@@ -107,7 +122,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/clear-refin
describe('Instance options', () => {
it('provides a function to clear the refinements', () => {
- const helper = jsHelper({}, '', {
+ const helper = jsHelper({}, 'indexName', {
facets: ['myFacet'],
});
helper.search = () => {};
@@ -118,39 +133,37 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/clear-refin
const makeWidget = connectClearRefinements(rendering);
const widget = makeWidget({});
- widget.init({
- helper,
- state: helper.state,
- createURL: () => '#',
- });
-
- expect(helper.hasRefinements('myFacet')).toBe(true);
- expect(helper.state.query).toBe('not empty');
- const initClearMethod = rendering.mock.calls[0][0].refine;
- initClearMethod();
+ widget.init(
+ createInitOptions({
+ helper,
+ state: helper.state,
+ })
+ );
- expect(helper.hasRefinements('myFacet')).toBe(false);
- expect(helper.state.query).toBe('not empty');
+ expect(rendering.mock.calls[0][0].refine).toBeInstanceOf(Function);
helper.toggleRefinement('myFacet', 'someOtherValue');
- widget.render({
- results: new SearchResults(helper.state, [{}]),
- state: helper.state,
- helper,
- createURL: () => '#',
- });
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ })
+ );
expect(helper.hasRefinements('myFacet')).toBe(true);
expect(helper.state.query).toBe('not empty');
- const renderClearMethod = rendering.mock.calls[1][0].refine;
- renderClearMethod();
+
+ const refine = rendering.mock.calls[1][0].refine;
+ refine();
+
expect(helper.hasRefinements('myFacet')).toBe(false);
expect(helper.state.query).toBe('not empty');
});
it('provides a function to clear the refinements and the query', () => {
- const helper = jsHelper({}, '', {
+ const helper = jsHelper({}, 'indexName', {
facets: ['myFacet'],
});
helper.search = () => {};
@@ -161,38 +174,76 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/clear-refin
const makeWidget = connectClearRefinements(rendering);
const widget = makeWidget({ excludedAttributes: [] });
- widget.init({
- helper,
- state: helper.state,
- createURL: () => '#',
- });
-
- expect(helper.hasRefinements('myFacet')).toBe(true);
- expect(helper.state.query).toBe('a query');
- const initClearMethod = rendering.mock.calls[0][0].refine;
- initClearMethod();
+ widget.init(
+ createInitOptions({
+ helper,
+ state: helper.state,
+ })
+ );
- expect(helper.hasRefinements('myFacet')).toBe(false);
- expect(helper.state.query).toBe('');
+ expect(rendering.mock.calls[0][0].refine).toBeInstanceOf(Function);
helper.toggleRefinement('myFacet', 'someOtherValue');
helper.setQuery('another query');
- widget.render({
- results: new SearchResults(helper.state, [{}]),
- state: helper.state,
- helper,
- createURL: () => '#',
- });
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ })
+ );
expect(helper.hasRefinements('myFacet')).toBe(true);
expect(helper.state.query).toBe('another query');
- const renderClearMethod = rendering.mock.calls[1][0].refine;
- renderClearMethod();
+
+ const refine = rendering.mock.calls[1][0].refine;
+ refine();
+
expect(helper.hasRefinements('myFacet')).toBe(false);
expect(helper.state.query).toBe('');
});
+ it('provides the same `refine` and `createURL` function references during the lifecycle', () => {
+ const helper = jsHelper({}, 'indexName');
+ helper.search = () => {};
+
+ const rendering = jest.fn();
+ const makeWidget = connectClearRefinements(rendering);
+ const widget = makeWidget({});
+
+ widget.init(
+ createInitOptions({
+ helper,
+ state: helper.state,
+ })
+ );
+
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ })
+ );
+
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ })
+ );
+
+ const [firstRender, secondRender, thirdRender] = rendering.mock.calls;
+
+ expect(secondRender[0].refine).toBe(firstRender[0].refine);
+ expect(thirdRender[0].refine).toBe(secondRender[0].refine);
+
+ expect(secondRender[0].createURL).toBe(firstRender[0].createURL);
+ expect(thirdRender[0].createURL).toBe(secondRender[0].createURL);
+ });
+
it('gets refinements from results', () => {
const helper = jsHelper({}, undefined, {
facets: ['aFacet'],
@@ -204,20 +255,22 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/clear-refin
const makeWidget = connectClearRefinements(rendering);
const widget = makeWidget();
- widget.init({
- helper,
- state: helper.state,
- createURL: () => '#',
- });
+ widget.init(
+ createInitOptions({
+ helper,
+ state: helper.state,
+ })
+ );
- expect(rendering.mock.calls[0][0].hasRefinements).toBe(true);
+ expect(rendering.mock.calls[0][0].hasRefinements).toBe(false);
- widget.render({
- results: new SearchResults(helper.state, [{}]),
- state: helper.state,
- helper,
- createURL: () => '#',
- });
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ })
+ );
expect(rendering.mock.calls[1][0].hasRefinements).toBe(true);
});
@@ -237,20 +290,22 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/clear-refin
excludedAttributes: [],
});
- widget.init({
- helper,
- state: helper.state,
- createURL: () => '#',
- });
+ widget.init(
+ createInitOptions({
+ helper,
+ state: helper.state,
+ })
+ );
- expect(rendering.mock.calls[0][0].hasRefinements).toBe(true);
+ expect(rendering.mock.calls[0][0].hasRefinements).toBe(false);
- widget.render({
- results: new SearchResults(helper.state, [{}]),
- state: helper.state,
- helper,
- createURL: () => '#',
- });
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ })
+ );
expect(rendering.mock.calls[1][0].hasRefinements).toBe(true);
});
@@ -267,26 +322,28 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/clear-refin
excludedAttributes: [],
});
- widget.init({
- helper,
- state: helper.state,
- createURL: () => '#',
- });
+ widget.init(
+ createInitOptions({
+ helper,
+ state: helper.state,
+ })
+ );
expect(rendering.mock.calls[0][0].hasRefinements).toBe(false);
- widget.render({
- results: new SearchResults(helper.state, [{}]),
- state: helper.state,
- helper,
- createURL: () => '#',
- });
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ })
+ );
expect(rendering.mock.calls[1][0].hasRefinements).toBe(false);
});
it('without includedAttributes or excludedAttributes and with a query has no refinements', () => {
- const helper = jsHelper({});
+ const helper = jsHelper({}, 'indexName');
helper.setQuery('not empty');
helper.search = () => {};
@@ -294,26 +351,28 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/clear-refin
const makeWidget = connectClearRefinements(rendering);
const widget = makeWidget({});
- widget.init({
- helper,
- state: helper.state,
- createURL: () => '#',
- });
+ widget.init(
+ createInitOptions({
+ helper,
+ state: helper.state,
+ })
+ );
expect(rendering.mock.calls[0][0].hasRefinements).toBe(false);
- widget.render({
- results: new SearchResults(helper.state, [{}]),
- state: helper.state,
- helper,
- createURL: () => '#',
- });
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ })
+ );
expect(rendering.mock.calls[1][0].hasRefinements).toBe(false);
});
it('includes only includedAttributes', () => {
- const helper = jsHelper({}, '', {
+ const helper = jsHelper({}, 'indexName', {
facets: ['facet1', 'facet2'],
});
helper.search = () => {};
@@ -327,30 +386,42 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/clear-refin
.toggleRefinement('facet2', 'value')
.setQuery('not empty');
- widget.init({
- helper,
- state: helper.state,
- createURL: () => '#',
- });
+ widget.init(
+ createInitOptions({
+ helper,
+ state: helper.state,
+ })
+ );
expect(helper.hasRefinements('facet1')).toBe(true);
expect(helper.hasRefinements('facet2')).toBe(true);
- const refine = rendering.mock.calls[0][0].refine;
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ })
+ );
+
+ const refine = rendering.mock.calls[1][0].refine;
refine();
- widget.render({
- helper,
- state: helper.state,
- createURL: () => '#',
- });
+
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ })
+ );
expect(helper.hasRefinements('facet1')).toBe(false);
expect(helper.hasRefinements('facet2')).toBe(true);
- expect(rendering.mock.calls[1][0].hasRefinements).toBe(false);
+ expect(rendering.mock.calls[2][0].hasRefinements).toBe(false);
});
it('includes only includedAttributes (with query)', () => {
- const helper = jsHelper({}, '', {
+ const helper = jsHelper({}, 'indexName', {
facets: ['facet1'],
});
helper.search = () => {};
@@ -361,30 +432,42 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/clear-refin
helper.toggleRefinement('facet1', 'value').setQuery('not empty');
- widget.init({
- helper,
- state: helper.state,
- createURL: () => '#',
- });
+ widget.init(
+ createInitOptions({
+ helper,
+ state: helper.state,
+ })
+ );
expect(helper.hasRefinements('facet1')).toBe(true);
expect(helper.state.query).toBe('not empty');
- const refine = rendering.mock.calls[0][0].refine;
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ })
+ );
+
+ const refine = rendering.mock.calls[1][0].refine;
refine();
- widget.render({
- helper,
- state: helper.state,
- createURL: () => '#',
- });
+
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ })
+ );
expect(helper.hasRefinements('facet1')).toBe(false);
expect(helper.state.query).toBe('');
- expect(rendering.mock.calls[1][0].hasRefinements).toBe(false);
+ expect(rendering.mock.calls[2][0].hasRefinements).toBe(false);
});
it('excludes excludedAttributes', () => {
- const helper = jsHelper({}, '', {
+ const helper = jsHelper({}, 'indexName', {
facets: ['facet1', 'facet2'],
});
helper.search = () => {};
@@ -402,48 +485,66 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/clear-refin
{
helper.setQuery('not empty');
- widget.init({
- helper,
- state: helper.state,
- createURL: () => '#',
- });
+ widget.init(
+ createInitOptions({
+ helper,
+ state: helper.state,
+ })
+ );
expect(helper.hasRefinements('facet1')).toBe(true);
expect(helper.hasRefinements('facet2')).toBe(true);
- const refine = rendering.mock.calls[0][0].refine;
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ })
+ );
+
+ const refine = rendering.mock.calls[1][0].refine;
refine();
expect(helper.hasRefinements('facet1')).toBe(false);
expect(helper.hasRefinements('facet2')).toBe(true);
- expect(rendering.mock.calls[0][0].hasRefinements).toBe(true);
+ expect(rendering.mock.calls[1][0].hasRefinements).toBe(true);
}
{
// facet has not been cleared and it is still refined with value
helper.setQuery('not empty');
- widget.render({
- helper,
- state: helper.state,
- results: new SearchResults(helper.state, [{}]),
- createURL: () => '#',
- });
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ })
+ );
expect(helper.hasRefinements('facet1')).toBe(false);
expect(helper.hasRefinements('facet2')).toBe(true);
- const refine = rendering.mock.calls[1][0].refine;
+ const refine = rendering.mock.calls[2][0].refine;
refine();
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ })
+ );
+
expect(helper.hasRefinements('facet1')).toBe(false);
expect(helper.hasRefinements('facet2')).toBe(true);
}
});
- describe('transformItems is called', () => {
- const helper = jsHelper({}, '', {
+ it('transformItems is called', () => {
+ const helper = jsHelper({}, 'indexName', {
facets: ['facet1', 'facet2', 'facet3'],
});
helper.search = () => {};
@@ -464,35 +565,47 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/clear-refin
.toggleRefinement('facet3', 'value')
.setQuery('not empty');
- widget.init({
- helper,
- state: helper.state,
- createURL: () => '#',
- });
+ widget.init(
+ createInitOptions({
+ helper,
+ state: helper.state,
+ })
+ );
expect(helper.hasRefinements('facet1')).toBe(true);
expect(helper.hasRefinements('facet2')).toBe(true);
expect(helper.hasRefinements('facet3')).toBe(true);
expect(helper.state.query).toBe('not empty');
- const refine = rendering.mock.calls[0][0].refine;
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ })
+ );
+
+ const refine = rendering.mock.calls[1][0].refine;
refine();
- widget.render({
- helper,
- state: helper.state,
- createURL: () => '#',
- });
+
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ })
+ );
expect(helper.hasRefinements('facet1')).toBe(true);
expect(helper.hasRefinements('facet2')).toBe(true);
expect(helper.hasRefinements('facet3')).toBe(false);
expect(helper.state.query).toBe('');
- expect(rendering.mock.calls[1][0].hasRefinements).toBe(false);
+ expect(rendering.mock.calls[2][0].hasRefinements).toBe(false);
});
describe('createURL', () => {
it('consistent with the list of excludedAttributes', () => {
- const helper = jsHelper({}, '', {
+ const helper = jsHelper({}, 'indexName', {
facets: ['facet', 'otherFacet'],
});
helper.search = () => {};
@@ -509,13 +622,23 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/clear-refin
{
helper.setQuery('not empty');
- widget.init({
- helper,
- state: helper.state,
- createURL: opts => opts,
- });
+ widget.init(
+ createInitOptions({
+ helper,
+ state: helper.state,
+ })
+ );
+
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ createURL: state => state,
+ })
+ );
- const { createURL, refine } = rendering.mock.calls[0][0];
+ const { createURL, refine } = rendering.mock.calls[1][0];
// The state represented by the URL should be equal to a state
// after refining.
@@ -527,14 +650,16 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/clear-refin
}
{
- widget.render({
- helper,
- state: helper.state,
- results: new SearchResults(helper.state, [{}]),
- createURL: () => '#',
- });
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ createURL: state => state,
+ })
+ );
- const { createURL, refine } = rendering.mock.calls[1][0];
+ const { createURL, refine } = rendering.mock.calls[2][0];
const createURLState = createURL();
refine();
@@ -546,7 +671,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/clear-refin
});
it('reset the page to 0', () => {
- const helper = jsHelper({}, '', {});
+ const helper = jsHelper({}, 'indexName', {});
helper.search = () => {};
helper.setQuery('not empty');
@@ -554,15 +679,26 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/clear-refin
const makeWidget = connectClearRefinements(rendering);
const widget = makeWidget({});
- widget.init({
- helper,
- state: helper.state,
- createURL: () => '#',
- });
- const clearRefinements = rendering.mock.calls[0][0].refine;
+ widget.init(
+ createInitOptions({
+ helper,
+ state: helper.state,
+ })
+ );
+
+ widget.render(
+ createRenderOptions({
+ results: new SearchResults(helper.state, [{}]),
+ helper,
+ state: helper.state,
+ })
+ );
+
+ const refine = rendering.mock.calls[1][0].refine;
helper.setPage(2);
- clearRefinements();
+ refine();
+
expect(helper.state.page).toBe(0);
});
});
diff --git a/src/connectors/clear-refinements/connectClearRefinements.js b/src/connectors/clear-refinements/connectClearRefinements.js
index c0ac65e925..fa2a3c4ba7 100644
--- a/src/connectors/clear-refinements/connectClearRefinements.js
+++ b/src/connectors/clear-refinements/connectClearRefinements.js
@@ -4,6 +4,8 @@ import {
getRefinements,
createDocumentationMessageGenerator,
noop,
+ uniq,
+ mergeSearchParameters,
} from '../../lib/utils';
const withUsage = createDocumentationMessageGenerator({
@@ -86,52 +88,23 @@ export default function connectClearRefinements(renderFn, unmountFn = noop) {
transformItems = items => items,
} = widgetParams;
- return {
- $$type: 'ais.clearRefinements',
+ const connectorState = {
+ refine: noop,
+ createURL: () => '',
+ };
- init({ helper, instantSearchInstance, createURL }) {
- const attributesToClear = getAttributesToClear({
- helper,
- includedAttributes,
- excludedAttributes,
- transformItems,
- });
- const hasRefinements = attributesToClear.length > 0;
-
- this._refine = () => {
- helper
- .setState(
- clearRefinements({
- helper,
- attributesToClear: getAttributesToClear({
- helper,
- includedAttributes,
- excludedAttributes,
- transformItems,
- }),
- })
- )
- .search();
- };
+ const cachedRefine = () => connectorState.refine();
+ const cachedCreateURL = () => connectorState.createURL();
- this._createURL = () =>
- createURL(
- clearRefinements({
- helper,
- attributesToClear: getAttributesToClear({
- helper,
- includedAttributes,
- excludedAttributes,
- transformItems,
- }),
- })
- );
+ return {
+ $$type: 'ais.clearRefinements',
+ init({ instantSearchInstance }) {
renderFn(
{
- hasRefinements,
- refine: this._refine,
- createURL: this._createURL,
+ hasRefinements: false,
+ refine: cachedRefine,
+ createURL: cachedCreateURL,
instantSearchInstance,
widgetParams,
},
@@ -139,20 +112,53 @@ export default function connectClearRefinements(renderFn, unmountFn = noop) {
);
},
- render({ helper, instantSearchInstance }) {
- const attributesToClear = getAttributesToClear({
- helper,
- includedAttributes,
- excludedAttributes,
- transformItems,
- });
- const hasRefinements = attributesToClear.length > 0;
+ render({ scopedResults, createURL, instantSearchInstance }) {
+ const attributesToClear = scopedResults.reduce(
+ (results, scopedResult) => {
+ return results.concat(
+ getAttributesToClear({
+ scopedResult,
+ includedAttributes,
+ excludedAttributes,
+ transformItems,
+ })
+ );
+ },
+ []
+ );
+
+ connectorState.refine = () => {
+ attributesToClear.forEach(({ helper: indexHelper, items }) => {
+ indexHelper
+ .setState(
+ clearRefinements({
+ helper: indexHelper,
+ attributesToClear: items,
+ })
+ )
+ .search();
+ });
+ };
+
+ connectorState.createURL = () =>
+ createURL(
+ mergeSearchParameters(
+ ...attributesToClear.map(({ helper: indexHelper, items }) => {
+ return clearRefinements({
+ helper: indexHelper,
+ attributesToClear: items,
+ });
+ })
+ )
+ );
renderFn(
{
- hasRefinements,
- refine: this._refine,
- createURL: this._createURL,
+ hasRefinements: attributesToClear.some(
+ attributeToClear => attributeToClear.items.length > 0
+ ),
+ refine: cachedRefine,
+ createURL: cachedCreateURL,
instantSearchInstance,
widgetParams,
},
@@ -168,7 +174,7 @@ export default function connectClearRefinements(renderFn, unmountFn = noop) {
}
function getAttributesToClear({
- helper,
+ scopedResult,
includedAttributes,
excludedAttributes,
transformItems,
@@ -177,22 +183,31 @@ function getAttributesToClear({
includedAttributes.indexOf('query') !== -1 ||
excludedAttributes.indexOf('query') === -1;
- return transformItems(
- getRefinements(helper.lastResults || {}, helper.state, clearsQuery)
- .map(refinement => refinement.attribute)
- .filter(
- attribute =>
- // If the array is empty (default case), we keep all the attributes
- includedAttributes.length === 0 ||
- // Otherwise, only add the specified attributes
- includedAttributes.indexOf(attribute) !== -1
- )
- .filter(
- attribute =>
- // If the query is included, we ignore the default `excludedAttributes = ['query']`
- (attribute === 'query' && clearsQuery) ||
- // Otherwise, ignore the excluded attributes
- excludedAttributes.indexOf(attribute) === -1
+ return {
+ helper: scopedResult.helper,
+ items: transformItems(
+ uniq(
+ getRefinements(
+ scopedResult.results,
+ scopedResult.helper.state,
+ clearsQuery
+ )
+ .map(refinement => refinement.attribute)
+ .filter(
+ attribute =>
+ // If the array is empty (default case), we keep all the attributes
+ includedAttributes.length === 0 ||
+ // Otherwise, only add the specified attributes
+ includedAttributes.indexOf(attribute) !== -1
+ )
+ .filter(
+ attribute =>
+ // If the query is included, we ignore the default `excludedAttributes = ['query']`
+ (attribute === 'query' && clearsQuery) ||
+ // Otherwise, ignore the excluded attributes
+ excludedAttributes.indexOf(attribute) === -1
+ )
)
- );
+ ),
+ };
}
diff --git a/src/widgets/clear-refinements/__tests__/__snapshots__/clear-refinements-test.js.snap b/src/widgets/clear-refinements/__tests__/__snapshots__/clear-refinements-test.js.snap
index 808c6b3cd0..00778b0e4b 100644
--- a/src/widgets/clear-refinements/__tests__/__snapshots__/clear-refinements-test.js.snap
+++ b/src/widgets/clear-refinements/__tests__/__snapshots__/clear-refinements-test.js.snap
@@ -1,21 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`clearRefinements() cssClasses should add the default CSS classes 1`] = `
-Object {
- "button": "ais-ClearRefinements-button",
- "disabledButton": "ais-ClearRefinements-button--disabled",
- "root": "ais-ClearRefinements",
-}
-`;
-
-exports[`clearRefinements() cssClasses should allow overriding CSS classes 1`] = `
-Object {
- "button": "ais-ClearRefinements-button myButton myPrimaryButton",
- "disabledButton": "ais-ClearRefinements-button--disabled disabled",
- "root": "ais-ClearRefinements myRoot",
-}
-`;
-
exports[`clearRefinements() with refinements calls twice render(, container) 1`] = `
{
const module = require.requireActual('preact-compat');
@@ -24,119 +28,87 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/clear-refin
});
describe('clearRefinements()', () => {
- let container;
- let results;
- let client;
- let helper;
- let createURL;
-
beforeEach(() => {
- createURL = jest.fn().mockReturnValue('#all-cleared');
- container = document.createElement('div');
- results = {};
- client = algoliasearch('APP_ID', 'API_KEY');
- helper = {
- state: {
- clearRefinements: jest.fn().mockReturnThis(),
- clearTags: jest.fn().mockReturnThis(),
- },
- search: jest.fn(),
- };
-
render.mockClear();
});
describe('without refinements', () => {
- beforeEach(() => {
- helper.state.facetsRefinements = {};
- });
-
it('calls twice render(, container)', () => {
+ const helper = algoliasearchHelper(createSearchClient(), 'indexName', {
+ facetsRefinements: {},
+ });
+ const container = document.createElement('div');
const widget = clearRefinements({
container,
});
- widget.init({
- helper,
- createURL,
- instantSearchInstance: {
- templatesConfig: {},
- },
- });
- widget.render({
- results,
- helper,
- state: helper.state,
- createURL,
- instantSearchInstance: {},
- });
- widget.render({
- results,
- helper,
- state: helper.state,
- createURL,
- instantSearchInstance: {},
- });
+ widget.init(createInitOptions({ helper }));
+ widget.render(createRenderOptions({ helper, state: helper.state }));
+ widget.render(createRenderOptions({ helper, state: helper.state }));
expect(render).toHaveBeenCalledTimes(2);
- expect(render.mock.calls[0][0]).toMatchSnapshot();
- expect(render.mock.calls[0][1]).toEqual(container);
+ const [firstRender, secondRender] = render.mock.calls;
- expect(render.mock.calls[1][0]).toMatchSnapshot();
- expect(render.mock.calls[1][1]).toEqual(container);
+ expect(firstRender[0]).toMatchSnapshot();
+ expect(firstRender[1]).toEqual(container);
+
+ expect(secondRender[0]).toMatchSnapshot();
+ expect(secondRender[1]).toEqual(container);
});
});
describe('with refinements', () => {
- beforeEach(() => {
- helper.state.facetsRefinements = { something: ['something'] };
- });
-
it('calls twice render(, container)', () => {
+ const helper = algoliasearchHelper(createSearchClient(), 'indexName', {
+ facetsRefinements: {
+ facet: ['value'],
+ },
+ });
+ const container = document.createElement('div');
const widget = clearRefinements({
container,
});
- widget.init({
- helper,
- createURL,
- instantSearchInstance: {
- templatesConfig: {},
- },
- });
- widget.render({ results, helper, state: helper.state, createURL });
- widget.render({ results, helper, state: helper.state, createURL });
+
+ widget.init(createInitOptions({ helper }));
+ widget.render(createRenderOptions({ helper, state: helper.state }));
+ widget.render(createRenderOptions({ helper, state: helper.state }));
expect(render).toHaveBeenCalledTimes(2);
- expect(render.mock.calls[0][0]).toMatchSnapshot();
- expect(render.mock.calls[0][1]).toEqual(container);
+ const [firstRender, secondRender] = render.mock.calls;
+
+ expect(firstRender[0]).toMatchSnapshot();
+ expect(firstRender[1]).toEqual(container);
- expect(render.mock.calls[1][0]).toMatchSnapshot();
- expect(render.mock.calls[1][1]).toEqual(container);
+ expect(secondRender[0]).toMatchSnapshot();
+ expect(secondRender[1]).toEqual(container);
});
});
describe('cssClasses', () => {
it('should add the default CSS classes', () => {
- helper = algoliasearchHelper(client, 'index_name');
+ const helper = algoliasearchHelper(createSearchClient(), 'indexName');
+ const container = document.createElement('div');
const widget = clearRefinements({
container,
});
- widget.init({
- helper,
- createURL,
- instantSearchInstance: {
- templatesConfig: {},
- },
- });
+ widget.init(createInitOptions({ helper }));
+ widget.render(createRenderOptions({ helper, state: helper.state }));
- widget.render({ results, helper, state: helper.state, createURL });
- expect(render.mock.calls[0][0].props.cssClasses).toMatchSnapshot();
+ expect(render.mock.calls[0][0].props.cssClasses).toMatchInlineSnapshot(`
+ Object {
+ "button": "ais-ClearRefinements-button",
+ "disabledButton": "ais-ClearRefinements-button--disabled",
+ "root": "ais-ClearRefinements",
+ }
+ `);
});
it('should allow overriding CSS classes', () => {
+ const helper = algoliasearchHelper(createSearchClient(), 'indexName');
+ const container = document.createElement('div');
const widget = clearRefinements({
container,
cssClasses: {
@@ -145,16 +117,17 @@ describe('clearRefinements()', () => {
disabledButton: ['disabled'],
},
});
- widget.init({
- helper,
- createURL,
- instantSearchInstance: {
- templatesConfig: {},
- },
- });
- widget.render({ results, helper, state: helper.state, createURL });
- expect(render.mock.calls[0][0].props.cssClasses).toMatchSnapshot();
+ widget.init(createInitOptions({ helper }));
+ widget.render(createRenderOptions({ helper, state: helper.state }));
+
+ expect(render.mock.calls[0][0].props.cssClasses).toMatchInlineSnapshot(`
+ Object {
+ "button": "ais-ClearRefinements-button myButton myPrimaryButton",
+ "disabledButton": "ais-ClearRefinements-button--disabled disabled",
+ "root": "ais-ClearRefinements myRoot",
+ }
+ `);
});
});
});
diff --git a/stories/clear-refinements.stories.js b/stories/clear-refinements.stories.js
index ae4b0e26ed..945aabf1de 100644
--- a/stories/clear-refinements.stories.js
+++ b/stories/clear-refinements.stories.js
@@ -88,4 +88,54 @@ storiesOf('ClearRefinements', module)
},
}
)
+ )
+ .add(
+ 'with multi indices',
+ withHits(({ search, container, instantsearch }) => {
+ const clearRefinementsContainer = document.createElement('div');
+ const instantSearchPriceAscTitle = document.createElement('h3');
+ instantSearchPriceAscTitle.innerHTML =
+ 'instant_search_price_asc
';
+ const instantSearchMediaTitle = document.createElement('h3');
+ instantSearchMediaTitle.innerHTML =
+ 'instant_search_rating_asc
';
+ const refinementListContainer1 = document.createElement('div');
+ const refinementListContainer2 = document.createElement('div');
+
+ container.appendChild(clearRefinementsContainer);
+ container.appendChild(instantSearchPriceAscTitle);
+ container.appendChild(refinementListContainer1);
+ container.appendChild(instantSearchMediaTitle);
+ container.appendChild(refinementListContainer2);
+
+ search.addWidgets([
+ instantsearch.widgets.clearRefinements({
+ container: clearRefinementsContainer,
+ }),
+
+ instantsearch.widgets
+ .index({
+ indexName: 'instant_search_price_asc',
+ })
+ .addWidgets([
+ instantsearch.widgets.refinementList({
+ container: refinementListContainer1,
+ attribute: 'brand',
+ limit: 3,
+ }),
+ ]),
+
+ instantsearch.widgets
+ .index({
+ indexName: 'instant_search_rating_asc',
+ })
+ .addWidgets([
+ instantsearch.widgets.refinementList({
+ container: refinementListContainer2,
+ attribute: 'categories',
+ limit: 3,
+ }),
+ ]),
+ ]);
+ })
);
diff --git a/test/mock/createWidget.ts b/test/mock/createWidget.ts
index fb5cd1eed5..c84263d565 100644
--- a/test/mock/createWidget.ts
+++ b/test/mock/createWidget.ts
@@ -30,6 +30,7 @@ export const createRenderOptions = (
): RenderOptions => {
const { instantSearchInstance = createInstantSearch(), ...rest } = args;
const response = createMultiSearchResponse();
+ const helper = args.helper || instantSearchInstance.helper!;
const results = new algolisearchHelper.SearchResults(
instantSearchInstance.helper!.state,
response.results
@@ -38,14 +39,14 @@ export const createRenderOptions = (
return {
instantSearchInstance,
templatesConfig: instantSearchInstance.templatesConfig,
- helper: instantSearchInstance.helper!,
- state: instantSearchInstance.helper!.state,
+ helper,
+ state: helper.state,
results,
scopedResults: [
{
- indexId: instantSearchInstance.helper!.state.index,
+ indexId: helper.state.index,
+ helper,
results,
- helper: instantSearchInstance.helper!,
},
],
searchMetadata: {