From 316b8f5a672ed8528d5cd54754eb9a52292a9087 Mon Sep 17 00:00:00 2001 From: Haroen Viaene Date: Thu, 11 Jan 2018 19:06:07 +0100 Subject: [PATCH] fix(currentRefinements): give access to id and index from transformItems for deduplication (#830) **Summary** The currentRefinemements widget (and connector) will give exactly the same data as the widgets which are present in the layout. As seen in #258, when there are two refinements with the same attribute, this will give those items the same `key` and render with warnings. **Result** 1. Give access to `index` and `id` (`${attributeName}`, `query`, ...) to `tranformItems` so users can deduplicate themselves 2. test how the deduplication can be done --- .../connectors/connectCurrentRefinements.js | 8 ++- .../connectCurrentRefinements.test.js | 58 +++++++++++++++---- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/packages/react-instantsearch/src/connectors/connectCurrentRefinements.js b/packages/react-instantsearch/src/connectors/connectCurrentRefinements.js index d0272ed0d2..cc4d1519a1 100644 --- a/packages/react-instantsearch/src/connectors/connectCurrentRefinements.js +++ b/packages/react-instantsearch/src/connectors/connectCurrentRefinements.js @@ -33,7 +33,13 @@ export default createConnector({ ) { return res; } - return res.concat(meta.items); + return res.concat( + meta.items.map(item => ({ + ...item, + id: meta.id, + index: meta.index, + })) + ); } } return res; diff --git a/packages/react-instantsearch/src/connectors/connectCurrentRefinements.test.js b/packages/react-instantsearch/src/connectors/connectCurrentRefinements.test.js index aa5931a8f4..16b7bd2598 100644 --- a/packages/react-instantsearch/src/connectors/connectCurrentRefinements.test.js +++ b/packages/react-instantsearch/src/connectors/connectCurrentRefinements.test.js @@ -11,22 +11,29 @@ const getProvidedProps = connect.getProvidedProps.bind(context); describe('connectCurrentRefinements', () => { it('provides the correct props to the component', () => { let props = getProvidedProps({}, null, null, [ - { items: ['one'] }, - { items: ['two'] }, - { items: ['three'], id: 'query' }, + { items: [{ label: 'one' }], id: 1, index: 'something' }, + { items: [{ label: 'two' }], id: 2, index: 'something' }, + { items: [{ label: 'three' }], id: 'query', index: 'something' }, + ]); + expect(props.items).toEqual([ + { id: 1, index: 'something', label: 'one' }, + { id: 2, index: 'something', label: 'two' }, ]); - expect(props.items).toEqual(['one', 'two']); props = getProvidedProps({}, null, null, []); expect(props).toEqual({ canRefine: false, items: [] }); const transformItems = jest.fn(() => ['items']); props = getProvidedProps({ transformItems }, null, null, [ - { items: ['one'] }, - { items: ['two'] }, - { items: ['three'] }, + { items: [{ label: 'one' }], id: 1, index: 'something' }, + { items: [{ label: 'two' }], id: 2, index: 'something' }, + { items: [{ label: 'three' }], id: 3, index: 'something' }, + ]); + expect(transformItems.mock.calls[0][0]).toEqual([ + { id: 1, index: 'something', label: 'one' }, + { id: 2, index: 'something', label: 'two' }, + { id: 3, index: 'something', label: 'three' }, ]); - expect(transformItems.mock.calls[0][0]).toEqual(['one', 'two', 'three']); expect(props.items).toEqual(['items']); }); @@ -38,10 +45,12 @@ describe('connectCurrentRefinements', () => { }; const props = getProvidedProps({ clearsQuery: true }, null, { results }, [ - { items: [{ currentRefinement: 'query' }], id: 'query' }, + { items: [{ currentRefinement: 'query' }], id: 'query', index: '' }, ]); - expect(props.items).toEqual([{ currentRefinement: 'query' }]); + expect(props.items).toEqual([ + { currentRefinement: 'query', id: 'query', index: '' }, + ]); }); it('dont provide the query if clearsQuery props is true but the current refinement is an empty string', () => { @@ -73,4 +82,33 @@ describe('connectCurrentRefinements', () => { ]); expect(searchState).toEqual({ wow: 'sweet', cool: 'neat' }); }); + + it('deduplicates entries with transformItems', () => { + const transformItems = items => + items + .map(({ id, index, ...rest }) => ({ + __dedupe: `${index}.${id}`, + id, + index, + ...rest, + })) + .sort((a, b) => a.id > b.__dedupe) + .filter( + (current, index, array) => + index === 0 || current.__dedupe !== array[index - 1].__dedupe + ) + // eslint-disable-next-line no-unused-vars + .map(({ __dedupe, ...item }) => item); + + const props = getProvidedProps({ transformItems }, null, null, [ + { items: [{ label: 'abra' }], id: 1, index: 'something' }, + { items: [{ label: 'cadabra' }], id: 2, index: 'something' }, + { items: [{ label: 'cadabra' }], id: 2, index: 'something' }, + ]); + + expect(props.items).toEqual([ + { id: 1, index: 'something', label: 'abra' }, + { id: 2, index: 'something', label: 'cadabra' }, + ]); + }); });