diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectHierarchicalMenu.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectHierarchicalMenu.js index b49fbb5232..4f44c4f4cc 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectHierarchicalMenu.js +++ b/packages/react-instantsearch-core/src/connectors/__tests__/connectHierarchicalMenu.js @@ -1,4 +1,4 @@ -import { SearchParameters } from 'algoliasearch-helper'; +import { SearchResults, SearchParameters } from 'algoliasearch-helper'; import connect from '../connectHierarchicalMenu'; jest.mock('../../core/createConnector', () => x => x); @@ -174,6 +174,159 @@ describe('connectHierarchicalMenu', () => { expect(props.items).toEqual(['items']); }); + it('facetValues results uses facetOrdering by default', () => { + const props = { + ...connect.defaultProps, + attributes: ['lvl0', 'lvl1'], + contextValue, + }; + const searchState = { hierarchicalMenu: { lvl0: 'wat' } }; + const state = connect.getSearchParameters( + new SearchParameters(), + props, + searchState + ); + const results = new SearchResults(state, [ + { + hits: [], + renderingContent: { + facetOrdering: { + values: { + lvl0: { + order: ['wat'], + }, + lvl1: { + order: ['wat > wut'], + }, + }, + }, + }, + facets: { + lvl0: { + wat: 20, + oy: 10, + }, + lvl1: { + 'wat > wot': 15, + 'wat > wut': 5, + }, + }, + }, + ]); + + const providedProps = connect.getProvidedProps(props, searchState, { + results, + }); + expect(providedProps.items).toEqual([ + { + label: 'wat', + value: undefined, + count: 20, + isRefined: true, + items: [ + { + label: 'wut', + value: 'wat > wut', + count: 5, + isRefined: false, + items: null, + }, + { + label: 'wot', + value: 'wat > wot', + count: 15, + isRefined: false, + items: null, + }, + ], + }, + { + label: 'oy', + value: 'oy', + count: 10, + isRefined: false, + items: null, + }, + ]); + }); + + it('facetValues results does not use facetOrdering if disabled', () => { + const props = { + attributes: ['lvl0', 'lvl1'], + facetOrdering: false, + contextValue, + }; + const searchState = { hierarchicalMenu: { lvl0: 'wat' } }; + const state = connect.getSearchParameters( + new SearchParameters(), + props, + searchState + ); + const results = new SearchResults(state, [ + { + hits: [], + renderingContent: { + facetOrdering: { + values: { + lvl0: { + order: ['wat'], + }, + lvl1: { + order: ['wat > wut'], + }, + }, + }, + }, + facets: { + lvl0: { + wat: 20, + oy: 10, + }, + lvl1: { + 'wat > wot': 15, + 'wat > wut': 5, + }, + }, + }, + ]); + + const providedProps = connect.getProvidedProps(props, searchState, { + results, + }); + expect(providedProps.items).toEqual([ + { + label: 'oy', + value: 'oy', + count: 10, + isRefined: false, + items: null, + }, + { + label: 'wat', + value: undefined, + count: 20, + isRefined: true, + items: [ + // default ordering: alphabetical + { + label: 'wot', + value: 'wat > wot', + count: 15, + isRefined: false, + items: null, + }, + { + label: 'wut', + value: 'wat > wut', + count: 5, + isRefined: false, + items: null, + }, + ], + }, + ]); + }); + it('shows the effect of showMoreLimit when there is no transformItems', () => { const results = { getFacetValues: jest.fn(), diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectMenu.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectMenu.js index 263e87bfd1..28925a4215 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectMenu.js +++ b/packages/react-instantsearch-core/src/connectors/__tests__/connectMenu.js @@ -1,4 +1,4 @@ -import { SearchParameters } from 'algoliasearch-helper'; +import { SearchParameters, SearchResults } from 'algoliasearch-helper'; import connect from '../connectMenu'; jest.mock('../../core/createConnector', () => x => x); @@ -245,6 +245,116 @@ describe('connectMenu', () => { ]); }); + it('facetValues have facetOrdering by default', () => { + const userProps = { + ...connect.defaultProps, + attribute: 'ok', + contextValue, + }; + const searchState = { + menu: { ok: 'wat' }, + }; + const parameters = connect.getSearchParameters( + new SearchParameters(), + userProps, + searchState + ); + + const searchResults = new SearchResults(parameters, [ + { + hits: [], + renderingContent: { + facetOrdering: { + values: { + ok: { + order: ['wat'], + }, + }, + }, + }, + facets: { + ok: { + wat: 20, + lol: 2000, + }, + }, + }, + ]); + + const providedProps = connect.getProvidedProps(userProps, searchState, { + results: searchResults, + }); + + expect(providedProps.items).toEqual([ + { + count: 20, + isRefined: true, + label: 'wat', + value: '', + }, + { + count: 2000, + isRefined: false, + label: 'lol', + value: 'lol', + }, + ]); + expect(providedProps.isFromSearch).toBe(false); + }); + + it('facetValues results does not use facetOrdering if disabled', () => { + const userProps = { attribute: 'ok', facetOrdering: false, contextValue }; + const searchState = { + menu: { ok: 'wat' }, + }; + const parameters = connect.getSearchParameters( + new SearchParameters(), + userProps, + searchState + ); + + const searchResults = new SearchResults(parameters, [ + { + hits: [], + renderingContent: { + facetOrdering: { + values: { + ok: { + order: ['wat'], + }, + }, + }, + }, + facets: { + ok: { + wat: 20, + lol: 2000, + }, + }, + }, + ]); + + const providedProps = connect.getProvidedProps(userProps, searchState, { + results: searchResults, + }); + + expect(providedProps.items).toEqual([ + { + count: 2000, + isRefined: false, + label: 'lol', + value: 'lol', + }, + { + count: 20, + isRefined: true, + label: 'wat', + value: '', + }, + ]); + expect(providedProps.isFromSearch).toBe(false); + }); + it("calling refine updates the widget's search state", () => { const nextState = connect.refine( { attribute: 'ok', contextValue }, @@ -435,13 +545,14 @@ describe('connectMenu', () => { }; props = connect.getProvidedProps( - { attribute: 'ok', contextValue }, + { ...connect.defaultProps, attribute: 'ok', contextValue }, {}, { results } ); expect(results.getFacetValues).toHaveBeenCalledWith('ok', { sortBy: ['count:desc', 'name:asc'], + facetOrdering: true, }); expect(props.items).toEqual([ @@ -479,13 +590,19 @@ describe('connectMenu', () => { }; props = connect.getProvidedProps( - { attribute: 'ok', searchable: true, contextValue }, + { + ...connect.defaultProps, + attribute: 'ok', + searchable: true, + contextValue, + }, {}, { results } ); expect(results.getFacetValues).toHaveBeenCalledWith('ok', { sortBy: undefined, + facetOrdering: true, }); expect(props.items).toEqual([ diff --git a/packages/react-instantsearch-core/src/connectors/__tests__/connectRefinementList.js b/packages/react-instantsearch-core/src/connectors/__tests__/connectRefinementList.js index 21421bead3..93db3a16b0 100644 --- a/packages/react-instantsearch-core/src/connectors/__tests__/connectRefinementList.js +++ b/packages/react-instantsearch-core/src/connectors/__tests__/connectRefinementList.js @@ -1,4 +1,4 @@ -import { SearchParameters } from 'algoliasearch-helper'; +import { SearchResults, SearchParameters } from 'algoliasearch-helper'; import connect from '../connectRefinementList'; jest.mock('../../core/createConnector', () => x => x); @@ -241,6 +241,116 @@ describe('connectRefinementList', () => { expect(props.isFromSearch).toBe(true); }); + it('facetValues have facetOrdering by default', () => { + const userProps = { + ...connect.defaultProps, + attribute: 'ok', + contextValue, + }; + const searchState = { + refinementList: { ok: ['wat'] }, + }; + const parameters = connect.getSearchParameters( + new SearchParameters(), + userProps, + searchState + ); + + const searchResults = new SearchResults(parameters, [ + { + hits: [], + renderingContent: { + facetOrdering: { + values: { + ok: { + order: ['lol'], + }, + }, + }, + }, + facets: { + ok: { + wat: 20, + lol: 2000, + }, + }, + }, + ]); + + const providedProps = connect.getProvidedProps(userProps, searchState, { + results: searchResults, + }); + + expect(providedProps.items).toEqual([ + { + count: 2000, + isRefined: false, + label: 'lol', + value: ['wat', 'lol'], + }, + { + count: 20, + isRefined: true, + label: 'wat', + value: [], + }, + ]); + expect(providedProps.isFromSearch).toBe(false); + }); + + it('facetValues results does not use facetOrdering if disabled', () => { + const userProps = { attribute: 'ok', facetOrdering: false, contextValue }; + const searchState = { + refinementList: { ok: ['wat'] }, + }; + const parameters = connect.getSearchParameters( + new SearchParameters(), + userProps, + searchState + ); + + const searchResults = new SearchResults(parameters, [ + { + hits: [], + renderingContent: { + facetOrdering: { + values: { + ok: { + order: ['lol'], + }, + }, + }, + }, + facets: { + ok: { + wat: 20, + lol: 2000, + }, + }, + }, + ]); + + const providedProps = connect.getProvidedProps(userProps, searchState, { + results: searchResults, + }); + + expect(providedProps.items).toEqual([ + { + count: 20, + isRefined: true, + label: 'wat', + value: [], + }, + { + count: 2000, + isRefined: false, + label: 'lol', + value: ['wat', 'lol'], + }, + ]); + expect(providedProps.isFromSearch).toBe(false); + }); + it("calling refine updates the widget's search state", () => { const nextState = connect.refine( { attribute: 'ok', contextValue }, diff --git a/packages/react-instantsearch-core/src/connectors/connectHierarchicalMenu.js b/packages/react-instantsearch-core/src/connectors/connectHierarchicalMenu.js index 3abc77555f..65674dc68e 100644 --- a/packages/react-instantsearch-core/src/connectors/connectHierarchicalMenu.js +++ b/packages/react-instantsearch-core/src/connectors/connectHierarchicalMenu.js @@ -161,6 +161,7 @@ export default createConnector({ limit: PropTypes.number, showMoreLimit: PropTypes.number, transformItems: PropTypes.func, + facetOrdering: PropTypes.bool, }, defaultProps: { @@ -170,10 +171,11 @@ export default createConnector({ separator: ' > ', rootPath: null, showParentLevel: true, + facetOrdering: true, }, getProvidedProps(props, searchState, searchResults) { - const { showMore, limit, showMoreLimit } = props; + const { showMore, limit, showMoreLimit, facetOrdering } = props; const id = getId(props); const results = getResults(searchResults, { @@ -194,7 +196,7 @@ export default createConnector({ }; } const itemsLimit = showMore ? showMoreLimit : limit; - const value = results.getFacetValues(id, { sortBy }); + const value = results.getFacetValues(id, { sortBy, facetOrdering }); const items = value.data ? transformValue(value.data, props, searchState, { ais: props.contextValue, diff --git a/packages/react-instantsearch-core/src/connectors/connectMenu.js b/packages/react-instantsearch-core/src/connectors/connectMenu.js index 4b67d2d6ea..d3f1e7def9 100644 --- a/packages/react-instantsearch-core/src/connectors/connectMenu.js +++ b/packages/react-instantsearch-core/src/connectors/connectMenu.js @@ -82,12 +82,14 @@ export default createConnector({ defaultRefinement: PropTypes.string, transformItems: PropTypes.func, searchable: PropTypes.bool, + facetOrdering: PropTypes.bool, }, defaultProps: { showMore: false, limit: 10, showMoreLimit: 20, + facetOrdering: true, }, getProvidedProps( @@ -97,7 +99,7 @@ export default createConnector({ meta, searchForFacetValuesResults ) { - const { attribute, searchable, indexContextValue } = props; + const { attribute, searchable, indexContextValue, facetOrdering } = props; const results = getResults(searchResults, { ais: props.contextValue, multiIndexContext: props.indexContextValue, @@ -149,6 +151,7 @@ export default createConnector({ items = results .getFacetValues(attribute, { sortBy: searchable ? undefined : defaultSortBy, + facetOrdering, }) .map(v => ({ label: v.name, diff --git a/packages/react-instantsearch-core/src/connectors/connectRefinementList.js b/packages/react-instantsearch-core/src/connectors/connectRefinementList.js index 6d4159589a..99cbe1ea07 100644 --- a/packages/react-instantsearch-core/src/connectors/connectRefinementList.js +++ b/packages/react-instantsearch-core/src/connectors/connectRefinementList.js @@ -104,6 +104,7 @@ export default createConnector({ ), searchable: PropTypes.bool, transformItems: PropTypes.func, + facetOrdering: PropTypes.bool, }, defaultProps: { @@ -111,6 +112,7 @@ export default createConnector({ showMore: false, limit: 10, showMoreLimit: 20, + facetOrdering: true, }, getProvidedProps( @@ -120,7 +122,7 @@ export default createConnector({ metadata, searchForFacetValuesResults ) { - const { attribute, searchable, indexContextValue } = props; + const { attribute, searchable, indexContextValue, facetOrdering } = props; const results = getResults(searchResults, { ais: props.contextValue, multiIndexContext: props.indexContextValue, @@ -167,7 +169,7 @@ export default createConnector({ count: v.count, isRefined: v.isRefined, })) - : results.getFacetValues(attribute, { sortBy }).map(v => ({ + : results.getFacetValues(attribute, { sortBy, facetOrdering }).map(v => ({ label: v.name, value: getValue(v.name, props, searchState, { ais: props.contextValue, diff --git a/packages/react-instantsearch-core/src/core/indexUtils.js b/packages/react-instantsearch-core/src/core/indexUtils.js index f6d02837c3..e5401c572b 100644 --- a/packages/react-instantsearch-core/src/core/indexUtils.js +++ b/packages/react-instantsearch-core/src/core/indexUtils.js @@ -6,6 +6,9 @@ export function getIndexId(context) { : context.ais.mainTargetedIndex; } +/** + * @returns {import('algoliasearch-helper').SearchResults} results + */ export function getResults(searchResults, context) { if (searchResults.results) { if (searchResults.results.hits) {