Skip to content

Commit

Permalink
fix(commerce): fix field suggestions state update (#4245)
Browse files Browse the repository at this point in the history
Field suggestion values weren't updating when the query changed. To fix
this, we fetch the facet search state using a selector, just like we do
for facets (thanks @fbeaudoincoveo!).

I've also fixed a few other things:
- Load the proper reducers in field suggestions controllers
- Add the reducer to the commerce engine
- Type the reducer loaders
- Replace the `Subscribable` type to `Controller` on field suggestions
controllers
- Adjusted the field suggestions integration test to rely on controllers
instead of engine state updates. This was actually a clue that the
current behavior was broken, as it was not possible to await state
changes on the field suggestions controllers 😅

[CAPI-1201]

[CAPI-1201]:
https://coveord.atlassian.net/browse/CAPI-1201?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
  • Loading branch information
Spuffynism authored Aug 1, 2024
1 parent c26ce47 commit e8ebb09
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 37 deletions.
2 changes: 2 additions & 0 deletions packages/headless/src/app/commerce-engine/commerce-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {setContext} from '../../features/commerce/context/context-actions';
import {contextReducer} from '../../features/commerce/context/context-slice';
import {didYouMeanReducer} from '../../features/commerce/did-you-mean/did-you-mean-slice';
import {commerceFacetSetReducer} from '../../features/commerce/facets/facet-set/facet-set-slice';
import {fieldSuggestionsOrderReducer} from '../../features/commerce/facets/field-suggestions-order/field-suggestions-order-slice';
import {manualNumericFacetReducer} from '../../features/commerce/facets/numeric-facet/manual-numeric-facet-slice';
import {paginationReducer} from '../../features/commerce/pagination/pagination-slice';
import {productListingReducer} from '../../features/commerce/product-listing/product-listing-slice';
Expand Down Expand Up @@ -45,6 +46,7 @@ const commerceEngineReducers = {
commercePagination: paginationReducer,
commerceSort: sortReducer,
facetOrder: facetOrderReducer,
fieldSuggestionsOrder: fieldSuggestionsOrderReducer,
facetSearchSet: specificFacetSearchSetReducer,
categoryFacetSearchSet: categoryFacetSearchSetReducer,
commerceFacetSet: commerceFacetSetReducer,
Expand Down
5 changes: 4 additions & 1 deletion packages/headless/src/commerce.index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,10 @@ export type {
CategoryFieldSuggestions,
CategoryFieldSuggestionsState,
} from './controllers/commerce/field-suggestions/headless-category-field-suggestions';
export type {FieldSuggestionsGenerator} from './controllers/commerce/field-suggestions/headless-field-suggestions-generator';
export type {
FieldSuggestionsGenerator,
GeneratedFieldSuggestionsControllers,
} from './controllers/commerce/field-suggestions/headless-field-suggestions-generator';
export type {FieldSuggestionsFacet} from './features/commerce/facets/field-suggestions-order/field-suggestions-order-state.ts';
export {buildFieldSuggestionsGenerator} from './controllers/commerce/field-suggestions/headless-field-suggestions-generator';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import {executeCommerceFieldSuggest} from '../../../features/commerce/facets/facet-search-set/commerce-facet-search-actions';
import {commerceFacetSetReducer as commerceFacetSet} from '../../../features/commerce/facets/facet-set/facet-set-slice';
import {CategoryFacetRequest} from '../../../features/commerce/facets/facet-set/interfaces/request';
import {fieldSuggestionsOrderReducer as fieldSuggestionsOrder} from '../../../features/commerce/facets/field-suggestions-order/field-suggestions-order-slice';
import {categoryFacetSearchSetReducer as categoryFacetSearchSet} from '../../../features/facets/facet-search-set/category/category-facet-search-set-slice';
import {updateFacetSearch} from '../../../features/facets/facet-search-set/specific/specific-facet-search-actions';
import {CommerceAppState} from '../../../state/commerce-app-state';
import {buildMockCategoryFacetSearch} from '../../../test/mock-category-facet-search';
Expand Down Expand Up @@ -58,6 +61,14 @@ describe('categoryFieldSuggestions', () => {
initFacet();
});

it('adds correct reducers to engine', () => {
expect(engine.addReducers).toHaveBeenCalledWith({
fieldSuggestionsOrder,
categoryFacetSearchSet,
commerceFacetSet,
});
});

it('should dispatch an #updateFacetSearch and #executeFieldSuggest action on #updateText', () => {
fieldSuggestions.updateText('foo');
expect(updateFacetSearch).toHaveBeenCalledWith({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import {createSelector} from '@reduxjs/toolkit';
import {FieldSuggestionsFacet} from '../../../api/commerce/search/query-suggest/query-suggest-response';
import {CommerceEngine} from '../../../app/commerce-engine/commerce-engine';
import {
CommerceEngine,
CommerceEngineState,
} from '../../../app/commerce-engine/commerce-engine';
import {stateKey} from '../../../app/state-key';
import {commerceFacetSetReducer as commerceFacetSet} from '../../../features/commerce/facets/facet-set/facet-set-slice';
import {fieldSuggestionsOrderReducer as fieldSuggestionsOrder} from '../../../features/commerce/facets/field-suggestions-order/field-suggestions-order-slice';
import {categoryFacetSearchSetReducer as categoryFacetSearchSet} from '../../../features/facets/facet-search-set/category/category-facet-search-set-slice';
import {
CategoryFacetSearchSection,
CommerceFacetSetSection,
FacetSearchSection,
FieldSuggestionsOrderSection,
} from '../../../state/state-sections';
import {loadReducerError} from '../../../utils/errors';
import {
buildController,
Subscribable,
Controller,
} from '../../controller/headless-controller';
import {
CategoryFieldSuggestionsState as CoreCategoryFieldSuggestionsState,
Expand All @@ -28,7 +40,7 @@ export type CategoryFieldSuggestionsState = CoreCategoryFieldSuggestionsState &
* This controller is a wrapper around the basic category facet controller search functionality, and thus exposes similar options and properties.
*/
export interface CategoryFieldSuggestions
extends Subscribable,
extends Controller,
FacetControllerType<'hierarchical'> {
/**
* Requests field suggestions based on a query.
Expand Down Expand Up @@ -81,13 +93,26 @@ export function buildCategoryFieldSuggestions(
isForFieldSuggestions: true,
});

const getState = () => engine[stateKey];

const getFacetForFieldSuggestions = (facetId: string) => {
return engine[stateKey].fieldSuggestionsOrder!.find(
return getState().fieldSuggestionsOrder.find(
(facet) => facet.facetId === facetId
)!;
};

const controller = buildController(engine);

const facetSearchStateSelector = createSelector(
(state: CommerceEngineState) =>
state.categoryFacetSearchSet[options.facetId],
(facetSearch) => ({
isLoading: facetSearch.isLoading,
moreValuesAvailable: facetSearch.response.moreValuesAvailable,
query: facetSearch.options.query,
values: facetSearch.response.values,
})
);
return {
...controller,
...facetSearch,
Expand All @@ -103,7 +128,7 @@ export function buildCategoryFieldSuggestions(
displayName: facet.displayName,
field: facet.field,
facetId: facet.facetId,
...facetSearch.state,
...facetSearchStateSelector(getState()),
};
},

Expand All @@ -113,7 +138,16 @@ export function buildCategoryFieldSuggestions(

function loadFieldSuggestionsReducers(
engine: CommerceEngine
): engine is CommerceEngine {
engine.addReducers({commerceFacetSet});
): engine is CommerceEngine<
FieldSuggestionsOrderSection &
CommerceFacetSetSection &
CategoryFacetSearchSection &
FacetSearchSection
> {
engine.addReducers({
fieldSuggestionsOrder,
categoryFacetSearchSet,
commerceFacetSet,
});
return true;
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import {FacetSearchType} from '../../../api/commerce/facet-search/facet-search-request';
import {FieldSuggestionsFacet} from '../../../api/commerce/search/query-suggest/query-suggest-response';
import {commerceFacetSetReducer as commerceFacetSet} from '../../../features/commerce/facets/facet-set/facet-set-slice';
import {fieldSuggestionsOrderReducer as fieldSuggestionsOrder} from '../../../features/commerce/facets/field-suggestions-order/field-suggestions-order-slice';
import {categoryFacetSearchSetReducer as categoryFacetSearchSet} from '../../../features/facets/facet-search-set/category/category-facet-search-set-slice';
import {specificFacetSearchSetReducer as facetSearchSet} from '../../../features/facets/facet-search-set/specific/specific-facet-search-set-slice';
import {CommerceAppState} from '../../../state/commerce-app-state';
import {buildMockCategoryFacetSearch} from '../../../test/mock-category-facet-search';
import {buildMockCommerceFacetRequest} from '../../../test/mock-commerce-facet-request';
Expand Down Expand Up @@ -71,9 +68,6 @@ describe('fieldSuggestionsGenerator', () => {
it('adds correct reducers to engine', () => {
expect(engine.addReducers).toHaveBeenCalledWith({
fieldSuggestionsOrder,
commerceFacetSet,
facetSearchSet,
categoryFacetSearchSet,
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ import {
CommerceEngineState,
} from '../../../app/commerce-engine/commerce-engine';
import {stateKey} from '../../../app/state-key';
import {commerceFacetSetReducer as commerceFacetSet} from '../../../features/commerce/facets/facet-set/facet-set-slice';
import {fieldSuggestionsOrderReducer as fieldSuggestionsOrder} from '../../../features/commerce/facets/field-suggestions-order/field-suggestions-order-slice';
import {FieldSuggestionsFacet} from '../../../features/commerce/facets/field-suggestions-order/field-suggestions-order-state';
import {executeSearch} from '../../../features/commerce/search/search-actions';
import {categoryFacetSearchSetReducer as categoryFacetSearchSet} from '../../../features/facets/facet-search-set/category/category-facet-search-set-slice';
import {specificFacetSearchSetReducer as facetSearchSet} from '../../../features/facets/facet-search-set/specific/specific-facet-search-set-slice';
import {FieldSuggestionsOrderSection} from '../../../state/state-sections';
import {loadReducerError} from '../../../utils/errors';
import {
buildController,
Expand Down Expand Up @@ -67,11 +65,8 @@ export function buildFieldSuggestionsGenerator(
const controller = buildController(engine);

const createFieldSuggestionsControllers = createSelector(
(state: CommerceEngineState) => state.fieldSuggestionsOrder!,
(state: CommerceEngineState) => state.commerceFacetSet,
(state: CommerceEngineState) => state.facetSearchSet,
(state: CommerceEngineState) => state.categoryFacetSearchSet,
(facetOrder, _commerceFacetSet, _facetSearchSet, _categoryFacetSearchSet) =>
(state: CommerceEngineState) => state.fieldSuggestionsOrder,
(facetOrder) =>
facetOrder.map(({type, facetId}) => {
switch (type) {
case 'hierarchical':
Expand All @@ -94,19 +89,16 @@ export function buildFieldSuggestionsGenerator(
},

get state() {
return engine[stateKey].fieldSuggestionsOrder!;
return engine[stateKey].fieldSuggestionsOrder;
},
};
}

function loadFieldSuggestionsGeneratorReducers(
engine: CommerceEngine
): engine is CommerceEngine {
): engine is CommerceEngine<FieldSuggestionsOrderSection> {
engine.addReducers({
fieldSuggestionsOrder,
commerceFacetSet,
facetSearchSet,
categoryFacetSearchSet,
});
return true;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import {executeCommerceFieldSuggest} from '../../../features/commerce/facets/facet-search-set/commerce-facet-search-actions';
import {commerceFacetSetReducer as commerceFacetSet} from '../../../features/commerce/facets/facet-set/facet-set-slice';
import {RegularFacetRequest} from '../../../features/commerce/facets/facet-set/interfaces/request';
import {fieldSuggestionsOrderReducer as fieldSuggestionsOrder} from '../../../features/commerce/facets/field-suggestions-order/field-suggestions-order-slice';
import {updateFacetSearch} from '../../../features/facets/facet-search-set/specific/specific-facet-search-actions';
import {specificFacetSearchSetReducer as facetSearchSet} from '../../../features/facets/facet-search-set/specific/specific-facet-search-set-slice';
import {CommerceAppState} from '../../../state/commerce-app-state';
import {buildMockCommerceFacetRequest} from '../../../test/mock-commerce-facet-request';
import {buildMockCommerceFacetSlice} from '../../../test/mock-commerce-facet-slice';
Expand Down Expand Up @@ -60,6 +63,14 @@ describe('fieldSuggestions', () => {
initFacet();
});

it('adds correct reducers to engine', () => {
expect(engine.addReducers).toHaveBeenCalledWith({
fieldSuggestionsOrder,
commerceFacetSet,
facetSearchSet,
});
});

it('should dispatch an #updateFacetSearch and #executeFieldSuggest action on #updateText', () => {
fieldSuggestions.updateText('foo');
expect(updateFacetSearch).toHaveBeenCalled();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import {createSelector} from '@reduxjs/toolkit';
import {FieldSuggestionsFacet} from '../../../api/commerce/search/query-suggest/query-suggest-response';
import {SpecificFacetSearchResult} from '../../../api/search/facet-search/specific-facet-search/specific-facet-search-response';
import {CommerceEngine} from '../../../app/commerce-engine/commerce-engine';
import {
CommerceEngine,
CommerceEngineState,
} from '../../../app/commerce-engine/commerce-engine';
import {stateKey} from '../../../app/state-key';
import {commerceFacetSetReducer as commerceFacetSet} from '../../../features/commerce/facets/facet-set/facet-set-slice';
import {fieldSuggestionsOrderReducer as fieldSuggestionsOrder} from '../../../features/commerce/facets/field-suggestions-order/field-suggestions-order-slice';
import {specificFacetSearchSetReducer as facetSearchSet} from '../../../features/facets/facet-search-set/specific/specific-facet-search-set-slice';
import {
CommerceFacetSetSection,
FacetSearchSection,
FieldSuggestionsOrderSection,
} from '../../../state/state-sections';
import {loadReducerError} from '../../../utils/errors';
import {
buildController,
Subscribable,
Controller,
} from '../../controller/headless-controller';
import {FacetControllerType} from '../core/facets/headless-core-commerce-facet';
import {RegularFacetOptions} from '../core/facets/regular/headless-commerce-regular-facet';
Expand All @@ -28,7 +39,7 @@ export type FieldSuggestionsState = RegularFacetSearchState &
* This controller is a wrapper around the basic facet controller search functionality, and thus exposes similar options and properties.
*/
export interface FieldSuggestions
extends Subscribable,
extends Controller,
FacetControllerType<'regular'> {
/**
* Requests field suggestions based on a query.
Expand Down Expand Up @@ -99,13 +110,26 @@ export function buildFieldSuggestions(
isForFieldSuggestions: true,
});

const getState = () => engine[stateKey];

const getFacetForFieldSuggestions = (facetId: string) => {
return engine[stateKey].fieldSuggestionsOrder!.find(
return getState().fieldSuggestionsOrder.find(
(facet) => facet.facetId === facetId
)!;
};

const controller = buildController(engine);

const facetSearchStateSelector = createSelector(
(state: CommerceEngineState) => state.facetSearchSet[options.facetId],
(facetSearch) => ({
isLoading: facetSearch.isLoading,
moreValuesAvailable: facetSearch.response.moreValuesAvailable,
query: facetSearch.options.query,
values: facetSearch.response.values,
})
);

return {
...controller,
...facetSearch,
Expand All @@ -121,7 +145,7 @@ export function buildFieldSuggestions(
displayName: facet.displayName,
field: facet.field,
facetId: facet.facetId,
...facetSearch.state,
...facetSearchStateSelector(getState()),
};
},

Expand All @@ -131,7 +155,9 @@ export function buildFieldSuggestions(

function loadFieldSuggestionsReducers(
engine: CommerceEngine
): engine is CommerceEngine {
engine.addReducers({commerceFacetSet});
): engine is CommerceEngine<
FieldSuggestionsOrderSection & CommerceFacetSetSection & FacetSearchSection
> {
engine.addReducers({fieldSuggestionsOrder, commerceFacetSet, facetSearchSet});
return true;
}
6 changes: 3 additions & 3 deletions packages/headless/src/integration-tests/commerce.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,9 @@ describe.skip('commerce', () => {
expect(generator.fieldSuggestions).toHaveLength(3);

for (const controller of generator.fieldSuggestions) {
await waitForNextStateChange(engine, {
await waitForNextStateChange(controller, {
action: () => controller.updateText('can'),
expectedSubscriberCalls: 3,
expectedSubscriberCalls: 2,
});
}

Expand All @@ -169,7 +169,7 @@ describe.skip('commerce', () => {
await search(box, 'acc');

for (const controller of generator.fieldSuggestions) {
await waitForNextStateChange(engine, {
await waitForNextStateChange(controller, {
action: () => controller.updateText('acc'),
expectedSubscriberCalls: 3,
});
Expand Down

0 comments on commit e8ebb09

Please sign in to comment.