From 946d4e46079cd8586389f7a2ff551db2e8d83ca2 Mon Sep 17 00:00:00 2001 From: Frederic Beaudoin Date: Thu, 25 Apr 2024 12:56:05 -0400 Subject: [PATCH] fix(headless/commerce): use numberOfValues from response when setting initialNumberOfValues / numberOfValues in facet request (#3856) https://coveord.atlassian.net/browse/KIT-3122 --- .../facets/facet-set/facet-set-slice.test.ts | 87 +++++++++++-------- .../facets/facet-set/facet-set-slice.ts | 30 ++++--- .../facets/facet-set/interfaces/response.ts | 1 + .../src/test/mock-commerce-facet-response.ts | 1 + 4 files changed, 69 insertions(+), 50 deletions(-) diff --git a/packages/headless/src/features/commerce/facets/facet-set/facet-set-slice.test.ts b/packages/headless/src/features/commerce/facets/facet-set/facet-set-slice.test.ts index ada58bbad26..8febea7d224 100644 --- a/packages/headless/src/features/commerce/facets/facet-set/facet-set-slice.test.ts +++ b/packages/headless/src/features/commerce/facets/facet-set/facet-set-slice.test.ts @@ -143,21 +143,17 @@ describe('commerceFacetSetReducer', () => { expect(finalState[facetId]).toBeDefined(); }); - it('sets the facet #initialNumberOfResults to #values length from facet response', () => { + it('sets the facet #initialNumberOfResults to #numberOfValues from facet response', () => { const facet = buildMockCommerceRegularFacetResponse({ facetId, - values: [ - buildMockCommerceRegularFacetValue(), - buildMockCommerceRegularFacetValue(), - buildMockCommerceRegularFacetValue(), - ], + numberOfValues: 5, }); const action = buildQueryAction([facet]); const finalState = commerceFacetSetReducer(state, action); expect(finalState[facetId]?.request.initialNumberOfValues).toEqual( - facet.values.length + 5 ); }); }); @@ -167,24 +163,19 @@ describe('commerceFacetSetReducer', () => { request: buildMockCommerceFacetRequest({ type: 'regular', facetId, - initialNumberOfValues: 1, - values: [buildMockCommerceRegularFacetValue()], + initialNumberOfValues: 8, }), }); const facet = buildMockCommerceRegularFacetResponse({ facetId, - values: [ - buildMockCommerceRegularFacetValue(), - buildMockCommerceRegularFacetValue(), - buildMockCommerceRegularFacetValue(), - ], + numberOfValues: 2, }); const action = buildQueryAction([facet]); const finalState = commerceFacetSetReducer(state, action); - expect(finalState[facetId]?.request.initialNumberOfValues).toEqual(1); + expect(finalState[facetId]?.request.initialNumberOfValues).toEqual(8); }); it('sets/updates facet request #displayName in state from response', () => { @@ -238,10 +229,10 @@ describe('commerceFacetSetReducer', () => { expect(updatedState2[facetId]?.request.field).toEqual(field2); }); - it('sets/updates facet request #numberOfValues in state from #values length in response', () => { + it('sets/updates facet request #numberOfValues in state from #numberOfValues in response', () => { const facetResponse1 = buildMockCommerceRegularFacetResponse({ facetId, - values: [buildMockCommerceRegularFacetValue()], + numberOfValues: 1, }); const action1 = buildQueryAction([facetResponse1]); @@ -251,11 +242,7 @@ describe('commerceFacetSetReducer', () => { const facetResponse2 = buildMockCommerceRegularFacetResponse({ facetId, - values: [ - buildMockCommerceRegularFacetValue(), - buildMockCommerceRegularFacetValue(), - buildMockCommerceRegularFacetValue(), - ], + numberOfValues: 3, }); const action2 = buildQueryAction([facetResponse2]); @@ -2087,11 +2074,31 @@ describe('commerceFacetSetReducer', () => { }); }); + it('#deselectAllBreadcrumbs resets the state of all facet values to "idle"', () => { + const facetIds = ['1', '2']; + for (const facetId of facetIds) { + state[facetId] = buildMockCommerceFacetSlice({ + request: buildMockCommerceFacetRequest({ + facetId, + values: [ + buildMockCommerceRegularFacetValue({state: 'selected'}), + buildMockCommerceRegularFacetValue({state: 'excluded'}), + buildMockCommerceRegularFacetValue({state: 'idle'}), + ], + }), + }); + } + + const finalState = commerceFacetSetReducer(state, deselectAllBreadcrumbs()); + + for (const facetId in finalState) { + for (const value of finalState[facetId]!.request.values) { + expect(value.state).toBe('idle'); + } + } + }); + describe.each([ - { - actionName: '#deselectAllBreadcrumbs', - action: deselectAllBreadcrumbs, - }, { actionName: '#setContext', action: setContext, @@ -2105,20 +2112,26 @@ describe('commerceFacetSetReducer', () => { action: setUser, }, ])('$actionName', ({action}: {action: ActionCreator}) => { - const facetId = '1'; - - it('resets facets', () => { - state[facetId] = buildMockCommerceFacetSlice({ - request: buildMockCommerceFacetRequest({ - type: 'regular', - facetId, - values: [{value: 'facet value', state: 'selected'}], - }), - }); + it('clears all facets values', () => { + const facetIds = ['1', '2']; + for (const facetId of facetIds) { + state[facetId] = buildMockCommerceFacetSlice({ + request: buildMockCommerceFacetRequest({ + facetId, + values: [ + buildMockCommerceRegularFacetValue({state: 'selected'}), + buildMockCommerceRegularFacetValue({state: 'excluded'}), + buildMockCommerceRegularFacetValue({state: 'idle'}), + ], + }), + }); + } const finalState = commerceFacetSetReducer(state, action({})); - expect(finalState[facetId].request.values[0].state).toEqual('idle'); + for (const facetId in finalState) { + expect(finalState[facetId].request.values.length).toBe(0); + } }); }); }); diff --git a/packages/headless/src/features/commerce/facets/facet-set/facet-set-slice.ts b/packages/headless/src/features/commerce/facets/facet-set/facet-set-slice.ts index b33fe704c2e..0cb7f902e50 100644 --- a/packages/headless/src/features/commerce/facets/facet-set/facet-set-slice.ts +++ b/packages/headless/src/features/commerce/facets/facet-set/facet-set-slice.ts @@ -1,8 +1,4 @@ -import { - AnyAction, - createReducer, - type Draft as WritableDraft, -} from '@reduxjs/toolkit'; +import {createReducer, type Draft as WritableDraft} from '@reduxjs/toolkit'; import { CategoryFacetValueRequest, DateRangeRequest, @@ -337,10 +333,10 @@ export const commerceFacetSetReducer = createReducer( handleDeselectAllFacetValues(request); }) - .addCase(deselectAllBreadcrumbs, resetAllFacetValues) - .addCase(setContext, resetAllFacetValues) - .addCase(setView, resetAllFacetValues) - .addCase(setUser, resetAllFacetValues); + .addCase(deselectAllBreadcrumbs, setAllFacetValuesToIdle) + .addCase(setContext, clearAllFacetValues) + .addCase(setView, clearAllFacetValues) + .addCase(setUser, clearAllFacetValues); } ); @@ -370,7 +366,9 @@ function ensureCategoryFacetRequest( function handleQueryFulfilled( state: WritableDraft, - action: AnyAction + action: ReturnType< + typeof fetchProductListing.fulfilled | typeof executeSearch.fulfilled + > ) { const existingFacets = new Set(Object.keys(state)); const facets = action.payload.response.facets; @@ -464,14 +462,14 @@ function updateStateFromFacetResponse( if (!facetRequest) { state[facetId] = {request: {} as AnyFacetRequest}; facetRequest = state[facetId].request; - facetRequest.initialNumberOfValues = facetResponse.values.length; + facetRequest.initialNumberOfValues = facetResponse.numberOfValues; } else { facetsToRemove.delete(facetId); } facetRequest.facetId = facetId; facetRequest.displayName = facetResponse.displayName; - facetRequest.numberOfValues = facetResponse.values.length; + facetRequest.numberOfValues = facetResponse.numberOfValues; facetRequest.field = facetResponse.field; facetRequest.type = facetResponse.type; facetRequest.values = @@ -540,12 +538,18 @@ function insertNewValue( facetRequest.numberOfValues = facetRequest.values.length; } -function resetAllFacetValues(state: CommerceFacetSetState) { +function setAllFacetValuesToIdle(state: CommerceFacetSetState) { Object.values(state).forEach((facet) => { facet.request.values.forEach((value) => (value.state = 'idle')); }); } +function clearAllFacetValues(state: CommerceFacetSetState) { + Object.values(state).forEach((facet) => { + facet.request.values = []; + }); +} + export function selectPath( request: CommerceFacetRequest, path: string[], diff --git a/packages/headless/src/features/commerce/facets/facet-set/interfaces/response.ts b/packages/headless/src/features/commerce/facets/facet-set/interfaces/response.ts index 7193e45bd2e..58f9f94dbdd 100644 --- a/packages/headless/src/features/commerce/facets/facet-set/interfaces/response.ts +++ b/packages/headless/src/features/commerce/facets/facet-set/interfaces/response.ts @@ -9,6 +9,7 @@ export interface BaseFacetResponse { fromAutoSelect: boolean; values: Value[]; type: Type; + numberOfValues: number; } export type RegularFacetResponse = BaseFacetResponse< diff --git a/packages/headless/src/test/mock-commerce-facet-response.ts b/packages/headless/src/test/mock-commerce-facet-response.ts index a972dac4145..0b75cea6faa 100644 --- a/packages/headless/src/test/mock-commerce-facet-response.ts +++ b/packages/headless/src/test/mock-commerce-facet-response.ts @@ -17,6 +17,7 @@ function getMockBaseCommerceFacetResponse(): Omit< fromAutoSelect: false, isFieldExpanded: false, moreValuesAvailable: false, + numberOfValues: 0, }; }