diff --git a/package-lock.json b/package-lock.json
index 34c77b27161..b65e805f9cf 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -55808,7 +55808,7 @@
},
"packages/atomic": {
"name": "@coveo/atomic",
- "version": "2.77.0",
+ "version": "2.77.2",
"license": "Apache-2.0",
"dependencies": {
"@coveo/bueno": "0.46.1",
@@ -55830,7 +55830,7 @@
"@axe-core/playwright": "4.9.1",
"@babel/core": "7.24.9",
"@coveo/atomic": "file:.",
- "@coveo/headless": "2.77.0",
+ "@coveo/headless": "2.79.0",
"@coveo/release": "1.0.0",
"@custom-elements-manifest/analyzer": "0.10.3",
"@fullhuman/postcss-purgecss": "6.0.0",
@@ -55907,7 +55907,7 @@
"node": ">=12.9.0"
},
"peerDependencies": {
- "@coveo/headless": "2.77.0"
+ "@coveo/headless": "2.79.0"
}
},
"packages/atomic-angular": {
@@ -55922,14 +55922,14 @@
"@angular/platform-browser": "17.3.12",
"@angular/platform-browser-dynamic": "17.3.12",
"@angular/router": "17.3.12",
- "@coveo/atomic": "2.77.0",
+ "@coveo/atomic": "2.77.2",
"rxjs": "7.8.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "17.3.8",
"@angular/cli": "17.3.8",
"@angular/compiler-cli": "17.3.12",
- "@coveo/headless": "2.77.0",
+ "@coveo/headless": "2.79.0",
"@types/jasmine": "5.1.4",
"@types/node": "20.14.12",
"jasmine-core": "5.2.0",
@@ -55943,7 +55943,7 @@
"typescript": "5.4.5"
},
"peerDependencies": {
- "@coveo/headless": "2.77.0"
+ "@coveo/headless": "2.79.0"
}
},
"packages/atomic-angular/node_modules/jasmine-core": {
@@ -55972,25 +55972,25 @@
},
"packages/atomic-angular/projects/atomic-angular": {
"name": "@coveo/atomic-angular",
- "version": "2.27.0",
+ "version": "2.27.2",
"license": "Apache-2.0",
"dependencies": {
- "@coveo/atomic": "2.77.0",
+ "@coveo/atomic": "2.77.2",
"tslib": "2.6.3"
},
"peerDependencies": {
"@angular/common": "14 - 17",
"@angular/core": "14 - 17",
- "@coveo/headless": "2.77.0"
+ "@coveo/headless": "2.79.0"
}
},
"packages/atomic-hosted-page": {
"name": "@coveo/atomic-hosted-page",
- "version": "0.6.4",
+ "version": "0.6.6",
"license": "Apache-2.0",
"dependencies": {
"@coveo/bueno": "0.46.1",
- "@coveo/headless": "2.77.0",
+ "@coveo/headless": "2.79.0",
"@stencil/core": "4.20.0"
},
"devDependencies": {
@@ -56069,12 +56069,12 @@
},
"packages/atomic-react": {
"name": "@coveo/atomic-react",
- "version": "2.13.3",
+ "version": "2.13.5",
"dependencies": {
- "@coveo/atomic": "2.77.0"
+ "@coveo/atomic": "2.77.2"
},
"devDependencies": {
- "@coveo/headless": "2.77.0",
+ "@coveo/headless": "2.79.0",
"@coveo/release": "1.0.0",
"@rollup/plugin-commonjs": "^25.0.0",
"@rollup/plugin-node-resolve": "^15.0.0",
@@ -56091,7 +56091,7 @@
"rollup-plugin-polyfill-node": "^0.13.0"
},
"peerDependencies": {
- "@coveo/headless": "2.77.0",
+ "@coveo/headless": "2.79.0",
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
}
@@ -59225,7 +59225,7 @@
},
"packages/headless": {
"name": "@coveo/headless",
- "version": "2.77.0",
+ "version": "2.79.0",
"license": "Apache-2.0",
"dependencies": {
"@coveo/bueno": "0.46.1",
@@ -59268,10 +59268,10 @@
},
"packages/headless-react": {
"name": "@coveo/headless-react",
- "version": "1.1.1",
+ "version": "1.1.3",
"license": "Apache-2.0",
"dependencies": {
- "@coveo/headless": "2.77.0"
+ "@coveo/headless": "2.79.0"
},
"devDependencies": {
"@coveo/release": "1.0.0",
@@ -59826,12 +59826,12 @@
},
"packages/quantic": {
"name": "@coveo/quantic",
- "version": "2.55.0",
+ "version": "2.57.0",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@coveo/bueno": "0.46.1",
- "@coveo/headless": "2.77.0",
+ "@coveo/headless": "2.79.0",
"dompurify": "3.1.6",
"marked": "12.0.2"
},
@@ -61528,7 +61528,7 @@
"@angular/platform-browser": "17.3.12",
"@angular/platform-browser-dynamic": "17.3.12",
"@angular/router": "17.3.12",
- "@coveo/atomic-angular": "2.27.0",
+ "@coveo/atomic-angular": "2.27.2",
"rxjs": "7.8.1",
"tslib": "2.6.3",
"zone.js": "0.14.8"
@@ -61829,9 +61829,9 @@
"name": "@coveo/atomic-next-samples",
"version": "0.0.0",
"dependencies": {
- "@coveo/atomic": "2.77.0",
- "@coveo/atomic-react": "2.13.3",
- "@coveo/headless": "2.77.0",
+ "@coveo/atomic": "2.77.2",
+ "@coveo/atomic-react": "2.13.5",
+ "@coveo/headless": "2.79.0",
"next": "14.2.5",
"react": "18.3.1",
"react-dom": "18.3.1"
@@ -61894,9 +61894,9 @@
"name": "@coveo/atomic-react-samples",
"version": "0.0.0",
"dependencies": {
- "@coveo/atomic": "2.77.0",
- "@coveo/atomic-react": "2.13.3",
- "@coveo/headless": "2.77.0",
+ "@coveo/atomic": "2.77.2",
+ "@coveo/atomic-react": "2.13.5",
+ "@coveo/headless": "2.79.0",
"react": "18.3.1",
"react-dom": "18.3.1"
},
@@ -62384,7 +62384,7 @@
"name": "@coveo/headless-commerce-react-samples",
"version": "0.1.0",
"dependencies": {
- "@coveo/headless": "2.77.0",
+ "@coveo/headless": "2.79.0",
"@testing-library/jest-dom": "6.4.8",
"@testing-library/react": "16.0.0",
"@testing-library/user-event": "14.5.2",
@@ -64852,7 +64852,7 @@
"version": "0.0.0",
"dependencies": {
"@coveo/auth": "1.11.22",
- "@coveo/headless": "2.77.0",
+ "@coveo/headless": "2.79.0",
"@testing-library/jest-dom": "6.4.8",
"@testing-library/react": "14.3.1",
"@testing-library/user-event": "14.5.2",
@@ -68518,8 +68518,8 @@
"name": "@coveo/headless-ssr-samples-common",
"version": "0.0.0",
"dependencies": {
- "@coveo/headless": "2.77.0",
- "@coveo/headless-react": "1.1.1",
+ "@coveo/headless": "2.79.0",
+ "@coveo/headless-react": "1.1.3",
"next": "14.2.5",
"react": "^18.2.0",
"react-dom": "^18.2.0"
@@ -68543,7 +68543,7 @@
"name": "@coveo/headless-ssr-commerce-samples",
"version": "0.0.0",
"dependencies": {
- "@coveo/headless": "2.77.0",
+ "@coveo/headless": "2.79.0",
"next": "14.2.5",
"react": "^18.2.0",
"react-dom": "^18.2.0"
@@ -68662,10 +68662,10 @@
"version": "0.1.0",
"dependencies": {
"@babel/standalone": "7.25.0",
- "@coveo/atomic": "2.77.0",
- "@coveo/atomic-hosted-page": "0.6.4",
- "@coveo/atomic-react": "2.13.3",
- "@coveo/headless": "2.77.0",
+ "@coveo/atomic": "2.77.2",
+ "@coveo/atomic-hosted-page": "0.6.6",
+ "@coveo/atomic-react": "2.13.5",
+ "@coveo/headless": "2.79.0",
"react": "18.3.1",
"react-dom": "18.3.1"
},
@@ -68734,8 +68734,8 @@
"name": "@coveo/atomic-stencil-samples",
"version": "0.0.0",
"dependencies": {
- "@coveo/atomic": "2.77.0",
- "@coveo/headless": "2.77.0",
+ "@coveo/atomic": "2.77.2",
+ "@coveo/headless": "2.79.0",
"@stencil/core": "4.20.0",
"stencil-router-v2": "0.6.0"
},
@@ -69018,7 +69018,7 @@
"name": "@coveo/atomic-vuejs-samples",
"version": "0.0.0",
"dependencies": {
- "@coveo/atomic": "2.77.0",
+ "@coveo/atomic": "2.77.2",
"vue": "^3.4.15"
},
"devDependencies": {
diff --git a/packages/atomic-angular/package.json b/packages/atomic-angular/package.json
index 2e3662a979c..0c22d6b8c29 100644
--- a/packages/atomic-angular/package.json
+++ b/packages/atomic-angular/package.json
@@ -20,17 +20,17 @@
"@angular/platform-browser": "17.3.12",
"@angular/platform-browser-dynamic": "17.3.12",
"@angular/router": "17.3.12",
- "@coveo/atomic": "2.77.0",
+ "@coveo/atomic": "2.77.2",
"rxjs": "7.8.1"
},
"peerDependencies": {
- "@coveo/headless": "2.77.0"
+ "@coveo/headless": "2.79.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "17.3.8",
"@angular/cli": "17.3.8",
"@angular/compiler-cli": "17.3.12",
- "@coveo/headless": "2.77.0",
+ "@coveo/headless": "2.79.0",
"@types/jasmine": "5.1.4",
"@types/node": "20.14.12",
"jasmine-core": "5.2.0",
diff --git a/packages/atomic-angular/projects/atomic-angular/CHANGELOG.md b/packages/atomic-angular/projects/atomic-angular/CHANGELOG.md
index 2374f4f5704..f8027e3c64b 100644
--- a/packages/atomic-angular/projects/atomic-angular/CHANGELOG.md
+++ b/packages/atomic-angular/projects/atomic-angular/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 2.27.1 (2024-08-20)
+
+### Bug Fixes
+
+- **refine-modal:** include facets from atomic-external ([#4219](https://github.com/coveo/ui-kit/issues/4219)) ([107c56b](https://github.com/coveo/ui-kit/commits/107c56b2d4a1467847210db05b1726b36ad4b50c))
+
# 2.27.0 (2024-08-14)
### Features
diff --git a/packages/atomic-angular/projects/atomic-angular/package.json b/packages/atomic-angular/projects/atomic-angular/package.json
index 1e77ce8f223..f4472094f35 100644
--- a/packages/atomic-angular/projects/atomic-angular/package.json
+++ b/packages/atomic-angular/projects/atomic-angular/package.json
@@ -1,6 +1,6 @@
{
"name": "@coveo/atomic-angular",
- "version": "2.27.0",
+ "version": "2.27.2",
"license": "Apache-2.0",
"repository": {
"url": "https://github.com/coveo/ui-kit"
@@ -8,10 +8,10 @@
"peerDependencies": {
"@angular/common": "14 - 17",
"@angular/core": "14 - 17",
- "@coveo/headless": "2.77.0"
+ "@coveo/headless": "2.79.0"
},
"dependencies": {
- "@coveo/atomic": "2.77.0",
+ "@coveo/atomic": "2.77.2",
"tslib": "2.6.3"
}
}
diff --git a/packages/atomic-hosted-page/package.json b/packages/atomic-hosted-page/package.json
index f731b99dafd..3d64bed6768 100644
--- a/packages/atomic-hosted-page/package.json
+++ b/packages/atomic-hosted-page/package.json
@@ -1,7 +1,7 @@
{
"name": "@coveo/atomic-hosted-page",
"description": "Web Component used to inject a Coveo Hosted Search Page in the DOM.",
- "version": "0.6.4",
+ "version": "0.6.6",
"repository": {
"type": "git",
"url": "git+https://github.com/coveo/ui-kit.git",
@@ -31,7 +31,7 @@
},
"dependencies": {
"@coveo/bueno": "0.46.1",
- "@coveo/headless": "2.77.0",
+ "@coveo/headless": "2.79.0",
"@stencil/core": "4.20.0"
},
"devDependencies": {
diff --git a/packages/atomic-react/package.json b/packages/atomic-react/package.json
index dfe510b59d0..37c7868bcb8 100644
--- a/packages/atomic-react/package.json
+++ b/packages/atomic-react/package.json
@@ -1,7 +1,7 @@
{
"name": "@coveo/atomic-react",
"sideEffects": false,
- "version": "2.13.3",
+ "version": "2.13.5",
"description": "React specific wrapper for the Atomic component library",
"repository": {
"type": "git",
@@ -29,11 +29,11 @@
"commerce/"
],
"dependencies": {
- "@coveo/atomic": "2.77.0"
+ "@coveo/atomic": "2.77.2"
},
"devDependencies": {
"@coveo/release": "1.0.0",
- "@coveo/headless": "2.77.0",
+ "@coveo/headless": "2.79.0",
"@rollup/plugin-commonjs": "^25.0.0",
"@rollup/plugin-node-resolve": "^15.0.0",
"@rollup/plugin-replace": "^5.0.0",
@@ -49,7 +49,7 @@
"@rollup/plugin-terser": "0.4.4"
},
"peerDependencies": {
- "@coveo/headless": "2.77.0",
+ "@coveo/headless": "2.79.0",
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
}
diff --git a/packages/atomic/CHANGELOG.md b/packages/atomic/CHANGELOG.md
index 18e92103719..257308a2725 100644
--- a/packages/atomic/CHANGELOG.md
+++ b/packages/atomic/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 2.77.1 (2024-08-20)
+
+### Bug Fixes
+
+- **refine-modal:** include facets from atomic-external ([#4219](https://github.com/coveo/ui-kit/issues/4219)) ([107c56b](https://github.com/coveo/ui-kit/commits/107c56b2d4a1467847210db05b1726b36ad4b50c))
+
# 2.77.0 (2024-08-14)
### Bug Fixes
diff --git a/packages/atomic/package.json b/packages/atomic/package.json
index b50e9fc3c6a..935250ba696 100644
--- a/packages/atomic/package.json
+++ b/packages/atomic/package.json
@@ -1,6 +1,6 @@
{
"name": "@coveo/atomic",
- "version": "2.77.0",
+ "version": "2.77.2",
"description": "A web-component library for building modern UIs interfacing with the Coveo platform",
"homepage": "https://docs.coveo.com/en/atomic/latest/",
"repository": {
@@ -66,7 +66,7 @@
"@axe-core/playwright": "4.9.1",
"@babel/core": "7.24.9",
"@coveo/atomic": "file:.",
- "@coveo/headless": "2.77.0",
+ "@coveo/headless": "2.79.0",
"@coveo/release": "1.0.0",
"@custom-elements-manifest/analyzer": "0.10.3",
"@fullhuman/postcss-purgecss": "6.0.0",
@@ -140,7 +140,7 @@
"wait-on": "7.2.0"
},
"peerDependencies": {
- "@coveo/headless": "2.77.0"
+ "@coveo/headless": "2.79.0"
},
"license": "Apache-2.0",
"engines": {
diff --git a/packages/headless-react/package.json b/packages/headless-react/package.json
index 7360d3229c9..f404f3bd434 100644
--- a/packages/headless-react/package.json
+++ b/packages/headless-react/package.json
@@ -1,6 +1,6 @@
{
"name": "@coveo/headless-react",
- "version": "1.1.1",
+ "version": "1.1.3",
"description": "React utilities for SSR (Server Side Rendering) with headless",
"homepage": "https://docs.coveo.com/en/headless/latest/",
"repository": {
@@ -33,7 +33,7 @@
"promote:npm:latest": "node ../../scripts/deploy/update-npm-tag.mjs latest"
},
"dependencies": {
- "@coveo/headless": "2.77.0"
+ "@coveo/headless": "2.79.0"
},
"devDependencies": {
"@coveo/release": "1.0.0",
diff --git a/packages/headless/CHANGELOG.md b/packages/headless/CHANGELOG.md
index 63cea604235..a60fad6b209 100644
--- a/packages/headless/CHANGELOG.md
+++ b/packages/headless/CHANGELOG.md
@@ -1,3 +1,20 @@
+# 2.79.0 (2024-08-21)
+
+### Features
+
+- **headless:** Add locale to insight search ([#4302](https://github.com/coveo/ui-kit/issues/4302)) ([3bea5b6](https://github.com/coveo/ui-kit/commits/3bea5b6df981f514f158fc530ee2e4428421b3e6))
+
+# 2.78.0 (2024-08-20)
+
+### Bug Fixes
+
+- **answerApi:** search context trigger new request ([#4293](https://github.com/coveo/ui-kit/issues/4293)) ([49b33a6](https://github.com/coveo/ui-kit/commits/49b33a66d55f1823029e51c58ce72e696fb56f01))
+- **rga:** state reset keep configid ([#4286](https://github.com/coveo/ui-kit/issues/4286)) ([a822cea](https://github.com/coveo/ui-kit/commits/a822ceab6f6fd9866a261c73248f0d580eb8b05d))
+
+### Features
+
+- **headless SSR:** support both search and listing solution types ([#4249](https://github.com/coveo/ui-kit/issues/4249)) ([dcd35d8](https://github.com/coveo/ui-kit/commits/dcd35d87e674a7d95e72800ae5b5dfc56d8ddcb8))
+
# 2.77.0 (2024-08-14)
### Bug Fixes
diff --git a/packages/headless/package.json b/packages/headless/package.json
index 1c8f57e4e48..9c62f13c8ff 100644
--- a/packages/headless/package.json
+++ b/packages/headless/package.json
@@ -14,7 +14,7 @@
},
"types": "./dist/definitions/index.d.ts",
"license": "Apache-2.0",
- "version": "2.77.0",
+ "version": "2.79.0",
"files": [
"dist/",
"recommendation/",
diff --git a/packages/headless/src/api/service/insight/query/query-request.ts b/packages/headless/src/api/service/insight/query/query-request.ts
index 3e365a326d8..4cc00a566b6 100644
--- a/packages/headless/src/api/service/insight/query/query-request.ts
+++ b/packages/headless/src/api/service/insight/query/query-request.ts
@@ -1,6 +1,7 @@
import {
ContextParam,
FoldingParam,
+ LocaleParam,
NumberOfResultsParam,
} from '../../../platform-service-params';
import {
@@ -36,6 +37,7 @@ export type InsightQueryRequest = InsightParam &
TabParam &
FoldingParam &
ContextParam &
+ LocaleParam &
PipelineRuleParams;
interface CaseContextParam {
diff --git a/packages/headless/src/app/insight-engine/insight-engine-configuration.ts b/packages/headless/src/app/insight-engine/insight-engine-configuration.ts
index 93a2204b62e..9e3353965dd 100644
--- a/packages/headless/src/app/insight-engine/insight-engine-configuration.ts
+++ b/packages/headless/src/app/insight-engine/insight-engine-configuration.ts
@@ -1,5 +1,8 @@
-import {Schema} from '@coveo/bueno';
-import {requiredNonEmptyString} from '../../utils/validate-payload';
+import {RecordValue, Schema} from '@coveo/bueno';
+import {
+ nonEmptyString,
+ requiredNonEmptyString,
+} from '../../utils/validate-payload';
import {
EngineConfiguration,
engineConfigurationDefinitions,
@@ -13,10 +16,36 @@ export interface InsightEngineConfiguration extends EngineConfiguration {
* Specifies the unique identifier of the target insight configuration.
*/
insightId: string;
+ /**
+ * Specifies the configuration for the insight search.
+ */
+ search?: InsightEngineSearchConfigurationOptions;
+}
+
+/**
+ * The insight engine search configuration options.
+ */
+export interface InsightEngineSearchConfigurationOptions {
+ /**
+ * The locale of the current user. Must comply with IETF’s BCP 47 definition: https://www.rfc-editor.org/rfc/bcp/bcp47.txt.
+ *
+ * Notes:
+ * Coveo Machine Learning models use this information to provide contextually relevant output.
+ * Moreover, this information can be referred to in query expressions and QPL statements by using the $locale object.
+ */
+ locale?: string;
}
export const insightEngineConfigurationSchema =
new Schema({
...engineConfigurationDefinitions,
insightId: requiredNonEmptyString,
+ search: new RecordValue({
+ options: {
+ required: false,
+ },
+ values: {
+ locale: nonEmptyString,
+ },
+ }),
});
diff --git a/packages/headless/src/app/insight-engine/insight-engine.test.ts b/packages/headless/src/app/insight-engine/insight-engine.test.ts
index 111415266c5..f703bad376b 100644
--- a/packages/headless/src/app/insight-engine/insight-engine.test.ts
+++ b/packages/headless/src/app/insight-engine/insight-engine.test.ts
@@ -23,7 +23,12 @@ describe('buildInsightEngine', () => {
beforeEach(() => {
options = {
- configuration: getSampleInsightEngineConfiguration(),
+ configuration: {
+ ...getSampleInsightEngineConfiguration(),
+ search: {
+ locale: 'en-US',
+ },
+ },
loggerOptions: {level: 'silent'},
};
@@ -41,6 +46,10 @@ describe('buildInsightEngine', () => {
);
});
+ it('sets the locale correctly', () => {
+ expect(engine.state.configuration?.search?.locale).toEqual('en-US');
+ });
+
it('exposes an #executeFirstSearch method', () => {
expect(engine.executeFirstSearch).toBeTruthy();
});
diff --git a/packages/headless/src/app/insight-engine/insight-engine.ts b/packages/headless/src/app/insight-engine/insight-engine.ts
index 12a191f632b..d18c542377b 100644
--- a/packages/headless/src/app/insight-engine/insight-engine.ts
+++ b/packages/headless/src/app/insight-engine/insight-engine.ts
@@ -5,6 +5,7 @@ import {NoopPreprocessRequest} from '../../api/preprocess-request';
import {InsightAPIClient} from '../../api/service/insight/insight-api-client';
import {interfaceLoad} from '../../features/analytics/analytics-actions';
import {LegacySearchAction} from '../../features/analytics/analytics-utils';
+import {updateSearchConfiguration} from '../../features/configuration/configuration-actions';
import {setInsightConfiguration} from '../../features/insight-configuration/insight-configuration-actions';
import {insightConfigurationReducer as insightConfiguration} from '../../features/insight-configuration/insight-configuration-slice';
import {insightInterfaceReducer as insightInterface} from '../../features/insight-interface/insight-interface-slice';
@@ -28,9 +29,13 @@ import {buildThunkExtraArguments} from '../thunk-extra-arguments';
import {
InsightEngineConfiguration,
insightEngineConfigurationSchema,
+ InsightEngineSearchConfigurationOptions,
} from './insight-engine-configuration';
-export type {InsightEngineConfiguration};
+export type {
+ InsightEngineConfiguration,
+ InsightEngineSearchConfigurationOptions,
+};
const insightEngineReducers = {
insightConfiguration,
@@ -99,7 +104,7 @@ export function buildInsightEngine(
const engine = buildEngine(augmentedOptions, thunkArguments);
- const {insightId} = options.configuration;
+ const {insightId, search} = options.configuration;
engine.dispatch(
setInsightConfiguration({
@@ -107,6 +112,10 @@ export function buildInsightEngine(
})
);
+ if (search) {
+ engine.dispatch(updateSearchConfiguration(search));
+ }
+
return {
...engine,
diff --git a/packages/headless/src/controllers/commerce/core/pagination/headless-core-commerce-pagination.ssr.ts b/packages/headless/src/controllers/commerce/core/pagination/headless-core-commerce-pagination.ssr.ts
new file mode 100644
index 00000000000..1822d547246
--- /dev/null
+++ b/packages/headless/src/controllers/commerce/core/pagination/headless-core-commerce-pagination.ssr.ts
@@ -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;
+}
diff --git a/packages/headless/src/controllers/commerce/core/parameter-manager/headless-core-parameter-manager.ssr.ts b/packages/headless/src/controllers/commerce/core/parameter-manager/headless-core-parameter-manager.ssr.ts
new file mode 100644
index 00000000000..17e2dc5b1c7
--- /dev/null
+++ b/packages/headless/src/controllers/commerce/core/parameter-manager/headless-core-parameter-manager.ssr.ts
@@ -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>,
+ TOptions,
+ ParameterManagerProps>
+ >;
+}
+
+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> {
+ engine.addReducers({
+ commerceFacetSet,
+ commerceSort,
+ commercePagination,
+ facetOrder,
+ manualNumericFacetSet,
+ });
+ return true;
+}
+
+function loadCommerceSearchParameterReducers(
+ engine: CoreEngineNext
+): engine is CoreEngineNext> {
+ loadCommerceCommonParameterReducers(engine);
+ engine.addReducers({query, querySet});
+ return true;
+}
+
+function loadCommerceProductListingParameterReducers(
+ engine: CoreEngineNext
+): engine is CoreEngineNext> {
+ loadCommerceCommonParameterReducers(engine);
+ return true;
+}
diff --git a/packages/headless/src/controllers/commerce/core/sort/headless-core-commerce-sort.ssr.ts b/packages/headless/src/controllers/commerce/core/sort/headless-core-commerce-sort.ssr.ts
new file mode 100644
index 00000000000..501c93c9efe
--- /dev/null
+++ b/packages/headless/src/controllers/commerce/core/sort/headless-core-commerce-sort.ssr.ts
@@ -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;
+}
diff --git a/packages/headless/src/controllers/commerce/core/summary/headless-core-summary.ssr.ts b/packages/headless/src/controllers/commerce/core/summary/headless-core-summary.ssr.ts
new file mode 100644
index 00000000000..ace73ffa7f0
--- /dev/null
+++ b/packages/headless/src/controllers/commerce/core/summary/headless-core-summary.ssr.ts
@@ -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;
+}
diff --git a/packages/headless/src/controllers/commerce/product-listing/headless-product-listing.ssr.ts b/packages/headless/src/controllers/commerce/product-listing/headless-product-listing.ssr.ts
index 69163c28983..444b7bc7e7a 100644
--- a/packages/headless/src/controllers/commerce/product-listing/headless-product-listing.ssr.ts
+++ b/packages/headless/src/controllers/commerce/product-listing/headless-product-listing.ssr.ts
@@ -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;
+export type ProductList = Pick<
+ ProductListing | Search,
+ 'state' | 'subscribe' | 'interactiveProduct'
+>;
/**
* Defines a `ProductListing` controller instance.
diff --git a/packages/headless/src/controllers/commerce/product-view/headless-product-view.ssr.ts b/packages/headless/src/controllers/commerce/product-view/headless-product-view.ssr.ts
new file mode 100644
index 00000000000..4d4862a1fdb
--- /dev/null
+++ b/packages/headless/src/controllers/commerce/product-view/headless-product-view.ssr.ts
@@ -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 {}
+
+/**
+ * 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,
+ };
+}
diff --git a/packages/headless/src/controllers/commerce/search/did-you-mean/headless-did-you-mean.ssr.ts b/packages/headless/src/controllers/commerce/search/did-you-mean/headless-did-you-mean.ssr.ts
new file mode 100644
index 00000000000..73844ef12e2
--- /dev/null
+++ b/packages/headless/src/controllers/commerce/search/did-you-mean/headless-did-you-mean.ssr.ts
@@ -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 {
+ return {
+ search: true,
+ build: (engine) => buildSearch(engine).didYouMean(),
+ };
+}
diff --git a/packages/headless/src/features/commerce/parameters/parameters-serializer.ts b/packages/headless/src/features/commerce/parameters/parameters-serializer.ts
index 059fa63cb82..e4f71fe1e71 100644
--- a/packages/headless/src/features/commerce/parameters/parameters-serializer.ts
+++ b/packages/headless/src/features/commerce/parameters/parameters-serializer.ts
@@ -38,6 +38,8 @@ export const searchSerializer: Serializer = {
deserialize,
};
+// TODO KIT-3462: add/export commerce SSR parameter serializer
+
export const productListingSerializer = {
serialize,
deserialize,
diff --git a/packages/headless/src/features/insight-search/insight-search-request.test.ts b/packages/headless/src/features/insight-search/insight-search-request.test.ts
index f26b5cde06f..822db02d2a2 100644
--- a/packages/headless/src/features/insight-search/insight-search-request.test.ts
+++ b/packages/headless/src/features/insight-search/insight-search-request.test.ts
@@ -39,6 +39,7 @@ describe('insight search request', () => {
expect(params.accessToken).toBe(state.configuration.accessToken);
expect(params.organizationId).toBe(state.configuration.organizationId);
expect(params.url).toBe(state.configuration.platformUrl);
+ expect(params.locale).toBe(state.configuration.search.locale);
});
it('#buildInsightSearchRequest returns the state #insightId', async () => {
diff --git a/packages/headless/src/features/insight-search/insight-search-request.ts b/packages/headless/src/features/insight-search/insight-search-request.ts
index 527f9a2aa4e..eab2b7ddd7a 100644
--- a/packages/headless/src/features/insight-search/insight-search-request.ts
+++ b/packages/headless/src/features/insight-search/insight-search-request.ts
@@ -21,11 +21,11 @@ export const buildInsightBaseRequest = async (
): Promise> => {
const cq = buildConstantQuery(state);
const facets = getAllFacets(state);
-
return mapSearchRequest({
accessToken: state.configuration.accessToken,
organizationId: state.configuration.organizationId,
url: state.configuration.platformUrl,
+ locale: state.configuration.search.locale,
insightId: state.insightConfiguration.insightId,
...(state.configuration.analytics.enabled &&
(await fromAnalyticsStateToAnalyticsParams(
diff --git a/packages/headless/src/insight.index.ts b/packages/headless/src/insight.index.ts
index ed5effa583a..3c4d75589ad 100644
--- a/packages/headless/src/insight.index.ts
+++ b/packages/headless/src/insight.index.ts
@@ -11,6 +11,7 @@ export type {
InsightEngine,
InsightEngineOptions,
InsightEngineConfiguration,
+ InsightEngineSearchConfigurationOptions,
} from './app/insight-engine/insight-engine';
export {buildInsightEngine} from './app/insight-engine/insight-engine';
diff --git a/packages/headless/src/ssr-commerce.index.ts b/packages/headless/src/ssr-commerce.index.ts
index d6ab85296a9..e1002f58379 100644
--- a/packages/headless/src/ssr-commerce.index.ts
+++ b/packages/headless/src/ssr-commerce.index.ts
@@ -53,17 +53,28 @@ 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,
@@ -71,11 +82,24 @@ export type {
} 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
@@ -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,
@@ -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,
diff --git a/packages/quantic/CHANGELOG.md b/packages/quantic/CHANGELOG.md
index ad2d352155a..81f292e663c 100644
--- a/packages/quantic/CHANGELOG.md
+++ b/packages/quantic/CHANGELOG.md
@@ -1,3 +1,16 @@
+# 2.57.0 (2024-08-21)
+
+### Features
+
+- **headless:** Add locale to insight search ([#4302](https://github.com/coveo/ui-kit/issues/4302)) ([3bea5b6](https://github.com/coveo/ui-kit/commits/3bea5b6df981f514f158fc530ee2e4428421b3e6))
+
+# 2.56.0 (2024-08-20)
+
+### Features
+
+- **quantic:** Add option for rephrase buttons, remove citations numbers ([#4277](https://github.com/coveo/ui-kit/issues/4277)) ([e65b99f](https://github.com/coveo/ui-kit/commits/e65b99f2bf1b8f2fead1595942a439731dd3f623))
+- **quantic:** added support for custom options in the quanticSort component ([#4101](https://github.com/coveo/ui-kit/issues/4101)) ([ecb5e24](https://github.com/coveo/ui-kit/commits/ecb5e2484b80822356dba24130e8058deb1413a6))
+
# 2.55.0 (2024-08-14)
### Bug Fixes
diff --git a/packages/quantic/force-app/main/default/lwc/quanticInsightInterface/quanticInsightInterface.js b/packages/quantic/force-app/main/default/lwc/quanticInsightInterface/quanticInsightInterface.js
index 3cceb57b57c..b7835389f08 100644
--- a/packages/quantic/force-app/main/default/lwc/quanticInsightInterface/quanticInsightInterface.js
+++ b/packages/quantic/force-app/main/default/lwc/quanticInsightInterface/quanticInsightInterface.js
@@ -1,5 +1,6 @@
// @ts-ignore
import getHeadlessConfiguration from '@salesforce/apex/InsightController.getHeadlessConfiguration';
+import LOCALE from '@salesforce/i18n/locale';
import {
getHeadlessBindings,
loadDependencies,
@@ -64,6 +65,9 @@ export default class QuanticInsightInterface extends LightningElement {
configuration: {
...JSON.parse(data),
insightId: this.insightId,
+ search: {
+ locale: LOCALE,
+ },
},
};
setEngineOptions(
diff --git a/packages/quantic/package.json b/packages/quantic/package.json
index 2c060f6619f..8c55a0f1fb0 100644
--- a/packages/quantic/package.json
+++ b/packages/quantic/package.json
@@ -1,6 +1,6 @@
{
"name": "@coveo/quantic",
- "version": "2.55.0",
+ "version": "2.57.0",
"description": "A Salesforce Lightning Web Component (LWC) library for building modern UIs interfacing with the Coveo platform",
"author": "coveo.com",
"homepage": "https://coveo.com",
@@ -46,7 +46,7 @@
},
"dependencies": {
"@coveo/bueno": "0.46.1",
- "@coveo/headless": "2.77.0",
+ "@coveo/headless": "2.79.0",
"dompurify": "3.1.6",
"marked": "12.0.2"
},
diff --git a/packages/samples/angular/package.json b/packages/samples/angular/package.json
index bbf83b992dc..c44f8513299 100644
--- a/packages/samples/angular/package.json
+++ b/packages/samples/angular/package.json
@@ -19,7 +19,7 @@
"@angular/platform-browser": "17.3.12",
"@angular/platform-browser-dynamic": "17.3.12",
"@angular/router": "17.3.12",
- "@coveo/atomic-angular": "2.27.0",
+ "@coveo/atomic-angular": "2.27.2",
"rxjs": "7.8.1",
"tslib": "2.6.3",
"zone.js": "0.14.8"
diff --git a/packages/samples/atomic-next/package.json b/packages/samples/atomic-next/package.json
index 16261c6835b..b90af090034 100644
--- a/packages/samples/atomic-next/package.json
+++ b/packages/samples/atomic-next/package.json
@@ -3,9 +3,9 @@
"version": "0.0.0",
"private": true,
"dependencies": {
- "@coveo/atomic": "2.77.0",
- "@coveo/atomic-react": "2.13.3",
- "@coveo/headless": "2.77.0",
+ "@coveo/atomic": "2.77.2",
+ "@coveo/atomic-react": "2.13.5",
+ "@coveo/headless": "2.79.0",
"next": "14.2.5",
"react": "18.3.1",
"react-dom": "18.3.1"
diff --git a/packages/samples/atomic-react/package.json b/packages/samples/atomic-react/package.json
index 9d66c59f4f2..9c2c3c94fcc 100644
--- a/packages/samples/atomic-react/package.json
+++ b/packages/samples/atomic-react/package.json
@@ -4,9 +4,9 @@
"description": "Samples with atomic-react",
"private": true,
"dependencies": {
- "@coveo/atomic": "2.77.0",
- "@coveo/atomic-react": "2.13.3",
- "@coveo/headless": "2.77.0",
+ "@coveo/atomic": "2.77.2",
+ "@coveo/atomic-react": "2.13.5",
+ "@coveo/headless": "2.79.0",
"react": "18.3.1",
"react-dom": "18.3.1"
},
diff --git a/packages/samples/headless-commerce-react/package.json b/packages/samples/headless-commerce-react/package.json
index bc099295460..973bb929fa5 100644
--- a/packages/samples/headless-commerce-react/package.json
+++ b/packages/samples/headless-commerce-react/package.json
@@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
- "@coveo/headless": "2.77.0",
+ "@coveo/headless": "2.79.0",
"@testing-library/jest-dom": "6.4.8",
"@testing-library/react": "16.0.0",
"@testing-library/user-event": "14.5.2",
diff --git a/packages/samples/headless-react/package.json b/packages/samples/headless-react/package.json
index ec03507bcb2..5f6c85aa425 100644
--- a/packages/samples/headless-react/package.json
+++ b/packages/samples/headless-react/package.json
@@ -5,7 +5,7 @@
"private": true,
"dependencies": {
"@coveo/auth": "1.11.22",
- "@coveo/headless": "2.77.0",
+ "@coveo/headless": "2.79.0",
"@testing-library/jest-dom": "6.4.8",
"@testing-library/react": "14.3.1",
"@testing-library/user-event": "14.5.2",
diff --git a/packages/samples/headless-ssr-commerce/app/_components/did-you-mean.tsx b/packages/samples/headless-ssr-commerce/app/_components/did-you-mean.tsx
new file mode 100644
index 00000000000..f772230fb67
--- /dev/null
+++ b/packages/samples/headless-ssr-commerce/app/_components/did-you-mean.tsx
@@ -0,0 +1 @@
+// TODO // TODO KIT-3463: implement did you mean in sample
diff --git a/packages/samples/headless-ssr-commerce/app/_components/listing-page.tsx b/packages/samples/headless-ssr-commerce/app/_components/listing-page.tsx
index ef9b0fa1d30..03c1492ddfe 100644
--- a/packages/samples/headless-ssr-commerce/app/_components/listing-page.tsx
+++ b/packages/samples/headless-ssr-commerce/app/_components/listing-page.tsx
@@ -7,8 +7,11 @@ import {
ListingHydratedState,
ListingStaticState,
} from '../_lib/commerce-engine';
+import Pagination from './pagination';
import {ProductList} from './product-list';
-import {Summary} from './summary';
+// import ShowMore from './show-more';
+import Sort from './sort';
+import Summary from './summary';
export default function ListingPage({
staticState,
@@ -36,16 +39,28 @@ export default function ListingPage({
return (
<>
- {/* TODO: add UI component here */}
-
+
+
+ {/* The ShowMore and Pagination components showcase two frequent ways to implement pagination. */}
+
+ {/* */}
>
);
}
diff --git a/packages/samples/headless-ssr-commerce/app/_components/pagination.tsx b/packages/samples/headless-ssr-commerce/app/_components/pagination.tsx
new file mode 100644
index 00000000000..c2e25737725
--- /dev/null
+++ b/packages/samples/headless-ssr-commerce/app/_components/pagination.tsx
@@ -0,0 +1,61 @@
+import {
+ Pagination as HeadlessPagination,
+ PaginationState,
+} from '@coveo/headless/ssr-commerce';
+import {useEffect, useState} from 'react';
+
+interface IPaginationProps {
+ staticState: PaginationState;
+ controller?: HeadlessPagination;
+}
+
+export default function Pagination(props: IPaginationProps) {
+ const {staticState, controller} = props;
+
+ const [state, setState] = useState(staticState);
+
+ useEffect(() => {
+ controller?.subscribe(() => setState(controller.state));
+ }, [controller]);
+
+ const renderPageRadioButtons = () => {
+ return Array.from({length: state.totalPages}, (_, i) => {
+ const page = i + 1;
+ return (
+
+ );
+ });
+ };
+
+ return (
+
+
+ Page {state.page + 1} of {state.totalPages}
+
+
+ {renderPageRadioButtons()}
+
+
+ );
+}
diff --git a/packages/samples/headless-ssr-commerce/app/_components/product-list.tsx b/packages/samples/headless-ssr-commerce/app/_components/product-list.tsx
index ef5315963d3..643125e4a49 100644
--- a/packages/samples/headless-ssr-commerce/app/_components/product-list.tsx
+++ b/packages/samples/headless-ssr-commerce/app/_components/product-list.tsx
@@ -1,7 +1,9 @@
import {
+ Product,
ProductList as ProductListingController,
ProductListState,
} from '@coveo/headless/ssr-commerce';
+import {useRouter} from 'next/navigation';
import {useEffect, useState, FunctionComponent} from 'react';
interface ProductListProps {
@@ -15,16 +17,30 @@ export const ProductList: FunctionComponent = ({
}) => {
const [state, setState] = useState(staticState);
+ const router = useRouter();
+
useEffect(
() => controller?.subscribe(() => setState({...controller.state})),
[controller]
);
+ const onProductClick = (product: Product) => {
+ controller?.interactiveProduct({options: {product}}).select();
+ router.push(
+ `/products/${product.ec_product_id}?name=${product.ec_name}&price=${product.ec_price}`
+ );
+ };
+
return (
{state.products.map((product) => (
-
-
{product.ec_name}
+
))}
diff --git a/packages/samples/headless-ssr-commerce/app/_components/product-page.tsx b/packages/samples/headless-ssr-commerce/app/_components/product-page.tsx
new file mode 100644
index 00000000000..f426375ed61
--- /dev/null
+++ b/packages/samples/headless-ssr-commerce/app/_components/product-page.tsx
@@ -0,0 +1,54 @@
+'use client';
+
+import {NavigatorContext} from '@coveo/headless/ssr-commerce';
+import {useSearchParams} from 'next/navigation';
+import {useEffect, useState} from 'react';
+import {
+ searchEngineDefinition,
+ SearchHydratedState,
+ SearchStaticState,
+} from '../_lib/commerce-engine';
+
+interface IProductPageProps {
+ staticState: SearchStaticState;
+ navigatorContext: NavigatorContext;
+ productId: string;
+}
+
+export default function ProductPage(props: IProductPageProps) {
+ const [hydratedState, setHydratedState] = useState<
+ SearchHydratedState | undefined
+ >(undefined);
+
+ const {staticState, navigatorContext, productId} = props;
+
+ const searchParams = useSearchParams();
+
+ const price = Number(searchParams.get('price')) ?? NaN;
+ const name = searchParams.get('name') ?? productId;
+
+ // Setting the navigator context provider also in client-side before hydrating the application
+ searchEngineDefinition.setNavigatorContextProvider(() => navigatorContext);
+
+ useEffect(() => {
+ searchEngineDefinition
+ .hydrateStaticState({
+ searchAction: staticState.searchAction,
+ })
+ .then(({engine, controllers}) => {
+ setHydratedState({engine, controllers});
+ });
+ }, [staticState]);
+
+ const controller = hydratedState?.controllers.productView;
+
+ useEffect(() => {
+ controller?.view({name, productId, price});
+ }, [controller, productId, name, price]);
+
+ return (
+
+ {name} ({productId}) - ${price}
+
+ );
+}
diff --git a/packages/samples/headless-ssr-commerce/app/_components/show-more.tsx b/packages/samples/headless-ssr-commerce/app/_components/show-more.tsx
new file mode 100644
index 00000000000..5ba95e57d8f
--- /dev/null
+++ b/packages/samples/headless-ssr-commerce/app/_components/show-more.tsx
@@ -0,0 +1,58 @@
+import {
+ Pagination as HeadlessPagination,
+ PaginationState,
+ Summary,
+} from '@coveo/headless/ssr-commerce';
+import {useEffect, useState} from 'react';
+
+interface IShowMoreProps {
+ staticState: PaginationState;
+ summaryController?: Summary;
+ controller?: HeadlessPagination;
+}
+
+export default function ShowMore(props: IShowMoreProps) {
+ const {controller, summaryController, staticState} = props;
+
+ const [state, setState] = useState(staticState);
+ const [summaryState, setSummaryState] = useState(
+ props.summaryController?.state
+ );
+
+ useEffect(() => {
+ controller?.subscribe(() => setState(controller.state));
+ }, [controller]);
+
+ useEffect(() => {
+ summaryController?.subscribe(() =>
+ setSummaryState(summaryController.state)
+ );
+ }, [summaryController]);
+
+ const handleFetchMore = () => {
+ controller?.fetchMoreProducts();
+ };
+
+ const isDisabled = () => {
+ return (
+ !controller ||
+ summaryState?.lastProduct === summaryState?.totalNumberOfProducts
+ );
+ };
+
+ return (
+ <>
+
+ Displaying {summaryState?.lastProduct ?? state.pageSize} out of{' '}
+ {state.totalEntries} products
+
+
+ >
+ );
+}
diff --git a/packages/samples/headless-ssr-commerce/app/_components/sort.tsx b/packages/samples/headless-ssr-commerce/app/_components/sort.tsx
new file mode 100644
index 00000000000..c0f0d661adb
--- /dev/null
+++ b/packages/samples/headless-ssr-commerce/app/_components/sort.tsx
@@ -0,0 +1,58 @@
+import {SortState} from '@coveo/headless/commerce';
+import {
+ Sort as HeadlessSort,
+ SortBy,
+ SortCriterion,
+} from '@coveo/headless/commerce';
+import {useEffect, useState} from 'react';
+
+interface ISortProps {
+ controller?: HeadlessSort;
+ staticState: SortState;
+}
+
+export default function Sort(props: ISortProps) {
+ const {controller, staticState} = props;
+
+ const [state, setState] = useState({...staticState});
+
+ useEffect(() => {
+ controller?.subscribe(() => setState(controller.state));
+ }, [controller]);
+
+ if (state.availableSorts.length === 0) {
+ return null;
+ }
+
+ const getSortLabel = (criterion: SortCriterion) => {
+ switch (criterion.by) {
+ case SortBy.Relevance:
+ return 'Relevance';
+ case SortBy.Fields:
+ return criterion.fields.map((field) => field.displayName).join(', ');
+ }
+ };
+
+ return (
+
+
+
+
+ );
+}
diff --git a/packages/samples/headless-ssr-commerce/app/_components/summary.tsx b/packages/samples/headless-ssr-commerce/app/_components/summary.tsx
index 8be36db7fa7..f267bca3300 100644
--- a/packages/samples/headless-ssr-commerce/app/_components/summary.tsx
+++ b/packages/samples/headless-ssr-commerce/app/_components/summary.tsx
@@ -1,48 +1,58 @@
import {
+ Summary as HeadlessSummary,
ProductListingSummaryState,
- Summary as SummaryController,
+ SearchSummaryState,
+ RecommendationsSummaryState,
} from '@coveo/headless/ssr-commerce';
-import {useEffect, useState, FunctionComponent} from 'react';
-import {ListingHydratedState} from '../_lib/commerce-engine';
+import {useEffect, useState} from 'react';
-interface SummaryProps {
- hydratedState?: ListingHydratedState;
- staticState: ProductListingSummaryState;
- controller?: SummaryController;
+interface ISummaryProps {
+ controller?: HeadlessSummary;
+ staticState:
+ | ProductListingSummaryState
+ | SearchSummaryState
+ | RecommendationsSummaryState;
}
-export const Summary: FunctionComponent = ({
- staticState,
- hydratedState,
- controller,
-}: SummaryProps) => {
+export default function Summary(props: ISummaryProps) {
+ const {controller, staticState} = props;
+
const [state, setState] = useState(staticState);
- useEffect(
- () => controller?.subscribe?.(() => setState({...controller.state})),
- [controller]
- );
-
- return (
- <>
-
- Hydrated:{' '}
-
-
-
- Rendered page with {state.totalNumberOfProducts} results
+ useEffect(() => {
+ controller?.subscribe(() => setState(controller.state));
+ }, [controller]);
+
+ const renderBaseSummary = () => {
+ const {firstProduct, lastProduct, totalNumberOfProducts} = state;
+ return (
+
+ Showing results {firstProduct} - {lastProduct} of{' '}
+ {totalNumberOfProducts}
+
+ );
+ };
+
+ const renderQuerySummary = () => {
+ if (!('query' in state)) {
+ return null;
+ }
+
+ return (
+
+ for {state.query}
-
- Rendered on{' '}
-
- {new Date().toISOString()}
-
-
- >
- );
-};
+ );
+ };
+
+ const renderSummary = () => {
+ return (
+
+ {renderBaseSummary()}
+ {renderQuerySummary()}
+
+ );
+ };
+
+ return {renderSummary()}
;
+}
diff --git a/packages/samples/headless-ssr-commerce/app/_lib/commerce-engine-config.ts b/packages/samples/headless-ssr-commerce/app/_lib/commerce-engine-config.ts
index f587b62afd4..f0c865a76e5 100644
--- a/packages/samples/headless-ssr-commerce/app/_lib/commerce-engine-config.ts
+++ b/packages/samples/headless-ssr-commerce/app/_lib/commerce-engine-config.ts
@@ -4,8 +4,12 @@ import {
CommerceEngineDefinitionOptions,
CommerceEngine,
defineProductList,
+ defineSummary,
+ definePagination,
+ defineSort,
+ defineProductView,
getSampleCommerceEngineConfiguration,
- defineQuerySummary,
+ defineDidYouMean, //defineParameterManager,
} from '@coveo/headless/ssr-commerce';
type CommerceEngineConfig = CommerceEngineDefinitionOptions<
@@ -15,9 +19,22 @@ type CommerceEngineConfig = CommerceEngineDefinitionOptions<
export default {
configuration: {
...getSampleCommerceEngineConfiguration(),
+ context: {
+ language: 'en',
+ country: 'US',
+ currency: 'USD',
+ view: {
+ url: 'https://sports.barca.group/browse/promotions/ui-kit-testing',
+ },
+ },
},
controllers: {
- summary: defineQuerySummary(),
+ summary: defineSummary(),
productList: defineProductList(),
+ pagination: definePagination({options: {pageSize: 9}}),
+ sort: defineSort(),
+ productView: defineProductView(),
+ didYouMean: defineDidYouMean(), // TODO KIT-3463: implement did you mean in sample
+ //parameterManager: defineParameterManager(), // TODO KIT-3462: implement parameter manager in sample
},
} satisfies CommerceEngineConfig;
diff --git a/packages/samples/headless-ssr-commerce/app/_lib/commerce-engine.ts b/packages/samples/headless-ssr-commerce/app/_lib/commerce-engine.ts
index 1abf5d8f9b0..e4519f5e1ba 100644
--- a/packages/samples/headless-ssr-commerce/app/_lib/commerce-engine.ts
+++ b/packages/samples/headless-ssr-commerce/app/_lib/commerce-engine.ts
@@ -11,15 +11,13 @@ export const {listingEngineDefinition, searchEngineDefinition} =
engineDefinition;
export type ListingStaticState = InferStaticState<
- typeof searchEngineDefinition
+ typeof listingEngineDefinition
>;
export type ListingHydratedState = InferHydratedState<
- typeof searchEngineDefinition
->;
-
-export type SearchStaticState = InferStaticState<
typeof listingEngineDefinition
>;
+
+export type SearchStaticState = InferStaticState;
export type SearchHydratedState = InferHydratedState<
- typeof listingEngineDefinition
+ typeof searchEngineDefinition
>;
diff --git a/packages/samples/headless-ssr-commerce/app/products/[productId]/page.tsx b/packages/samples/headless-ssr-commerce/app/products/[productId]/page.tsx
new file mode 100644
index 00000000000..dade8bc6550
--- /dev/null
+++ b/packages/samples/headless-ssr-commerce/app/products/[productId]/page.tsx
@@ -0,0 +1,32 @@
+import ProductPage from '@/app/_components/product-page';
+import {searchEngineDefinition} from '@/app/_lib/commerce-engine';
+import {NextJsNavigatorContext} from '@/app/_lib/navigatorContextProvider';
+import {headers} from 'next/headers';
+import {Suspense} from 'react';
+
+export default async function ProductDescriptionPage({
+ params,
+}: {
+ params: {productId: string};
+}) {
+ // Sets the navigator context provider to use the newly created `navigatorContext` before fetching the app static state
+ const navigatorContext = new NextJsNavigatorContext(headers());
+ searchEngineDefinition.setNavigatorContextProvider(() => navigatorContext);
+
+ // Fetches the static state of the app with initial state (when applicable)
+ const staticState = await searchEngineDefinition.fetchStaticState();
+ return (
+ <>
+ Product description page
+ Loading...
}>
+
+
+ >
+ );
+}
+
+export const dynamic = 'force-dynamic';
diff --git a/packages/samples/headless-ssr-commerce/package.json b/packages/samples/headless-ssr-commerce/package.json
index 9955e076938..48843bcd66f 100644
--- a/packages/samples/headless-ssr-commerce/package.json
+++ b/packages/samples/headless-ssr-commerce/package.json
@@ -11,7 +11,7 @@
"build:next": "next build"
},
"dependencies": {
- "@coveo/headless": "2.77.0",
+ "@coveo/headless": "2.79.0",
"next": "14.2.5",
"react": "^18.2.0",
"react-dom": "^18.2.0"
diff --git a/packages/samples/headless-ssr/package.json b/packages/samples/headless-ssr/package.json
index 0374aa632d6..ccdd8b90289 100644
--- a/packages/samples/headless-ssr/package.json
+++ b/packages/samples/headless-ssr/package.json
@@ -8,8 +8,8 @@
"e2e:watch": "cypress open --browser chrome --e2e"
},
"dependencies": {
- "@coveo/headless-react": "1.1.1",
- "@coveo/headless": "2.77.0",
+ "@coveo/headless-react": "1.1.3",
+ "@coveo/headless": "2.79.0",
"next": "14.2.5",
"react": "^18.2.0",
"react-dom": "^18.2.0"
diff --git a/packages/samples/iife/package.json b/packages/samples/iife/package.json
index f9d8b1255b9..e4d6dc0141a 100644
--- a/packages/samples/iife/package.json
+++ b/packages/samples/iife/package.json
@@ -12,10 +12,10 @@
},
"dependencies": {
"@babel/standalone": "7.25.0",
- "@coveo/atomic": "2.77.0",
- "@coveo/atomic-hosted-page": "0.6.4",
- "@coveo/atomic-react": "2.13.3",
- "@coveo/headless": "2.77.0",
+ "@coveo/atomic": "2.77.2",
+ "@coveo/atomic-hosted-page": "0.6.6",
+ "@coveo/atomic-react": "2.13.5",
+ "@coveo/headless": "2.79.0",
"react": "18.3.1",
"react-dom": "18.3.1"
},
diff --git a/packages/samples/stencil/package.json b/packages/samples/stencil/package.json
index 3c18baf7f16..3c1be41598c 100644
--- a/packages/samples/stencil/package.json
+++ b/packages/samples/stencil/package.json
@@ -8,8 +8,8 @@
"e2e:watch": "cypress open --browser chrome --e2e"
},
"dependencies": {
- "@coveo/atomic": "2.77.0",
- "@coveo/headless": "2.77.0",
+ "@coveo/atomic": "2.77.2",
+ "@coveo/headless": "2.79.0",
"@stencil/core": "4.20.0",
"stencil-router-v2": "0.6.0"
},
diff --git a/packages/samples/vuejs/package.json b/packages/samples/vuejs/package.json
index c0089ac455c..1a281c0c643 100644
--- a/packages/samples/vuejs/package.json
+++ b/packages/samples/vuejs/package.json
@@ -13,7 +13,7 @@
},
"dependencies": {
"vue": "^3.4.15",
- "@coveo/atomic": "2.77.0"
+ "@coveo/atomic": "2.77.2"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.3",