Skip to content

Commit

Permalink
feat(commerce): add core + plp date range facets (#3481)
Browse files Browse the repository at this point in the history
  • Loading branch information
fbeaudoincoveo authored Dec 14, 2023
1 parent b644bdf commit 19eb84a
Show file tree
Hide file tree
Showing 37 changed files with 1,436 additions and 265 deletions.
5 changes: 5 additions & 0 deletions packages/headless/src/commerce.index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,20 @@ export {buildSearchSort} from './controllers/commerce/search/sort/headless-searc

export type {CommerceRegularFacet} from './controllers/commerce/facets/core/regular/headless-commerce-regular-facet';
export type {CommerceNumericFacet} from './controllers/commerce/facets/core/numeric/headless-commerce-numeric-facet';
export type {CommerceDateFacet} from './controllers/commerce/facets/core/date/headless-commerce-date-facet';
export type {
FacetType,
FacetValueRequest,
RegularFacetValue,
NumericRangeRequest,
NumericFacetValue,
DateRangeRequest,
DateFacetValue,
} from './controllers/commerce/facets/core/headless-core-commerce-facet';
export type {ProductListingFacetGenerator} from './controllers/commerce/product-listing/facets/headless-product-listing-facet-generator';
export {buildProductListingFacetGenerator} from './controllers/commerce/product-listing/facets/headless-product-listing-facet-generator';
export type {SearchFacetGenerator} from './controllers/commerce/search/facets/headless-search-facet-generator';
export {buildSearchFacetGenerator} from './controllers/commerce/search/facets/headless-search-facet-generator';

export type {Search} from './controllers/commerce/search/headless-search';
export {buildSearch} from './controllers/commerce/search/headless-search';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {CommerceFacetRequest} from '../../../../../features/commerce/facets/facet-set/interfaces/request';
import {FacetType} from '../../../../../features/commerce/facets/facet-set/interfaces/response';
import {fetchProductListing} from '../../../../../features/commerce/product-listing/product-listing-actions';
import {
toggleExcludeDateFacetValue,
toggleSelectDateFacetValue,
} from '../../../../../features/facets/range-facets/date-facet-set/date-facet-actions';
import {CommerceAppState} from '../../../../../state/commerce-app-state';
import {MockCommerceEngine, buildMockCommerceEngine} from '../../../../../test';
import {buildMockCommerceFacetRequest} from '../../../../../test/mock-commerce-facet-request';
import {buildMockCommerceDateFacetResponse} from '../../../../../test/mock-commerce-facet-response';
import {buildMockCommerceFacetSlice} from '../../../../../test/mock-commerce-facet-slice';
import {buildMockCommerceDateFacetValue} from '../../../../../test/mock-commerce-facet-value';
import {buildMockCommerceState} from '../../../../../test/mock-commerce-state';
import {
CommerceDateFacet,
CommerceDateFacetOptions,
buildCommerceDateFacet,
} from './headless-commerce-date-facet';

describe('CommerceDateFacet', () => {
const facetId: string = 'date_facet_id';
const type: FacetType = 'dateRange';
const start = '2023-01-01';
const end = '2024-01-01';
let options: CommerceDateFacetOptions;
let state: CommerceAppState;
let engine: MockCommerceEngine;
let facet: CommerceDateFacet;

function initFacet() {
engine = buildMockCommerceEngine({state});
facet = buildCommerceDateFacet(engine, options);
}

function setFacetRequest(config: Partial<CommerceFacetRequest> = {}) {
state.commerceFacetSet[facetId] = buildMockCommerceFacetSlice({
request: buildMockCommerceFacetRequest({facetId, type, ...config}),
});
state.productListing.facets = [
buildMockCommerceDateFacetResponse({facetId}),
];
}

beforeEach(() => {
options = {
facetId,
fetchResultsActionCreator: fetchProductListing,
};

state = buildMockCommerceState();
setFacetRequest();

initFacet();
});

it('initializes', () => {
expect(facet).toBeTruthy();
});

it('exposes #subscribe method', () => {
expect(facet.subscribe).toBeTruthy();
});

describe('#toggleSelect', () => {
it('dispatches #toggleSelectDateFacetValue', () => {
const facetValue = buildMockCommerceDateFacetValue({start, end});
facet.toggleSelect(facetValue);

expect(engine.actions).toContainEqual(
toggleSelectDateFacetValue({facetId, selection: facetValue})
);
});
});

describe('#toggleExclude', () => {
it('dispatches #toggleExcludeDateFacetValue', () => {
const facetValue = buildMockCommerceDateFacetValue({start, end});
facet.toggleExclude(facetValue);

expect(engine.actions).toContainEqual(
toggleExcludeDateFacetValue({facetId, selection: facetValue})
);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {CommerceEngine} from '../../../../../app/commerce-engine/commerce-engine';
import {
toggleExcludeDateFacetValue,
toggleSelectDateFacetValue,
} from '../../../../../features/facets/range-facets/date-facet-set/date-facet-actions';
import {
CoreCommerceFacet,
CoreCommerceFacetOptions,
DateFacetValue,
DateRangeRequest,
buildCoreCommerceFacet,
} from '../headless-core-commerce-facet';

export type CommerceDateFacetOptions = Omit<
CoreCommerceFacetOptions,
'toggleSelectActionCreator' | 'toggleExcludeActionCreator'
>;

/**
* The `CommerceDateFacet` controller offers a high-level programming interface for implementing date commerce
* facet UI component.
*/
export type CommerceDateFacet = CoreCommerceFacet<
DateRangeRequest,
DateFacetValue
>;

/**
* @internal
*
* **Important:** This initializer is meant for internal use by headless only.
* As an implementer, you must not import or use this initializer directly in your code.
* You will instead interact with `CommerceDateFacet` controller instances through the state of a `FacetGenerator`
* controller.
*
* @param engine - The headless commerce engine.
* @param options - The `CommerceDateFacet` options used internally.
* @returns A `CommerceDateFacet` controller instance.
*/
export function buildCommerceDateFacet(
engine: CommerceEngine,
options: CommerceDateFacetOptions
): CommerceDateFacet {
return buildCoreCommerceFacet<DateRangeRequest, DateFacetValue>(engine, {
options: {
...options,
toggleSelectActionCreator: toggleSelectDateFacetValue,
toggleExcludeActionCreator: toggleExcludeDateFacetValue,
},
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import {buildMockCommerceFacetRequest} from '../../../../../test/mock-commerce-f
import {
buildMockCommerceRegularFacetResponse,
buildMockCommerceNumericFacetResponse,
buildMockCommerceDateFacetResponse,
} from '../../../../../test/mock-commerce-facet-response';
import {buildMockCommerceState} from '../../../../../test/mock-commerce-state';
import {buildProductListingDateFacet} from '../../../product-listing/facets/headless-product-listing-date-facet';
import {buildProductListingNumericFacet} from '../../../product-listing/facets/headless-product-listing-numeric-facet';
import {buildProductListingRegularFacet} from '../../../product-listing/facets/headless-product-listing-regular-facet';
import {
Expand All @@ -28,6 +30,14 @@ describe('CommerceFacetGenerator', () => {
const mockState = buildMockCommerceState();
const facets = [];
switch (type) {
case 'regular':
facets.push(
buildMockCommerceRegularFacetResponse({
facetId,
field: 'some_regular_field',
})
);
break;
case 'numericalRange':
facets.push(
buildMockCommerceNumericFacetResponse({
Expand All @@ -36,17 +46,17 @@ describe('CommerceFacetGenerator', () => {
})
);
break;
case 'regular':
case 'dateRange': // TODO
case 'hierarchical': // TODO
default:
case 'dateRange':
facets.push(
buildMockCommerceRegularFacetResponse({
buildMockCommerceDateFacetResponse({
facetId,
field: 'some_regular_field',
field: 'some_date_field',
})
);
break;
case 'hierarchical': // TODO
default:
break;
}
engine = buildMockCommerceEngine({
state: {
Expand All @@ -69,6 +79,7 @@ describe('CommerceFacetGenerator', () => {
options = {
buildNumericFacet: buildProductListingNumericFacet,
buildRegularFacet: buildProductListingRegularFacet,
buildDateFacet: buildProductListingDateFacet,
};
facetGenerator = buildCommerceFacetGenerator(engine, options);
}
Expand All @@ -95,7 +106,7 @@ describe('CommerceFacetGenerator', () => {
});
});
describe('when facet state contains regular facets', () => {
it('should generate regular facet controllers', () => {
it('generates regular facet controllers', () => {
const facetId = 'regular_facet_id';
initFacetGenerator(facetId, 'regular');

Expand All @@ -107,7 +118,7 @@ describe('CommerceFacetGenerator', () => {
});

describe('when facet state contains numeric facets', () => {
it('should generate numeric facet controllers', () => {
it('generates numeric facet controllers', () => {
const facetId = 'numeric_facet_id';
initFacetGenerator(facetId, 'numericalRange');

Expand All @@ -117,10 +128,18 @@ describe('CommerceFacetGenerator', () => {
);
});
});
});

it('should generate date facet controllers', () => {
// TODO
describe('when facet state contains date facets', () => {
it('generates date facet controllers', () => {
const facetId = 'date_facet_id';
initFacetGenerator(facetId, 'dateRange');

expect(facetGenerator.state.facets.length).toEqual(1);
expect(facetGenerator.state.facets[0].state).toEqual(
buildProductListingDateFacet(engine, {facetId}).state
);
});
});
});

it('should generate category facet controllers', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@ import {
buildController,
Controller,
} from '../../../../controller/headless-controller';
import {ProductListingNumericFacetBuilder} from '../../../product-listing/facets/headless-product-listing-numeric-facet';
import {ProductListingRegularFacetBuilder} from '../../../product-listing/facets/headless-product-listing-regular-facet';
import {CoreCommerceFacet} from '../headless-core-commerce-facet';
import {CommerceDateFacet} from '../date/headless-commerce-date-facet';
import {
CommerceFacetOptions,
CoreCommerceFacet,
} from '../headless-core-commerce-facet';
import {CommerceNumericFacet} from '../numeric/headless-commerce-numeric-facet';
import {CommerceRegularFacet} from '../regular/headless-commerce-regular-facet';

/**
* The `CommerceFacetGenerator` headless controller creates commerce facet controllers from the Commerce API search or
Expand All @@ -41,21 +45,22 @@ export interface CommerceFacetGeneratorState {
facets: CoreCommerceFacet<AnyFacetValueRequest, AnyFacetValueResponse>[];
}

type CommerceRegularFacetBuilder = ProductListingRegularFacetBuilder; // TODO: | CommerceSearchRegularFacetBuilder;
type CommerceNumericFacetBuilder = ProductListingNumericFacetBuilder; // TODO: | CommerceSearchNumericFacetBuilder;
// TODO: type CommerceDateFacetBuilder = ProductListingDateFacetBuilder | CommerceSearchDateFacetBuilder;
// TODO: type CommerceCategoryFacetBuilder = ProductListingCategoryFacetBuilder | CommerceSearchCategoryFacetBuilder;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type CommerceFacetBuilder<Facet extends CoreCommerceFacet<any, any>> = (
engine: CommerceEngine,
options: CommerceFacetOptions
) => Facet;

/**
* @internal
*
* The `CommerceFacetGenerator` options used internally.
*/
export interface CommerceFacetGeneratorOptions {
buildRegularFacet: CommerceRegularFacetBuilder;
buildNumericFacet: CommerceNumericFacetBuilder;
// TODO: buildDateFacet: CommerceDateFacetBuilder;
// TODO: buildCategoryFacet: CommerceNumericFacetBuilder;
buildRegularFacet: CommerceFacetBuilder<CommerceRegularFacet>;
buildNumericFacet: CommerceFacetBuilder<CommerceNumericFacet>;
buildDateFacet: CommerceFacetBuilder<CommerceDateFacet>;
// TODO: buildCategoryFacet: CommerceFacetBuilder<CommerceCategoryFacet>;
}

/**
Expand Down Expand Up @@ -83,7 +88,8 @@ export function buildCommerceFacetGenerator(
switch (type) {
case 'numericalRange':
return options.buildNumericFacet(engine, {facetId});
case 'dateRange': // TODO: return options.buildDateFacet(engine, {facetId});
case 'dateRange':
return options.buildDateFacet(engine, {facetId});
case 'hierarchical': // TODO return options.buildCategoryFacet(engine, {facetId});
case 'regular':
default:
Expand Down
Loading

0 comments on commit 19eb84a

Please sign in to comment.