From e75bbb31775e9cf52fdb19af9d08a272430a4cd5 Mon Sep 17 00:00:00 2001 From: Haroen Viaene Date: Mon, 18 Mar 2019 13:22:29 +0100 Subject: [PATCH] feat(context): migrate base connector [STEP 2] This is enough changes to be able to look at the storybook without errors again (only pagination, hits, searchbox of course, and nothing multi-index) For full roadmap, see #2178 --- .../connectors/__tests__/connectConfigure.js | 74 +++++++++++++------ .../src/connectors/__tests__/connectHits.js | 17 +++-- .../connectors/__tests__/connectPagination.js | 44 ++++++----- .../connectors/__tests__/connectSearchBox.js | 38 ++++++---- .../src/connectors/connectConfigure.js | 21 ++++-- .../src/connectors/connectHits.js | 14 +++- .../src/connectors/connectPagination.js | 12 +-- .../src/connectors/connectSearchBox.js | 25 ++++--- 8 files changed, 156 insertions(+), 89 deletions(-) diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectConfigure.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectConfigure.js index 26c5746a92..b345cabf28 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectConfigure.js +++ b/packages/react-instantsearch-core/src/connectors/__tests__/connectConfigure.js @@ -5,29 +5,52 @@ jest.mock('../../core/createConnector', () => x => x); describe('connectConfigure', () => { describe('single index', () => { - const context = { context: { ais: { mainTargetedIndex: 'index' } } }; - const getSearchParameters = connect.getSearchParameters.bind(context); - const transitionState = connect.transitionState.bind(context); - const cleanUp = connect.cleanUp.bind(context); + const contextValue = { ais: { mainTargetedIndex: 'index' } }; - it('propagates the props to the SearchParameters without children', () => { - const searchParameters = getSearchParameters( + it('propagates the props to the SearchParameters without children & contextValue', () => { + const searchParameters = connect.getSearchParameters( new SearchParameters(), - { distinct: 1, whatever: 'please', children: 'whatever' }, + { distinct: 1, whatever: 'please', children: 'whatever', contextValue }, {} ); + + expect(searchParameters).toEqual( + expect.objectContaining({ + distinct: 1, + whatever: 'please', + }) + ); + expect(searchParameters).not.toEqual( + expect.objectContaining({ + children: 'whatever', + contextValue, + }) + ); expect(searchParameters.getQueryParameter('distinct')).toEqual(1); expect(searchParameters.getQueryParameter('whatever')).toEqual('please'); expect( searchParameters.getQueryParameter.bind(searchParameters, 'children') - ).toThrow(); + ).toThrowErrorMatchingInlineSnapshot( + `"Parameter 'children' is not an attribute of SearchParameters (http://algolia.github.io/algoliasearch-helper-js/docs/SearchParameters.html)"` + ); + expect( + searchParameters.getQueryParameter.bind( + searchParameters, + 'contextValue' + ) + ).toThrowErrorMatchingInlineSnapshot( + `"Parameter 'contextValue' is not an attribute of SearchParameters (http://algolia.github.io/algoliasearch-helper-js/docs/SearchParameters.html)"` + ); }); it('calling transitionState should add configure parameters to the search state', () => { - const providedThis = {}; - let searchState = transitionState.call( - providedThis, - { distinct: 1, whatever: 'please', children: 'whatever' }, + let searchState = connect.transitionState( + { + distinct: 1, + whatever: 'please', + children: 'whatever', + contextValue, + }, {}, {} ); @@ -35,9 +58,8 @@ describe('connectConfigure', () => { configure: { distinct: 1, whatever: 'please' }, }); - searchState = transitionState.call( - providedThis, - { whatever: 'other', children: 'whatever' }, + searchState = connect.transitionState( + { whatever: 'other', children: 'whatever', contextValue }, { configure: { distinct: 1, whatever: 'please' } }, { configure: { distinct: 1, whatever: 'please' } } ); @@ -46,22 +68,32 @@ describe('connectConfigure', () => { }); it('calling cleanUp should remove configure parameters from the search state', () => { - let searchState = cleanUp( - { distinct: 1, whatever: 'please', children: 'whatever' }, + let searchState = connect.cleanUp( { - configure: { distinct: 1, whatever: 'please', another: 'parameters' }, + distinct: 1, + whatever: 'please', + children: 'whatever', + contextValue, + }, + { + configure: { + distinct: 1, + whatever: 'please', + another: 'parameters', + }, } ); expect(searchState).toEqual({ configure: { another: 'parameters' } }); - searchState = cleanUp( - { distinct: 1, whatever: 'please', children: 'whatever' }, + searchState = connect.cleanUp( + { distinct: 1, whatever: 'please', children: 'whatever', contextValue }, { configure: { distinct: 1, whatever: 'please' } } ); expect(searchState).toEqual({ configure: {} }); }); }); - describe('multi index', () => { + + describe.skip('multi index', () => { let context = { context: { ais: { mainTargetedIndex: 'first' }, diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectHits.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectHits.js index 0af790c701..cdcdb5ddb1 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectHits.js +++ b/packages/react-instantsearch-core/src/connectors/__tests__/connectHits.js @@ -6,11 +6,11 @@ const { getSearchParameters } = connect; describe('connectHits', () => { describe('single index', () => { - const context = { context: { ais: { mainTargetedIndex: 'index' } } }; - const getProvidedProps = connect.getProvidedProps.bind(context); + const contextValue = { mainTargetedIndex: 'index' }; + it('provides the current hits to the component', () => { const hits = [{}]; - const props = getProvidedProps(null, null, { + const props = connect.getProvidedProps({ contextValue }, null, { results: { hits, hitsPerPage: 2, page: 2 }, }); expect(props).toEqual({ @@ -20,7 +20,7 @@ describe('connectHits', () => { it('adds positions to the hits provided to the component', () => { const hits = [{}]; - const props = getProvidedProps(null, null, { + const props = connect.getProvidedProps({ contextValue }, null, { results: { hits, hitsPerPage: 2, page: 2 }, }); expect(props).toEqual({ @@ -30,7 +30,7 @@ describe('connectHits', () => { it('adds queryID to the hits provided to the component', () => { const hits = [{}]; - const props = getProvidedProps(null, null, { + const props = connect.getProvidedProps({ contextValue }, null, { results: { hits, hitsPerPage: 2, page: 2, queryID: 'theQueryID' }, }); expect(props).toEqual({ @@ -39,7 +39,9 @@ describe('connectHits', () => { }); it("doesn't render when no hits are available", () => { - const props = getProvidedProps(null, null, { results: null }); + const props = connect.getProvidedProps({ contextValue }, null, { + results: null, + }); expect(props).toEqual({ hits: [] }); }); @@ -48,7 +50,8 @@ describe('connectHits', () => { expect(searchParameters).toEqual({ hitsPerPage: 10 }); }); }); - describe('multi index', () => { + + describe.skip('multi index', () => { const context = { context: { ais: { mainTargetedIndex: 'first' }, diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectPagination.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectPagination.js index f2528f7972..ef130e1c9a 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectPagination.js +++ b/packages/react-instantsearch-core/src/connectors/__tests__/connectPagination.js @@ -8,23 +8,22 @@ let params; describe('connectPagination', () => { describe('single index', () => { - const context = { context: { ais: { mainTargetedIndex: 'index' } } }; - const getProvidedProps = connect.getProvidedProps.bind(context); - const refine = connect.refine.bind(context); - const getSP = connect.getSearchParameters.bind(context); - const getMetadata = connect.getMetadata.bind(context); - const cleanUp = connect.cleanUp.bind(context); + const contextValue = { mainTargetedIndex: 'index' }; it('provides the correct props to the component', () => { - props = getProvidedProps({}, {}, { results: { nbPages: 666, hits: [] } }); + props = connect.getProvidedProps( + { contextValue }, + {}, + { results: { nbPages: 666, hits: [] } } + ); expect(props).toEqual({ currentRefinement: 1, nbPages: 666, canRefine: true, }); - props = getProvidedProps( - {}, + props = connect.getProvidedProps( + { contextValue }, { page: 5 }, { results: { nbPages: 666, hits: [] } } ); @@ -34,8 +33,8 @@ describe('connectPagination', () => { canRefine: true, }); - props = getProvidedProps( - {}, + props = connect.getProvidedProps( + { contextValue }, { page: '5' }, { results: { nbPages: 666, hits: [] } } ); @@ -45,8 +44,8 @@ describe('connectPagination', () => { canRefine: true, }); - props = getProvidedProps( - {}, + props = connect.getProvidedProps( + { contextValue }, { page: '1' }, { results: { nbPages: 1, hits: [] } } ); @@ -58,12 +57,12 @@ describe('connectPagination', () => { }); it("doesn't render when no results are available", () => { - props = getProvidedProps({}, {}, {}); + props = connect.getProvidedProps({ contextValue }, {}, {}); expect(props).toBe(null); }); it("calling refine updates the widget's search state", () => { - const nextState = refine({}, { otherKey: 'val' }, 'yep'); + const nextState = connect.refine({}, { otherKey: 'val' }, 'yep'); expect(nextState).toEqual({ otherKey: 'val', page: 'yep', @@ -72,18 +71,22 @@ describe('connectPagination', () => { it('refines the page parameter', () => { const initSP = new SearchParameters(); - params = getSP(initSP, {}, { page: 667 }); + params = connect.getSearchParameters( + initSP, + { contextValue }, + { page: 667 } + ); expect(params.page).toBe(666); }); it('registers its id in metadata', () => { - const metadata = getMetadata({}, {}); + const metadata = connect.getMetadata({ contextValue }, {}); expect(metadata).toEqual({ id: 'page' }); }); it('should return the right searchState when clean up', () => { - const newState = cleanUp( - {}, + const newState = connect.cleanUp( + { contextValue }, { page: { searchState: 'searchState' }, another: { searchState: 'searchState' }, @@ -92,7 +95,8 @@ describe('connectPagination', () => { expect(newState).toEqual({ another: { searchState: 'searchState' } }); }); }); - describe('multi index', () => { + + describe.skip('multi index', () => { let context = { context: { ais: { mainTargetedIndex: 'first' }, diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectSearchBox.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectSearchBox.js index bfad319eac..df792d0e48 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectSearchBox.js +++ b/packages/react-instantsearch-core/src/connectors/__tests__/connectSearchBox.js @@ -8,21 +8,22 @@ let params; describe('connectSearchBox', () => { describe('single index', () => { - const context = { context: { ais: { mainTargetedIndex: 'index' } } }; - const getProvidedProps = connect.getProvidedProps.bind(context); - const refine = connect.refine.bind(context); - const getSP = connect.getSearchParameters.bind(context); - const cleanUp = connect.cleanUp.bind(context); + const contextValue = { mainTargetedIndex: 'index' }; + it('provides the correct props to the component', () => { - props = getProvidedProps({}, {}, {}); + props = connect.getProvidedProps({ contextValue }, {}, {}); expect(props).toEqual({ currentRefinement: '' }); - props = getProvidedProps({}, { query: 'yep' }, {}); + props = connect.getProvidedProps({ contextValue }, { query: 'yep' }, {}); expect(props).toEqual({ currentRefinement: 'yep' }); }); it("calling refine updates the widget's search state", () => { - const nextState = refine({}, { otherKey: 'val' }, 'yep'); + const nextState = connect.refine( + { contextValue }, + { otherKey: 'val' }, + 'yep' + ); expect(nextState).toEqual({ otherKey: 'val', page: 1, @@ -31,19 +32,29 @@ describe('connectSearchBox', () => { }); it('supports defaultRefinement', () => { - expect(getProvidedProps({ defaultRefinement: 'yaw' }, {}, {})).toEqual({ + expect( + connect.getProvidedProps( + { defaultRefinement: 'yaw', contextValue }, + {}, + {} + ) + ).toEqual({ currentRefinement: 'yaw', }); }); it('refines the query parameter', () => { - params = getSP(new SearchParameters(), {}, { query: 'bar' }); + params = connect.getSearchParameters( + new SearchParameters(), + { contextValue }, + { query: 'bar' } + ); expect(params.query).toBe('bar'); }); it('should return the right searchState when clean up', () => { - const searchState = cleanUp( - {}, + const searchState = connect.cleanUp( + { contextValue }, { query: { searchState: 'searchState' }, another: { searchState: 'searchState' }, @@ -52,7 +63,8 @@ describe('connectSearchBox', () => { expect(searchState).toEqual({ another: { searchState: 'searchState' } }); }); }); - describe('multi index', () => { + + describe.skip('multi index', () => { let context = { context: { ais: { mainTargetedIndex: 'first' }, diff --git a/packages/react-instantsearch-core/src/connectors/connectConfigure.js b/packages/react-instantsearch-core/src/connectors/connectConfigure.js index 24f5c9e411..5e796caf97 100644 --- a/packages/react-instantsearch-core/src/connectors/connectConfigure.js +++ b/packages/react-instantsearch-core/src/connectors/connectConfigure.js @@ -1,4 +1,4 @@ -import { omit, difference, keys } from 'lodash'; +import { omit, difference } from 'lodash'; import createConnector from '../core/createConnector'; import { refineValue, @@ -16,37 +16,42 @@ export default createConnector({ return {}; }, getSearchParameters(searchParameters, props) { - const items = omit(props, 'children'); + const items = omit(props, 'children', 'contextValue'); return searchParameters.setQueryParameters(items); }, transitionState(props, prevSearchState, nextSearchState) { const id = getId(); - const items = omit(props, 'children'); + const items = omit(props, 'children', 'contextValue'); const nonPresentKeys = this._props - ? difference(keys(this._props), keys(props)) + ? difference(Object.keys(this._props), Object.keys(props)) : []; this._props = props; const nextValue = { [id]: { ...omit(nextSearchState[id], nonPresentKeys), ...items }, }; - return refineValue(nextSearchState, nextValue, this.context); + return refineValue(nextSearchState, nextValue, { ais: props.contextValue }); }, cleanUp(props, searchState) { const id = getId(); - const indexId = getIndexId(this.context); + const indexId = getIndexId({ ais: props.contextValue }); + const subState = - hasMultipleIndices(this.context) && searchState.indices + hasMultipleIndices(props.contextValue) && searchState.indices ? searchState.indices[indexId] : searchState; + const configureKeys = subState && subState[id] ? Object.keys(subState[id]) : []; + const configureState = configureKeys.reduce((acc, item) => { if (!props[item]) { acc[item] = subState[id][item]; } return acc; }, {}); + const nextValue = { [id]: configureState }; - return refineValue(searchState, nextValue, this.context); + + return refineValue(searchState, nextValue, { ais: props.contextValue }); }, }); diff --git a/packages/react-instantsearch-core/src/connectors/connectHits.js b/packages/react-instantsearch-core/src/connectors/connectHits.js index eea2678b6c..b394affa85 100644 --- a/packages/react-instantsearch-core/src/connectors/connectHits.js +++ b/packages/react-instantsearch-core/src/connectors/connectHits.js @@ -45,7 +45,7 @@ export default createConnector({ displayName: 'AlgoliaHits', getProvidedProps(props, searchState, searchResults) { - const results = getResults(searchResults, this.context); + const results = getResults(searchResults, { ais: props.contextValue }); if (!results) { return { hits: [] }; } @@ -61,9 +61,15 @@ export default createConnector({ return { hits: hitsWithPositionsAndQueryID }; }, - /* Hits needs to be considered as a widget to trigger a search if no others widgets are used. - * To be considered as a widget you need either getSearchParameters, getMetadata or getTransitionState - * See createConnector.js + /* Hits needs to be considered as a widget to trigger a search, + * even if no other widgets are used. + * + * To be considered as a widget you need either: + * - getSearchParameters + * - getMetadata + * - getTransitionState + * + * See: createConnector.tsx * */ getSearchParameters(searchParameters) { return searchParameters; diff --git a/packages/react-instantsearch-core/src/connectors/connectPagination.js b/packages/react-instantsearch-core/src/connectors/connectPagination.js index 5cb61aea84..80dcada3b9 100644 --- a/packages/react-instantsearch-core/src/connectors/connectPagination.js +++ b/packages/react-instantsearch-core/src/connectors/connectPagination.js @@ -55,7 +55,7 @@ export default createConnector({ displayName: 'AlgoliaPagination', getProvidedProps(props, searchState, searchResults) { - const results = getResults(searchResults, this.context); + const results = getResults(searchResults, { ais: props.contextValue }); if (!results) { return null; @@ -64,22 +64,24 @@ export default createConnector({ const nbPages = results.nbPages; return { nbPages, - currentRefinement: getCurrentRefinement(props, searchState, this.context), + currentRefinement: getCurrentRefinement(props, searchState, { + ais: props.contextValue, + }), canRefine: nbPages > 1, }; }, refine(props, searchState, nextPage) { - return refine(props, searchState, nextPage, this.context); + return refine(props, searchState, nextPage, { ais: props.contextValue }); }, cleanUp(props, searchState) { - return cleanUpValue(searchState, this.context, getId()); + return cleanUpValue(searchState, { ais: props.contextValue }, getId()); }, getSearchParameters(searchParameters, props, searchState) { return searchParameters.setPage( - getCurrentRefinement(props, searchState, this.context) - 1 + getCurrentRefinement(props, searchState, { ais: props.contextValue }) - 1 ); }, diff --git a/packages/react-instantsearch-core/src/connectors/connectSearchBox.js b/packages/react-instantsearch-core/src/connectors/connectSearchBox.js index 99783adb08..09a80a185a 100644 --- a/packages/react-instantsearch-core/src/connectors/connectSearchBox.js +++ b/packages/react-instantsearch-core/src/connectors/connectSearchBox.js @@ -58,42 +58,45 @@ export default createConnector({ getProvidedProps(props, searchState, searchResults) { return { - currentRefinement: getCurrentRefinement(props, searchState, this.context), + currentRefinement: getCurrentRefinement(props, searchState, { + ais: props.contextValue, + }), isSearchStalled: searchResults.isSearchStalled, }; }, refine(props, searchState, nextRefinement) { - return refine(props, searchState, nextRefinement, this.context); + return refine(props, searchState, nextRefinement, { + ais: props.contextValue, + }); }, cleanUp(props, searchState) { - return cleanUp(props, searchState, this.context); + return cleanUp(props, searchState, { ais: props.contextValue }); }, getSearchParameters(searchParameters, props, searchState) { return searchParameters.setQuery( - getCurrentRefinement(props, searchState, this.context) + getCurrentRefinement(props, searchState, { ais: props.contextValue }) ); }, getMetadata(props, searchState) { const id = getId(props); - const currentRefinement = getCurrentRefinement( - props, - searchState, - this.context - ); + const currentRefinement = getCurrentRefinement(props, searchState, { + ais: props.contextValue, + }); return { id, - index: getIndexId(this.context), + index: getIndexId({ ais: props.contextValue }), items: currentRefinement === null ? [] : [ { label: `${id}: ${currentRefinement}`, - value: nextState => refine(props, nextState, '', this.context), + value: nextState => + refine(props, nextState, '', { ais: props.contextValue }), currentRefinement, }, ],