Skip to content

Commit

Permalink
feat(commerce): commerce search box + query suggest (#3461)
Browse files Browse the repository at this point in the history
* create search box controller

* simplify controller definition

* remove unnecessary mock

* apply review suggestions

* apply more review suggestions

* reset pagination before search

* group state tests

* Revert "reset pagination before search"

This reverts commit 4429385.
  • Loading branch information
Spuffynism authored Dec 12, 2023
1 parent 27a8785 commit 74c3b65
Show file tree
Hide file tree
Showing 21 changed files with 996 additions and 58 deletions.
25 changes: 23 additions & 2 deletions packages/headless/src/api/commerce/commerce-api-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import {
} from './commerce-api-error-response';
import {buildRequest, CommerceAPIRequest} from './common/request';
import {CommerceSuccessResponse} from './common/response';
import {
buildQuerySuggestRequest,
QuerySuggestRequest,
} from './search/query-suggest/query-suggest-request';
import {QuerySuggestSuccessResponse} from './search/query-suggest/query-suggest-response';
import {CommerceSearchRequest} from './search/request';

export interface AsyncThunkCommerceOptions<
Expand Down Expand Up @@ -59,7 +64,23 @@ export class CommerceAPIClient {
});
}

private async query(options: PlatformClientCallOptions) {
async querySuggest(
req: QuerySuggestRequest
): Promise<CommerceAPIResponse<QuerySuggestSuccessResponse>> {
const requestOptions = buildQuerySuggestRequest(req);
return this.query<QuerySuggestSuccessResponse>({
...requestOptions,
requestParams: {
...requestOptions.requestParams,
query: req?.query,
},
...this.options,
});
}

private async query<T = CommerceSuccessResponse>(
options: PlatformClientCallOptions
) {
const response = await PlatformClient.call(options);

if (response instanceof Error) {
Expand All @@ -68,7 +89,7 @@ export class CommerceAPIClient {

const body = await response.json();
return response.ok
? {success: body as CommerceSuccessResponse}
? {success: body as T}
: {error: body as CommerceAPIErrorStatusResponse};
}
}
2 changes: 1 addition & 1 deletion packages/headless/src/api/commerce/common/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const prepareRequestParams = (req: CommerceAPIRequest) => {
};

export const baseRequest = (
req: CommerceAPIRequest,
req: BaseParam & TrackingIdParam,
path: string
): Pick<
PlatformClientCallOptions,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {BaseParam} from '../../../platform-service-params';
import {
ClientIdParam,
CurrencyParam,
LanguageParam,
QueryParam,
TrackingIdParam,
ContextParam,
CountryParam,
} from '../../commerce-api-params';
import {baseRequest} from '../../common/request';

export type QuerySuggestRequest = BaseParam &
TrackingIdParam &
LanguageParam &
CountryParam &
CurrencyParam &
ClientIdParam &
ContextParam &
QueryParam;

export const buildQuerySuggestRequest = (req: QuerySuggestRequest) => {
return {
...baseRequest(req, 'search/querySuggest'),
requestParams: prepareRequestParams(req),
};
};

const prepareRequestParams = (req: QuerySuggestRequest) => {
const {trackingId, query, clientId, context, language, country, currency} =
req;
return {
trackingId,
query,
clientId,
context,
language,
country,
currency,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {
SearchAPIErrorWithExceptionInBody,
SearchAPIErrorWithStatusCode,
} from '../../../search/search-api-error-response';

/**
* A Coveo ML query suggestion.
*/
export interface QuerySuggestCompletion {
/**
* The original query expression.
*/
expression: string;

/**
* The highlighted query completion suggestion.
*/
highlighted: string;
}

/**
* A response from the Coveo ML query suggest service.
*/
export interface QuerySuggestSuccessResponse {
/**
* Contains an array of query suggestions.
*/
completions: QuerySuggestCompletion[];

/**
* The query suggest response ID.
*/
responseId: string;
}

export type QuerySuggest =
| QuerySuggestSuccessResponse
| SearchAPIErrorWithExceptionInBody
| SearchAPIErrorWithStatusCode;
8 changes: 8 additions & 0 deletions packages/headless/src/commerce.index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,11 @@ export {buildProductListingFacetGenerator} from './controllers/commerce/product-

export type {Search} from './controllers/commerce/search/headless-search';
export {buildSearch} from './controllers/commerce/search/headless-search';

export {updateQuery} from './features/commerce/query/query-actions';

export {buildSearchBox} from './controllers/commerce/search-box/headless-search-box';
export type {
SearchBox,
SearchBoxState,
} from './controllers/commerce/search-box/headless-search-box';
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {Schema} from '@coveo/bueno';
import {
searchBoxOptionDefinitions as coreSearchBoxOptionDefinitions,
SearchBoxOptions as CoreSearchBoxOptions,
} from '../../core/search-box/headless-core-search-box-options';

export type SearchBoxOptions = Pick<
CoreSearchBoxOptions,
'id' | 'highlightOptions' | 'clearFilters'
>;

type DefaultSearchBoxOptions = Pick<SearchBoxOptions, 'clearFilters'>;

export const defaultSearchBoxOptions: Required<DefaultSearchBoxOptions> = {
clearFilters: true,
};

const {id, highlightOptions, clearFilters} = coreSearchBoxOptionDefinitions;
export const searchBoxOptionDefinitions = {id, highlightOptions, clearFilters};

export const searchBoxOptionsSchema = new Schema<Required<SearchBoxOptions>>(
searchBoxOptionDefinitions
);
Loading

0 comments on commit 74c3b65

Please sign in to comment.