From 72655ab07c7eef61445ff749e03047cd895c8c0a Mon Sep 17 00:00:00 2001 From: Alexandre Stanislawski Date: Fri, 12 May 2017 18:52:00 +0200 Subject: [PATCH] fix(refinementList): reimplement show more on refinement list --- .../RefinementList/RefinementList.js | 2 +- src/connectors/menu/connectMenu.js | 2 - .../refinement-list/connectRefinementList.js | 65 ++++++++++++++++--- .../refinement-list/refinement-list.js | 17 +++-- 4 files changed, 65 insertions(+), 21 deletions(-) diff --git a/src/components/RefinementList/RefinementList.js b/src/components/RefinementList/RefinementList.js index 9fab687a6d..0b386b0dcd 100644 --- a/src/components/RefinementList/RefinementList.js +++ b/src/components/RefinementList/RefinementList.js @@ -182,12 +182,12 @@ RawRefinementList.propTypes = { }), depth: React.PropTypes.number, facetValues: React.PropTypes.array, - showMore: React.PropTypes.bool, templateProps: React.PropTypes.object.isRequired, toggleRefinement: React.PropTypes.func.isRequired, searchFacetValues: React.PropTypes.func, searchPlaceholder: React.PropTypes.string, isFromSearch: React.PropTypes.bool, + showMore: React.PropTypes.bool, toggleShowMore: React.PropTypes.func, isShowingMore: React.PropTypes.bool, }; diff --git a/src/connectors/menu/connectMenu.js b/src/connectors/menu/connectMenu.js index 97af52ae4a..e00d8f37d4 100644 --- a/src/connectors/menu/connectMenu.js +++ b/src/connectors/menu/connectMenu.js @@ -162,8 +162,6 @@ export default function connectMenu(renderFn) { .toggleRefinement(attributeName, facetValue) .search(); - this._helper = helper; - renderFn({ items: [], createURL: this._createURL, diff --git a/src/connectors/refinement-list/connectRefinementList.js b/src/connectors/refinement-list/connectRefinementList.js index d04d765698..7319f08906 100644 --- a/src/connectors/refinement-list/connectRefinementList.js +++ b/src/connectors/refinement-list/connectRefinementList.js @@ -10,6 +10,8 @@ var customRefinementList = connectRefinementList(function render(params) { // searchForItems, // instantSearchInstance, // canRefine, + // toggleShowMore, + // isShowingMore, // widgetParams, // } }); @@ -18,17 +20,20 @@ search.addWidget( attributeName, [ operator = 'or' ], [ limit ], + [ showMoreLimit ], [ sortBy = ['isRefined', 'count:desc', 'name:asc']], }) ); Full documentation available at https://community.algolia.com/instantsearch.js/connectors/connectRefinementList.html `; -export const checkUsage = ({attributeName, operator, usageMessage}) => { +export const checkUsage = ({attributeName, operator, usageMessage, showMoreLimit, limit}) => { const noAttributeName = attributeName === undefined; const invalidOperator = !(/^(and|or)$/).test(operator); + const invalidShowMoreLimit = showMoreLimit !== undefined ? + isNaN(showMoreLimit) || showMoreLimit < limit : false; - if (noAttributeName || invalidOperator) { + if (noAttributeName || invalidOperator || invalidShowMoreLimit) { throw new Error(usageMessage); } }; @@ -45,7 +50,10 @@ export const checkUsage = ({attributeName, operator, usageMessage}) => { * @typedef {Object} CustomRefinementListWidgetOptions * @property {string} attributeName The name of the attribute in the records. * @property {"and"|"or"} [operator = 'or'] How the filters are combined together. - * @property {number} [limit = undefined] The max number of items to display. + * @property {number} [limit = undefined] The max number of items to display when + * `showMoreLimit` is not or if the widget is showing less value. + * @property {number} [showMoreLimit] The max number of items to display if the widget + * is showing more items. * @property {string[]|function} [sortBy = ['isRefined', 'count:desc', 'name:asc']] How to sort refinements. Possible values: `count|isRefined|name:asc|name:desc`. */ @@ -58,14 +66,16 @@ export const checkUsage = ({attributeName, operator, usageMessage}) => { * @property {boolean} isFromSearch `true` if the values are from an index search. * @property {boolean} canRefine `true` if a refinement can be applied. * @property {Object} widgetParams All original `CustomRefinementListWidgetOptions` forwarded to the `renderFn`. + * @property {boolean} isShowingMore True if the menu is displaying all the menu items. + * @property {function} toggleShowMore Toggles the number of values displayed between `limit` and `showMoreLimit`. */ /** * **RefinementList** connector provides the logic to build a custom widget that will let the * user filter the results based on the values of a specific facet. * - * The connector provides to the rendering: `refine()` to select a value and - * `items` that are the values that can be selected. + * This connector provides a `toggleShowMore()` function to display more or less items and a `refine()` + * function to select an item. * @type {Connector} * @param {function(RefinementListRenderingOptions, boolean)} renderFn Rendering function for the custom **RefinementList** widget. * @return {function(CustomRefinementListWidgetOptions)} Re-usable widget factory for a custom **RefinementList** widget. @@ -123,17 +133,20 @@ export default function connectRefinementList(renderFn) { attributeName, operator = 'or', limit, + showMoreLimit, sortBy = ['isRefined', 'count:desc', 'name:asc'], } = widgetParams; - checkUsage({attributeName, operator, usage}); + checkUsage({attributeName, operator, usage, limit, showMoreLimit}); const formatItems = ({name: label, ...item}) => ({...item, label, value: label, highlighted: label}); const render = ({items, state, createURL, helperSpecializedSearchFacetValues, - refine, isFromSearch, isFirstSearch, instantSearchInstance}) => { + refine, isFromSearch, isFirstSearch, + isShowingMore, toggleShowMore, + instantSearchInstance}) => { // Compute a specific createURL method able to link to any facet value state change const _createURL = facetValue => createURL(state.toggleRefinement(attributeName, facetValue)); @@ -157,6 +170,8 @@ export default function connectRefinementList(renderFn) { isFromSearch, canRefine: isFromSearch || items.length > 0, widgetParams, + isShowingMore, + toggleShowMore, }, isFirstSearch); }; @@ -198,6 +213,24 @@ export default function connectRefinementList(renderFn) { }; return { + isShowingMore: false, + + // Provide the same function to the `renderFn` so that way the user + // has to only bind it once when `isFirstRendering` for instance + toggleShowMore() {}, + cachedToggleShowMore() { this.toggleShowMore(); }, + + createToggleShowMore(renderOptions) { + return () => { + this.isShowingMore = !this.isShowingMore; + this.render(renderOptions); + }; + }, + + getLimit() { + return this.isShowingMore ? showMoreLimit : limit; + }, + getConfiguration: (configuration = {}) => { const widgetConfiguration = { [operator === 'and' ? 'facets' : 'disjunctiveFacets']: [attributeName], @@ -205,12 +238,18 @@ export default function connectRefinementList(renderFn) { if (limit !== undefined) { const currentMaxValuesPerFacet = configuration.maxValuesPerFacet || 0; - widgetConfiguration.maxValuesPerFacet = Math.max(currentMaxValuesPerFacet, limit); + if (showMoreLimit === undefined) { + widgetConfiguration.maxValuesPerFacet = Math.max(currentMaxValuesPerFacet, limit); + } else { + widgetConfiguration.maxValuesPerFacet = Math.max(currentMaxValuesPerFacet, limit, showMoreLimit); + } } return widgetConfiguration; }, init({helper, createURL, instantSearchInstance}) { + this.cachedToggleShowMore = this.cachedToggleShowMore.bind(this); + refine = facetValue => helper .toggleRefinement(attributeName, facetValue) .search(); @@ -226,15 +265,21 @@ export default function connectRefinementList(renderFn) { isFromSearch: false, isFirstSearch: true, instantSearchInstance, + isShowingMore: this.isShowingMore, + toggleShowMore: this.cachedToggleShowMore, }); }, - render({results, state, createURL, instantSearchInstance}) { + render(renderOptions) { + const {results, state, createURL, instantSearchInstance} = renderOptions; const items = results .getFacetValues(attributeName, {sortBy}) + .slice(0, this.getLimit()) .map(formatItems); lastResultsFromMainSearch = items; + this.toggleShowMore = this.createToggleShowMore(renderOptions); + render({ items, state, @@ -244,6 +289,8 @@ export default function connectRefinementList(renderFn) { isFromSearch: false, isFirstSearch: false, instantSearchInstance, + isShowingMore: this.isShowingMore, + toggleShowMore: this.cachedToggleShowMore, }); }, }; diff --git a/src/widgets/refinement-list/refinement-list.js b/src/widgets/refinement-list/refinement-list.js index 97da4136c3..d79cbdf568 100644 --- a/src/widgets/refinement-list/refinement-list.js +++ b/src/widgets/refinement-list/refinement-list.js @@ -24,8 +24,6 @@ const renderer = ({ templates, renderState, collapsible, - limitMax, - limit, autoHideContainer, showMoreConfig, searchForFacetValues, @@ -37,6 +35,8 @@ const renderer = ({ isFromSearch, instantSearchInstance, canRefine, + toggleShowMore, + isShowingMore, }, isFirstRendering) => { if (isFirstRendering) { renderState.templateProps = prepareTemplateProps({ @@ -60,15 +60,15 @@ const renderer = ({ cssClasses={cssClasses} facetValues={items} headerFooterData={headerFooterData} - limitMax={limitMax} - limitMin={limit} shouldAutoHideContainer={autoHideContainer && canRefine === false} - showMore={showMoreConfig !== null} templateProps={renderState.templateProps} toggleRefinement={refine} searchFacetValues={searchForFacetValues ? searchForItems : undefined} searchPlaceholder={searchForFacetValues.placeholder || 'Search for other...'} isFromSearch={isFromSearch} + showMore={showMoreConfig !== null} + toggleShowMore={toggleShowMore} + isShowingMore={isShowingMore} />, containerNode ); @@ -223,7 +223,7 @@ export default function refinementList({ throw new Error('showMore.limit configuration should be > than the limit in the main configuration'); // eslint-disable-line } - const limitMax = showMoreConfig && showMoreConfig.limit || limit; + const showMoreLimit = showMoreConfig && showMoreConfig.limit || limit; const containerNode = getContainerNode(container); const showMoreTemplates = showMoreConfig ? prefixKeys('show-more-', showMoreConfig.templates) : {}; const searchForValuesTemplates = searchForFacetValues ? searchForFacetValues.templates : {}; @@ -249,8 +249,6 @@ export default function refinementList({ templates: allTemplates, renderState: {}, collapsible, - limitMax, - limit, autoHideContainer, showMoreConfig, searchForFacetValues, @@ -261,7 +259,8 @@ export default function refinementList({ return makeWidget({ attributeName, operator, - limit: limitMax, + limit, + showMoreLimit, sortBy, }); } catch (e) {