From 8135cc6fecd96d4335f8ab25995d011704e0b3c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Vannicatte?= <20689156+shortcuts@users.noreply.github.com> Date: Fri, 30 Oct 2020 11:31:43 +0100 Subject: [PATCH] feat(menu): implement `getRenderState` and `getWidgetRenderState` (#4540) * feat(menu): implement `getRenderState` and `getWidgetRenderState` * Removed duplicated declaration of `jsHelper`, moved `cachedToggleShowMore` binding to `init` to avoid it happens more than once --- .../menu/__tests__/connectMenu-test.js | 116 +++++++++++++++++ src/connectors/menu/connectMenu.js | 117 ++++++++++-------- 2 files changed, 180 insertions(+), 53 deletions(-) diff --git a/src/connectors/menu/__tests__/connectMenu-test.js b/src/connectors/menu/__tests__/connectMenu-test.js index 95cf5b7545..7ab7d3186a 100644 --- a/src/connectors/menu/__tests__/connectMenu-test.js +++ b/src/connectors/menu/__tests__/connectMenu-test.js @@ -2,8 +2,13 @@ import jsHelper, { SearchResults, SearchParameters, } from 'algoliasearch-helper'; +import { createSearchClient } from '../../../../test/mock/createSearchClient'; import { createSingleSearchResponse } from '../../../../test/mock/createAPIResponse'; import { createInstantSearch } from '../../../../test/mock/createInstantSearch'; +import { + createInitOptions, + createRenderOptions, +} from '../../../../test/mock/createWidget'; import connectMenu from '../connectMenu'; describe('connectMenu', () => { @@ -437,6 +442,117 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/menu/js/#co expect(() => widget.dispose({ helper, state: helper.state })).not.toThrow(); }); + describe('getRenderState', () => { + test('returns the render state', () => { + const renderFn = jest.fn(); + const unmountFn = jest.fn(); + const createMenu = connectMenu(renderFn, unmountFn); + const menu = createMenu({ + attribute: 'brand', + }); + const helper = jsHelper( + createSearchClient(), + 'indexName', + menu.getWidgetSearchParameters(new SearchParameters(), { uiState: {} }) + ); + + const renderState1 = menu.getRenderState( + { menu: {} }, + createInitOptions({ helper }) + ); + + expect(renderState1.menu).toEqual({ + items: [], + createURL: undefined, + refine: undefined, + canRefine: false, + isShowingMore: false, + toggleShowMore: expect.any(Function), + canToggleShowMore: false, + widgetParams: { attribute: 'brand' }, + }); + }); + + test('returns the render state with results', () => { + const renderFn = jest.fn(); + const unmountFn = jest.fn(); + const createMenu = connectMenu(renderFn, unmountFn); + const menu = createMenu({ + attribute: 'brand', + }); + const helper = jsHelper( + createSearchClient(), + 'indexName', + menu.getWidgetSearchParameters(new SearchParameters(), { + uiState: {}, + }) + ); + + menu.init(createInitOptions({ helper })); + + expect( + menu.getRenderState( + {}, + createRenderOptions({ + helper, + results: new SearchResults(helper.state, [ + createSingleSearchResponse({ + hits: [], + facets: { + brand: 300, + }, + }), + ]), + }) + ) + ).toEqual({ + menu: { + items: [], + canRefine: false, + refine: expect.any(Function), + sendEvent: expect.any(Function), + createURL: expect.any(Function), + widgetParams: { attribute: 'brand' }, + isShowingMore: false, + toggleShowMore: expect.any(Function), + canToggleShowMore: false, + }, + }); + }); + }); + + describe('getWidgetRenderState', () => { + test('returns the widget render state', () => { + const renderFn = jest.fn(); + const unmountFn = jest.fn(); + const createMenu = connectMenu(renderFn, unmountFn); + const menu = createMenu({ + attribute: 'brand', + }); + const helper = jsHelper( + createSearchClient(), + 'indexName', + menu.getWidgetSearchParameters(new SearchParameters(), { uiState: {} }) + ); + + const renderState1 = menu.getWidgetRenderState( + {}, + createInitOptions({ helper }) + ); + + expect(renderState1).toEqual({ + items: [], + createURL: undefined, + refine: undefined, + canRefine: false, + isShowingMore: false, + toggleShowMore: expect.any(Function), + canToggleShowMore: false, + widgetParams: { attribute: 'brand' }, + }); + }); + }); + describe('showMore', () => { it('should set `maxValuesPerFacet` by default', () => { const widget = makeWidget({ diff --git a/src/connectors/menu/connectMenu.js b/src/connectors/menu/connectMenu.js index 02a4188d5f..408a52e33f 100644 --- a/src/connectors/menu/connectMenu.js +++ b/src/connectors/menu/connectMenu.js @@ -140,19 +140,9 @@ export default function connectMenu(renderFn, unmountFn = noop) { return this.isShowingMore ? showMoreLimit : limit; }, - refine(helper) { - return facetValue => { - const [refinedItem] = helper.getHierarchicalFacetBreadcrumb( - attribute - ); - sendEvent('click', facetValue ? facetValue : refinedItem); - helper - .toggleRefinement(attribute, facetValue ? facetValue : refinedItem) - .search(); - }; - }, + init(initOptions) { + const { helper, createURL, instantSearchInstance } = initOptions; - init({ helper, createURL, instantSearchInstance }) { sendEvent = createSendEventForFacet({ instantSearchInstance, helper, @@ -160,64 +150,39 @@ export default function connectMenu(renderFn, unmountFn = noop) { widgetType: this.$$type, }); - this.cachedToggleShowMore = this.cachedToggleShowMore.bind(this); - this._createURL = facetValue => createURL(helper.state.toggleRefinement(attribute, facetValue)); - this._refine = this.refine(helper); + this._refine = function(facetValue) { + const [refinedItem] = helper.getHierarchicalFacetBreadcrumb( + attribute + ); + sendEvent('click', facetValue ? facetValue : refinedItem); + helper + .toggleRefinement(attribute, facetValue ? facetValue : refinedItem) + .search(); + }; + + this.cachedToggleShowMore = this.cachedToggleShowMore.bind(this); renderFn( { - items: [], - createURL: this._createURL, - refine: this._refine, - sendEvent, + ...this.getWidgetRenderState(initOptions), instantSearchInstance, - canRefine: false, - widgetParams, - isShowingMore: this.isShowingMore, - toggleShowMore: this.cachedToggleShowMore, - canToggleShowMore: false, }, true ); }, - render({ results, instantSearchInstance }) { - const facetValues = results.getFacetValues(attribute, { sortBy }); - const facetItems = - facetValues && facetValues.data ? facetValues.data : []; - - const items = transformItems( - facetItems - .slice(0, this.getLimit()) - .map(({ name: label, path: value, ...item }) => ({ - ...item, - label, - value, - })) - ); + render(renderOptions) { + const { instantSearchInstance } = renderOptions; - this.toggleShowMore = this.createToggleShowMore({ - results, - instantSearchInstance, - }); + this.toggleShowMore = this.createToggleShowMore(renderOptions); renderFn( { - items, - createURL: this._createURL, - refine: this._refine, - sendEvent, + ...this.getWidgetRenderState(renderOptions), instantSearchInstance, - canRefine: items.length > 0, - widgetParams, - isShowingMore: this.isShowingMore, - toggleShowMore: this.cachedToggleShowMore, - canToggleShowMore: - showMore && - (this.isShowingMore || facetItems.length > this.getLimit()), }, false ); @@ -231,6 +196,52 @@ export default function connectMenu(renderFn, unmountFn = noop) { .setQueryParameter('maxValuesPerFacet', undefined); }, + getRenderState(renderState, renderOptions) { + return { + ...renderState, + menu: this.getWidgetRenderState(renderOptions), + }; + }, + + getWidgetRenderState(widgetOptions) { + let items = []; + let canToggleShowMore = false; + + if (widgetOptions.results) { + const facetValues = widgetOptions.results.getFacetValues(attribute, { + sortBy, + }); + const facetItems = + facetValues && facetValues.data ? facetValues.data : []; + + canToggleShowMore = + showMore && + (this.isShowingMore || facetItems.length > this.getLimit()); + + items = transformItems( + facetItems + .slice(0, this.getLimit()) + .map(({ name: label, path: value, ...item }) => ({ + ...item, + label, + value, + })) + ); + } + + return { + items, + createURL: this._createURL, + refine: this._refine, + sendEvent, + canRefine: items.length > 0, + widgetParams, + isShowingMore: this.isShowingMore, + toggleShowMore: this.cachedToggleShowMore, + canToggleShowMore, + }; + }, + getWidgetUiState(uiState, { searchParameters }) { const [value] = searchParameters.getHierarchicalFacetBreadcrumb( attribute