Skip to content

Commit

Permalink
feat(headless SSR): add define function for pagination, parameter man…
Browse files Browse the repository at this point in the history
…ager, sort, summary, productView, and didYouMean (#4266)

https://coveord.atlassian.net/browse/KIT-3392

DidYouMean and ParameterManager aren't in the sample yet. I created
separate Jiras for those.

---------

Co-authored-by: ylakhdar <ylakhdar@coveo.com>
Co-authored-by: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com>
Co-authored-by: Nico Labarre <Spuffynism@users.noreply.github.com>
  • Loading branch information
4 people committed Aug 21, 2024
1 parent 6c7f390 commit 23b4d59
Show file tree
Hide file tree
Showing 20 changed files with 694 additions and 70 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {ensureAtLeastOneSolutionType} from '../../../../app/commerce-ssr-engine/common';
import {
ControllerDefinitionOption,
SolutionType,
SubControllerDefinitionWithoutProps,
} from '../../../../app/commerce-ssr-engine/types/common';
import {buildProductListing} from '../../product-listing/headless-product-listing';
import {buildSearch} from '../../search/headless-search';
import {
Pagination,
PaginationProps,
PaginationState,
} from './headless-core-commerce-pagination';

export type {Pagination, PaginationProps, PaginationState};

/**
* Defines a `Pagination` controller instance.
*
* @param props - The configurable `Pagination` properties.
* @returns The `Pagination` controller definition.
*
* @internal
*/
export function definePagination<
TOptions extends ControllerDefinitionOption | undefined,
>(props?: PaginationProps, options?: TOptions) {
ensureAtLeastOneSolutionType(options);
return {
...options,
build: (engine, solutionType) =>
solutionType === SolutionType.listing
? buildProductListing(engine).pagination(props)
: buildSearch(engine).pagination(props),
} as SubControllerDefinitionWithoutProps<Pagination, TOptions>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import {ensureAtLeastOneSolutionType} from '../../../../app/commerce-ssr-engine/common';
import {
ControllerDefinitionOption,
SolutionType,
SubControllerDefinitionWithProps,
} from '../../../../app/commerce-ssr-engine/types/common';
import {CoreEngineNext} from '../../../../app/engine';
import {commerceFacetSetReducer as commerceFacetSet} from '../../../../features/commerce/facets/facet-set/facet-set-slice';
import {manualNumericFacetReducer as manualNumericFacetSet} from '../../../../features/commerce/facets/numeric-facet/manual-numeric-facet-slice';
import {paginationReducer as commercePagination} from '../../../../features/commerce/pagination/pagination-slice';
import {Parameters} from '../../../../features/commerce/parameters/parameters-actions';
import {ProductListingParameters} from '../../../../features/commerce/product-listing-parameters/product-listing-parameters-actions';
import {queryReducer as query} from '../../../../features/commerce/query/query-slice';
import {CommerceSearchParameters} from '../../../../features/commerce/search-parameters/search-parameters-actions';
import {sortReducer as commerceSort} from '../../../../features/commerce/sort/sort-slice';
import {facetOrderReducer as facetOrder} from '../../../../features/facets/facet-order/facet-order-slice';
import {querySetReducer as querySet} from '../../../../features/query-set/query-set-slice';
import {loadReducerError} from '../../../../utils/errors';
import {buildProductListing} from '../../product-listing/headless-product-listing';
import {buildSearch} from '../../search/headless-search';
import {
ParameterManager,
ParameterManagerProps,
ParameterManagerState,
} from './headless-core-parameter-manager';

export type {
ParameterManager,
ParameterManagerProps,
ParameterManagerState,
Parameters,
ProductListingParameters,
CommerceSearchParameters,
};

/**
* Defines a `ParameterManager` controller instance.
*
* @returns The `ParameterManager` controller definition.
*
* @internal
*/
export function defineParameterManager<
TOptions extends ControllerDefinitionOption | undefined,
>(options?: TOptions) {
ensureAtLeastOneSolutionType(options);
return {
...options,
buildWithProps: (engine, props, solutionType) => {
if (solutionType === SolutionType.listing) {
if (!loadCommerceProductListingParameterReducers(engine)) {
throw loadReducerError;
}
return buildProductListing(engine).parameterManager(props);
} else {
if (!loadCommerceSearchParameterReducers(engine)) {
throw loadReducerError;
}
return buildSearch(engine).parameterManager(props);
}
},
} as SubControllerDefinitionWithProps<
ParameterManager<MappedParameterTypes<typeof options>>,
TOptions,
ParameterManagerProps<MappedParameterTypes<typeof options>>
>;
}

type MappedParameterTypes<
TOptions extends ControllerDefinitionOption | undefined,
> = TOptions extends {listing: true; search: true} | undefined
? ProductListingParameters | CommerceSearchParameters
: TOptions extends {listing: true; search: false}
? ProductListingParameters
: TOptions extends {listing: false; search: true}
? CommerceSearchParameters
: never;

function loadCommerceCommonParameterReducers(
engine: CoreEngineNext
): engine is CoreEngineNext<ParameterManager<Parameters>> {
engine.addReducers({
commerceFacetSet,
commerceSort,
commercePagination,
facetOrder,
manualNumericFacetSet,
});
return true;
}

function loadCommerceSearchParameterReducers(
engine: CoreEngineNext
): engine is CoreEngineNext<ParameterManager<CommerceSearchParameters>> {
loadCommerceCommonParameterReducers(engine);
engine.addReducers({query, querySet});
return true;
}

function loadCommerceProductListingParameterReducers(
engine: CoreEngineNext
): engine is CoreEngineNext<ParameterManager<ProductListingParameters>> {
loadCommerceCommonParameterReducers(engine);
return true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {ensureAtLeastOneSolutionType} from '../../../../app/commerce-ssr-engine/common';
import {
ControllerDefinitionOption,
SolutionType,
SubControllerDefinitionWithoutProps,
} from '../../../../app/commerce-ssr-engine/types/common';
import {buildProductListing} from '../../product-listing/headless-product-listing';
import {buildSearch} from '../../search/headless-search';
import {Sort, SortProps, SortState} from './headless-core-commerce-sort';

export type {Sort, SortProps, SortState};

/**
* Defines a `Sort` controller instance.
*
* @param props - The configurable `Sort` properties.
* @returns The `Sort` controller definition.
*
* @internal
*/
export function defineSort<
TOptions extends ControllerDefinitionOption | undefined,
>(props?: SortProps, options?: TOptions) {
ensureAtLeastOneSolutionType(options);
return {
...options,
build: (engine, solutionType) =>
solutionType === SolutionType.listing
? buildProductListing(engine).sort(props)
: buildSearch(engine).sort(props),
} as SubControllerDefinitionWithoutProps<Sort, TOptions>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {ensureAtLeastOneSolutionType} from '../../../../app/commerce-ssr-engine/common';
import {
ControllerDefinitionOption,
SolutionType,
SubControllerDefinitionWithoutProps,
} from '../../../../app/commerce-ssr-engine/types/common';
import {buildProductListing} from '../../product-listing/headless-product-listing';
import {ProductListingSummaryState} from '../../product-listing/summary/headless-product-listing-summary';
import {RecommendationsSummaryState} from '../../recommendations/summary/headless-recommendations-summary';
import {buildSearch} from '../../search/headless-search';
import {SearchSummaryState} from '../../search/summary/headless-search-summary';
import {Summary, SummaryState} from './headless-core-summary';

export type {
Summary,
ProductListingSummaryState,
RecommendationsSummaryState,
SearchSummaryState,
SummaryState,
};

/**
* Defines a `Summary` controller instance.
*
* @returns The `Summary` controller definition.
*
* @internal
*/
export function defineSummary<
TOptions extends ControllerDefinitionOption | undefined,
>(options?: TOptions) {
ensureAtLeastOneSolutionType(options);
return {
...options,
build: (engine, solutionType) =>
solutionType === SolutionType.listing
? buildProductListing(engine).summary()
: buildSearch(engine).summary(),
} as SubControllerDefinitionWithoutProps<Summary, TOptions>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import {buildSearch, Search} from '../search/headless-search';
import {ProductListing, buildProductListing} from './headless-product-listing';

export type {ProductListingState as ProductListState} from './headless-product-listing';
export type ProductList = Pick<ProductListing | Search, 'state' | 'subscribe'>;
export type ProductList = Pick<
ProductListing | Search,
'state' | 'subscribe' | 'interactiveProduct'
>;

/**
* Defines a `ProductListing` controller instance.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {CommerceEngine} from '../../../app/commerce-engine/commerce-engine';
import {SharedControllerDefinitionWithoutProps} from '../../../app/commerce-ssr-engine/types/common';
import {
buildController,
Controller,
} from '../../controller/headless-controller';
import {
buildProductView,
ProductView as BaseProductView,
} from './headless-product-view';

export interface ProductViewDefinition
extends SharedControllerDefinitionWithoutProps<ProductView> {}

/**
* Defines a `ProductView` controller instance.
*
* This controller is stateless and does not implement a `subscribe` method,
* making it simpler but different from other controllers in the system.
* Its sole purpose is to log an `ec.productView` event.
*
* @returns The `ProductView` controller definition.
*
* @internal
*/
export function defineProductView(): ProductViewDefinition {
return {
listing: true,
search: true,
build: (engine) => buildSSRProductView(engine),
};
}

export interface ProductView extends BaseProductView, Controller {}

function buildSSRProductView(engine: CommerceEngine): ProductView {
const controller = buildController(engine);
const productView = buildProductView(engine);
return {
...controller,
...productView,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {SearchOnlyControllerDefinitionWithoutProps} from '../../../../app/commerce-ssr-engine/types/common';
import {buildSearch} from '../headless-search';
import {DidYouMean, DidYouMeanState} from './headless-did-you-mean';

export type {DidYouMean, DidYouMeanState};

/**
* Defines a `DidYouMean` controller instance.
*
* @returns The `DidYouMean` controller definition.
*
* @internal
* */
export function defineDidYouMean(): SearchOnlyControllerDefinitionWithoutProps<DidYouMean> {
return {
search: true,
build: (engine) => buildSearch(engine).didYouMean(),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export const searchSerializer: Serializer<CommerceSearchParameters> = {
deserialize,
};

// TODO KIT-3462: add/export commerce SSR parameter serializer

export const productListingSerializer = {
serialize,
deserialize,
Expand Down
52 changes: 38 additions & 14 deletions packages/headless/src/ssr-commerce.index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,29 +53,53 @@ export type {
Subscribable,
} from './controllers/controller/headless-controller';

export type {CategoryFacet} from './controllers/commerce/core/facets/category/headless-commerce-category-facet';
export type {
DateFacet,
DateFacetState,
} from './controllers/commerce/core/facets/date/headless-commerce-date-facet';
export type {RegularFacetValue} from './controllers/commerce/core/facets/headless-core-commerce-facet';
DidYouMean,
DidYouMeanState,
} from './controllers/commerce/search/did-you-mean/headless-did-you-mean.ssr';
export {defineDidYouMean} from './controllers/commerce/search/did-you-mean/headless-did-you-mean.ssr';

export type {
Pagination,
PaginationProps,
PaginationState,
} from './controllers/commerce/core/pagination/headless-core-commerce-pagination.ssr';
export {definePagination} from './controllers/commerce/core/pagination/headless-core-commerce-pagination.ssr';

export type {
NumericFacet,
NumericFacetState,
} from './controllers/commerce/core/facets/numeric/headless-commerce-numeric-facet';
export type {RegularFacet} from './controllers/commerce/core/facets/regular/headless-commerce-regular-facet';
ParameterManager,
ParameterManagerProps,
ParameterManagerState,
Parameters,
ProductListingParameters,
CommerceSearchParameters,
} from './controllers/commerce/core/parameter-manager/headless-core-parameter-manager.ssr';
export {defineParameterManager} from './controllers/commerce/core/parameter-manager/headless-core-parameter-manager.ssr';

export type {
ProductList,
ProductListState,
} from './controllers/commerce/product-listing/headless-product-listing.ssr';
export {defineProductList} from './controllers/commerce/product-listing/headless-product-listing.ssr';

export type {ProductView} from './controllers/commerce/product-view/headless-product-view.ssr';
export {defineProductView} from './controllers/commerce/product-view/headless-product-view.ssr';

export type {
Sort,
SortProps,
SortState,
} from './controllers/commerce/core/sort/headless-core-commerce-sort.ssr';
export {defineSort} from './controllers/commerce/core/sort/headless-core-commerce-sort.ssr';

export type {
ProductListingSummaryState,
Summary,
} from './controllers/commerce/core/sub-controller/headless-sub-controller.ssr';
export {defineQuerySummary} from './controllers/commerce/core/sub-controller/headless-sub-controller.ssr';
ProductListingSummaryState,
RecommendationsSummaryState,
SearchSummaryState,
SummaryState,
} from './controllers/commerce/core/summary/headless-core-summary.ssr';
export {defineSummary} from './controllers/commerce/core/summary/headless-core-summary.ssr';

// TODO: KIT-3391 - export other SSR commerce controllers

Expand Down Expand Up @@ -109,7 +133,7 @@ export {buildResultTemplatesManager} from './features/result-templates/result-te
//#endregion

// Types & Helpers
export {buildSSRSearchParameterSerializer} from './features/search-parameters/search-parameter-serializer.ssr';
// TODO KIT-3462: add export commerce SSR parameter serializer
export type {
BaseProduct,
Product,
Expand All @@ -134,7 +158,7 @@ export {
buildRelevanceSortCriterion,
} from './features/sort-criteria/criteria';
export {parseCriterionExpression} from './features/sort-criteria/criteria-parser';
export type {Template} from './features/templates/templates-manager.ts';
export type {Template} from './features/templates/templates-manager';
export type {
ProductTemplate,
ProductTemplateCondition,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// TODO // TODO KIT-3463: implement did you mean in sample
Loading

0 comments on commit 23b4d59

Please sign in to comment.