diff --git a/packages/atomic/src/components/insight/atomic-insight-generated-answer/atomic-insight-generated-answer.tsx b/packages/atomic/src/components/insight/atomic-insight-generated-answer/atomic-insight-generated-answer.tsx
index 82dc15be2f6..7b05385f2f6 100644
--- a/packages/atomic/src/components/insight/atomic-insight-generated-answer/atomic-insight-generated-answer.tsx
+++ b/packages/atomic/src/components/insight/atomic-insight-generated-answer/atomic-insight-generated-answer.tsx
@@ -68,6 +68,10 @@ export class AtomicInsightGeneratedAnswer
public searchStatus!: InsightSearchStatus;
private resizeObserver?: ResizeObserver;
+ private readonly DEFAULT_COLLAPSED_HEIGHT = 16;
+ private readonly MAX_COLLAPSED_HEIGHT = 32;
+ private readonly MIN_COLLAPSED_HEIGHT = 9;
+
@BindStateToController('generatedAnswer', {
onUpdateCallbackMethod: 'onGeneratedAnswerStateUpdate',
})
@@ -101,6 +105,12 @@ export class AtomicInsightGeneratedAnswer
*/
@Prop() collapsible?: boolean;
+ /**
+ * The maximum height (in rem units) of the answer when collapsed.
+ *
+ */
+ @Prop() maxCollapsedHeight = this.DEFAULT_COLLAPSED_HEIGHT;
+
/**
* @internal
* The unique identifier of the answer configuration to use to generate the answer.
@@ -112,7 +122,6 @@ export class AtomicInsightGeneratedAnswer
private generatedAnswerCommon!: GeneratedAnswerCommon;
private fullAnswerHeight?: number;
- private maxCollapsedHeight = 250;
public initialize() {
this.generatedAnswerCommon = new GeneratedAnswerCommon({
@@ -213,10 +222,19 @@ export class AtomicInsightGeneratedAnswer
}
private adaptAnswerHeight() {
- this.fullAnswerHeight = this.host?.shadowRoot
+ const answerHeight = this.host?.shadowRoot
?.querySelector('[part="generated-text"]')
?.getBoundingClientRect().height;
- this.updateAnswerHeight();
+
+ if (answerHeight) {
+ const rootFontSize = parseFloat(
+ getComputedStyle(document.documentElement).fontSize
+ );
+
+ this.fullAnswerHeight = answerHeight / rootFontSize;
+
+ this.updateAnswerHeight();
+ }
}
private getAnswerContainer() {
@@ -229,15 +247,38 @@ export class AtomicInsightGeneratedAnswer
);
}
+ private validateMaxCollapsedHeight(): number {
+ const isValid =
+ this.maxCollapsedHeight >= this.MIN_COLLAPSED_HEIGHT &&
+ this.maxCollapsedHeight <= this.MAX_COLLAPSED_HEIGHT;
+
+ if (!isValid) {
+ console.warn(
+ `max-collapsed-height (${this.maxCollapsedHeight}rem) is out of the valid range (${this.MIN_COLLAPSED_HEIGHT}rem - ${this.MAX_COLLAPSED_HEIGHT}rem). Falling back to default value (${this.DEFAULT_COLLAPSED_HEIGHT}rem).`
+ );
+ }
+
+ return isValid ? this.maxCollapsedHeight : this.DEFAULT_COLLAPSED_HEIGHT;
+ }
+
+ private setCSSVariable(variableName: string, value: string) {
+ const container = this.getAnswerContainer();
+ if (container) {
+ (container as HTMLElement).style.setProperty(variableName, value);
+ }
+ }
+
private updateAnswerHeight() {
const container = this.getAnswerContainer();
const footer = this.getAnswerFooter();
+ const maxHeight = this.validateMaxCollapsedHeight();
if (!container || !footer) {
return;
}
- if (this.fullAnswerHeight! > this.maxCollapsedHeight) {
+ if (this.fullAnswerHeight! > maxHeight) {
+ this.setCSSVariable('--atomic-crga-collapsed-height', `${maxHeight}rem`);
this.toggleClass(
container,
'answer-collapsed',
diff --git a/packages/atomic/src/components/search/atomic-facet-manager/atomic-facet-manager.tsx b/packages/atomic/src/components/search/atomic-facet-manager/atomic-facet-manager.tsx
index 347205361da..057d9a2a9f5 100644
--- a/packages/atomic/src/components/search/atomic-facet-manager/atomic-facet-manager.tsx
+++ b/packages/atomic/src/components/search/atomic-facet-manager/atomic-facet-manager.tsx
@@ -105,7 +105,7 @@ export class AtomicFacetManager implements InitializableComponent {
}
disconnectedCallback() {
- this.bindings.i18n.off('languageChanged', this.sortFacets);
+ this.bindings?.i18n.off('languageChanged', this.sortFacets);
}
public render() {
diff --git a/packages/atomic/src/components/search/atomic-generated-answer/atomic-generated-answer.tsx b/packages/atomic/src/components/search/atomic-generated-answer/atomic-generated-answer.tsx
index a659237a4b7..0f78fa09203 100644
--- a/packages/atomic/src/components/search/atomic-generated-answer/atomic-generated-answer.tsx
+++ b/packages/atomic/src/components/search/atomic-generated-answer/atomic-generated-answer.tsx
@@ -71,6 +71,10 @@ export class AtomicGeneratedAnswer implements InitializableComponent {
public searchStatus!: SearchStatus;
private resizeObserver?: ResizeObserver;
+ private readonly DEFAULT_COLLAPSED_HEIGHT = 16;
+ private readonly MAX_COLLAPSED_HEIGHT = 32;
+ private readonly MIN_COLLAPSED_HEIGHT = 9;
+
@BindStateToController('generatedAnswer', {
onUpdateCallbackMethod: 'onGeneratedAnswerStateUpdate',
})
@@ -110,6 +114,12 @@ export class AtomicGeneratedAnswer implements InitializableComponent {
*/
@Prop() collapsible?: boolean;
+ /**
+ * The maximum height (in rem units) of the answer when collapsed.
+ *
+ */
+ @Prop() maxCollapsedHeight = this.DEFAULT_COLLAPSED_HEIGHT;
+
/**
* @internal
* The unique identifier of the answer configuration to use to generate the answer.
@@ -147,7 +157,6 @@ export class AtomicGeneratedAnswer implements InitializableComponent {
private generatedAnswerCommon!: GeneratedAnswerCommon;
private fullAnswerHeight?: number;
- private maxCollapsedHeight = 250;
public initialize() {
if (
@@ -257,10 +266,19 @@ export class AtomicGeneratedAnswer implements InitializableComponent {
}
private adaptAnswerHeight() {
- this.fullAnswerHeight = this.host?.shadowRoot
+ const answerHeight = this.host?.shadowRoot
?.querySelector('[part="generated-text"]')
?.getBoundingClientRect().height;
- this.updateAnswerHeight();
+
+ if (answerHeight) {
+ const rootFontSize = parseFloat(
+ getComputedStyle(document.documentElement).fontSize
+ );
+
+ this.fullAnswerHeight = answerHeight / rootFontSize;
+
+ this.updateAnswerHeight();
+ }
}
private getAnswerContainer() {
@@ -273,15 +291,38 @@ export class AtomicGeneratedAnswer implements InitializableComponent {
);
}
- private updateAnswerHeight() {
+ private validateMaxCollapsedHeight(): number {
+ const isValid =
+ this.maxCollapsedHeight >= this.MIN_COLLAPSED_HEIGHT &&
+ this.maxCollapsedHeight <= this.MAX_COLLAPSED_HEIGHT;
+
+ if (!isValid) {
+ console.warn(
+ `max-collapsed-height (${this.maxCollapsedHeight}rem) is out of the valid range (${this.MIN_COLLAPSED_HEIGHT}rem - ${this.MAX_COLLAPSED_HEIGHT}rem). Falling back to default value (${this.DEFAULT_COLLAPSED_HEIGHT}rem).`
+ );
+ }
+
+ return isValid ? this.maxCollapsedHeight : this.DEFAULT_COLLAPSED_HEIGHT;
+ }
+
+ private setCSSVariable(variableName: string, value: string) {
const container = this.getAnswerContainer();
+ if (container) {
+ (container as HTMLElement).style.setProperty(variableName, value);
+ }
+ }
+
+ private updateAnswerHeight() {
+ const container = this.getAnswerContainer() as HTMLElement;
const footer = this.getAnswerFooter();
+ const maxHeight = this.validateMaxCollapsedHeight();
if (!container || !footer) {
return;
}
- if (this.fullAnswerHeight! > this.maxCollapsedHeight) {
+ if (this.fullAnswerHeight! > maxHeight) {
+ this.setCSSVariable('--atomic-crga-collapsed-height', `${maxHeight}rem`);
this.toggleClass(
container,
'answer-collapsed',
diff --git a/packages/atomic/src/components/search/tabs/atomic-tab-manager/atomic-tab-manager.tsx b/packages/atomic/src/components/search/tabs/atomic-tab-manager/atomic-tab-manager.tsx
index 3f3e5596fad..41da1693187 100644
--- a/packages/atomic/src/components/search/tabs/atomic-tab-manager/atomic-tab-manager.tsx
+++ b/packages/atomic/src/components/search/tabs/atomic-tab-manager/atomic-tab-manager.tsx
@@ -13,7 +13,9 @@ import {
import {Bindings} from '../../atomic-search-interface/atomic-search-interface';
/**
- * @alpha
+ * The `atomic-tab-manager` component manages a collection of tabs,
+ * allowing users to switch between them. Each child `atomic-tab` represents an
+ * individual tab within the manager.
*
* @part button-container - The container for the tab button.
* @part button - The tab button.
diff --git a/packages/atomic/src/components/search/tabs/atomic-tab/atomic-tab.tsx b/packages/atomic/src/components/search/tabs/atomic-tab/atomic-tab.tsx
index a6755d17e1d..8d3d95e61f3 100644
--- a/packages/atomic/src/components/search/tabs/atomic-tab/atomic-tab.tsx
+++ b/packages/atomic/src/components/search/tabs/atomic-tab/atomic-tab.tsx
@@ -1,7 +1,8 @@
import {Component, Prop, Element} from '@stencil/core';
/**
- * @internal
+ * The `atomic-tab` component represents an individual tab within the `atomic-tab-manager` component.
+ * It must be used as a child of the `atomic-tab-manager` component to function correctly.
*/
@Component({
tag: 'atomic-tab',
diff --git a/packages/atomic/stencil.config.ts b/packages/atomic/stencil.config.ts
index 20f5bdbb8c1..43eb88fd0a7 100644
--- a/packages/atomic/stencil.config.ts
+++ b/packages/atomic/stencil.config.ts
@@ -159,7 +159,7 @@ export const config: Config = {
copy: [
{src: 'themes'},
{
- src: '../node_modules/@salesforce-ux/design-system/assets/icons/{doctype,standard}/*.svg',
+ src: '../../../node_modules/@salesforce-ux/design-system/assets/icons/{doctype,standard}/*.svg',
dest: 'assets',
},
],
@@ -178,7 +178,7 @@ export const config: Config = {
? {src: 'external-builds', dest: 'build/headless'}
: {src: ''},
{
- src: '../node_modules/@salesforce-ux/design-system/assets/icons/{doctype,standard}/*.svg',
+ src: '../../../node_modules/@salesforce-ux/design-system/assets/icons/{doctype,standard}/*.svg',
dest: 'build/assets',
},
].filter((n) => n.src),
diff --git a/packages/auth/CHANGELOG.md b/packages/auth/CHANGELOG.md
index 9abf110e975..0df09167321 100644
--- a/packages/auth/CHANGELOG.md
+++ b/packages/auth/CHANGELOG.md
@@ -1,3 +1,8 @@
+##
2.0.6 (2025-01-08)
+
+- fix(deps): update all dependencies j:kit-282 (#4753) ([8a2cae4](https://github.com/coveo/ui-kit/commits/8a2cae4)), closes [#4753](https://github.com/coveo/ui-kit/issues/4753)
+- fix(deps): update all dependencies j:kit-282 (major) (#4754) ([0a3e0d5](https://github.com/coveo/ui-kit/commits/0a3e0d5)), closes [#4754](https://github.com/coveo/ui-kit/issues/4754)
+
##
2.0.5 (2024-12-04)
- chore(deps): update all dependencies j:kit-282 (#4705) ([34921b9](https://github.com/coveo/ui-kit/commits/34921b9)), closes [#4705](https://github.com/coveo/ui-kit/issues/4705) [#8203](https://github.com/coveo/ui-kit/issues/8203)
diff --git a/packages/auth/package.json b/packages/auth/package.json
index c8163a57a1b..9e9b3fe051f 100644
--- a/packages/auth/package.json
+++ b/packages/auth/package.json
@@ -1,6 +1,6 @@
{
"name": "@coveo/auth",
- "version": "2.0.5",
+ "version": "2.0.6",
"description": "Functions to help authenticate with the Coveo platform.",
"main": "./dist/auth.js",
"module": "./dist/auth.esm.js",
diff --git a/packages/headless-react/CHANGELOG.md b/packages/headless-react/CHANGELOG.md
index b05e7bb1284..38170cc3953 100644
--- a/packages/headless-react/CHANGELOG.md
+++ b/packages/headless-react/CHANGELOG.md
@@ -1,3 +1,15 @@
+## 2.4.0 (2025-01-08)
+
+- chore(deps): update dependency eslint-plugin-react to v7.37.3 j:kit-282 (#4819) ([b51ea24](https://github.com/coveo/ui-kit/commits/b51ea24)), closes [#4819](https://github.com/coveo/ui-kit/issues/4819)
+- fix(deps): update all dependencies j:kit-282 (major) (#4754) ([0a3e0d5](https://github.com/coveo/ui-kit/commits/0a3e0d5)), closes [#4754](https://github.com/coveo/ui-kit/issues/4754)
+- fix(deps): update typescript j:kit-282 (#4721) ([4ee6eba](https://github.com/coveo/ui-kit/commits/4ee6eba)), closes [#4721](https://github.com/coveo/ui-kit/issues/4721)
+- fix(headless commerce SSR): fix react provider hydration logic (#4792) ([69efd25](https://github.com/coveo/ui-kit/commits/69efd25)), closes [#4792](https://github.com/coveo/ui-kit/issues/4792)
+- fix(headless-react): fix error when using keys other than their name for WithProps controllers (#478 ([56a5d92](https://github.com/coveo/ui-kit/commits/56a5d92)), closes [#4783](https://github.com/coveo/ui-kit/issues/4783)
+- fix(headless-ssr-commerce): fix client side recommendation refresh with a productId (#4793) ([454178b](https://github.com/coveo/ui-kit/commits/454178b)), closes [#4793](https://github.com/coveo/ui-kit/issues/4793)
+- docs(headless commerce SSR): add parameter manager in SSR + nextjs sample (#4770) ([208e054](https://github.com/coveo/ui-kit/commits/208e054)), closes [#4770](https://github.com/coveo/ui-kit/issues/4770)
+- docs(headless-react): create typedoc site & publish it as an artifact (#4747) ([3f2b6fa](https://github.com/coveo/ui-kit/commits/3f2b6fa)), closes [#4747](https://github.com/coveo/ui-kit/issues/4747)
+- feat(headless SSR): improved error messaging for hook misuse (#4771) ([915daa1](https://github.com/coveo/ui-kit/commits/915daa1)), closes [#4771](https://github.com/coveo/ui-kit/issues/4771)
+
## 2.3.0 (2024-12-11)
- feat(headless SSR): fetchStaticState should only accept solutiontype-specific controllers (#4769) ([f961cb2](https://github.com/coveo/ui-kit/commits/f961cb2)), closes [#4769](https://github.com/coveo/ui-kit/issues/4769)
diff --git a/packages/headless-react/package.json b/packages/headless-react/package.json
index ee74fc0e4bd..513a703b47a 100644
--- a/packages/headless-react/package.json
+++ b/packages/headless-react/package.json
@@ -1,6 +1,6 @@
{
"name": "@coveo/headless-react",
- "version": "2.3.0",
+ "version": "2.4.0",
"description": "React utilities for SSR (Server Side Rendering) with headless",
"homepage": "https://docs.coveo.com/en/headless/latest/",
"repository": {
@@ -40,7 +40,7 @@
"build:typedoc:merge": "typedoc --tsconfig tsconfig.typedoc.json"
},
"dependencies": {
- "@coveo/headless": "3.12.0"
+ "@coveo/headless": "3.13.0"
},
"devDependencies": {
"@coveo/release": "1.0.0",
diff --git a/packages/headless/CHANGELOG.md b/packages/headless/CHANGELOG.md
index 52c410842d2..ff77c002bbf 100644
--- a/packages/headless/CHANGELOG.md
+++ b/packages/headless/CHANGELOG.md
@@ -1,3 +1,23 @@
+## 3.13.0 (2025-01-08)
+
+- fix(deps): update all dependencies j:kit-282 (#4753) ([8a2cae4](https://github.com/coveo/ui-kit/commits/8a2cae4)), closes [#4753](https://github.com/coveo/ui-kit/issues/4753)
+- fix(deps): update all dependencies j:kit-282 (#4808) ([c1ad91c](https://github.com/coveo/ui-kit/commits/c1ad91c)), closes [#4808](https://github.com/coveo/ui-kit/issues/4808)
+- fix(deps): update all dependencies j:kit-282 (#4810) ([fb2c7c6](https://github.com/coveo/ui-kit/commits/fb2c7c6)), closes [#4810](https://github.com/coveo/ui-kit/issues/4810)
+- fix(deps): update all dependencies j:kit-282 (major) (#4754) ([0a3e0d5](https://github.com/coveo/ui-kit/commits/0a3e0d5)), closes [#4754](https://github.com/coveo/ui-kit/issues/4754)
+- fix(deps): update all dependencies j:kit-282 (major) (#4813) ([4023170](https://github.com/coveo/ui-kit/commits/4023170)), closes [#4813](https://github.com/coveo/ui-kit/issues/4813)
+- fix(deps): update dependency @coveo/relay to v0.8.1 j:kit-282 (#4809) ([f6516e9](https://github.com/coveo/ui-kit/commits/f6516e9)), closes [#4809](https://github.com/coveo/ui-kit/issues/4809)
+- fix(deps): update dependency @reduxjs/toolkit to v2.5.0 j:kit-282 (#4811) ([ca85011](https://github.com/coveo/ui-kit/commits/ca85011)), closes [#4811](https://github.com/coveo/ui-kit/issues/4811)
+- fix(headless commerce SSR): expose promoteChildToParent method on SSR ProductList type (#4798) ([7d02e19](https://github.com/coveo/ui-kit/commits/7d02e19)), closes [#4798](https://github.com/coveo/ui-kit/issues/4798)
+- fix(headless-react): fix error when using keys other than their name for WithProps controllers (#478 ([56a5d92](https://github.com/coveo/ui-kit/commits/56a5d92)), closes [#4783](https://github.com/coveo/ui-kit/issues/4783)
+- fix(headless-ssr-commerce): fix client side recommendation refresh with a productId (#4793) ([454178b](https://github.com/coveo/ui-kit/commits/454178b)), closes [#4793](https://github.com/coveo/ui-kit/issues/4793)
+- fix(headless): handle end of stream message when answer can't be generated (#4800) ([7eed073](https://github.com/coveo/ui-kit/commits/7eed073)), closes [#4800](https://github.com/coveo/ui-kit/issues/4800)
+- docs(headless commerce SSR): add parameter manager in SSR + nextjs sample (#4770) ([208e054](https://github.com/coveo/ui-kit/commits/208e054)), closes [#4770](https://github.com/coveo/ui-kit/issues/4770)
+- docs(headless-react): create typedoc site & publish it as an artifact (#4747) ([3f2b6fa](https://github.com/coveo/ui-kit/commits/3f2b6fa)), closes [#4747](https://github.com/coveo/ui-kit/issues/4747)
+- test(headless SSR): add unit tests for all solution type factories (#4790) ([84f933b](https://github.com/coveo/ui-kit/commits/84f933b)), closes [#4790](https://github.com/coveo/ui-kit/issues/4790)
+- test(headless SSR): add unit tests for recommendation factories (#4786) ([f65d022](https://github.com/coveo/ui-kit/commits/f65d022)), closes [#4786](https://github.com/coveo/ui-kit/issues/4786)
+- feat(headless commerce): add commerce parameters slice and excludeDefaultParameters controller optio ([e25890d](https://github.com/coveo/ui-kit/commits/e25890d)), closes [#4759](https://github.com/coveo/ui-kit/issues/4759) [/github.com/coveo/ui-kit/pull/4759/files#diff-3418e45cea101e732b2ca21969a80f152654268fa3334c2e3a369f95e2d172a5](https://github.com//github.com/coveo/ui-kit/pull/4759/files/issues/diff-3418e45cea101e732b2ca21969a80f152654268fa3334c2e3a369f95e2d172a5) [/github.com/coveo/ui-kit/pull/4759/files#diff-ec6eb48af4ca0f0d2cac2569ba073a45015b0869fa1a5d9414f43d1472360812](https://github.com//github.com/coveo/ui-kit/pull/4759/files/issues/diff-ec6eb48af4ca0f0d2cac2569ba073a45015b0869fa1a5d9414f43d1472360812)
+- feat(headless SSR): improved error messaging for hook misuse (#4771) ([915daa1](https://github.com/coveo/ui-kit/commits/915daa1)), closes [#4771](https://github.com/coveo/ui-kit/issues/4771)
+
## 3.12.0 (2024-12-11)
- feat(headless SSR): fetch recommendations with a productId (#4768) ([6d53c57](https://github.com/coveo/ui-kit/commits/6d53c57)), closes [#4768](https://github.com/coveo/ui-kit/issues/4768)
diff --git a/packages/headless/package.json b/packages/headless/package.json
index 919a69133c3..1d4c436770f 100644
--- a/packages/headless/package.json
+++ b/packages/headless/package.json
@@ -118,7 +118,7 @@
},
"types": "./dist/definitions/index.d.ts",
"license": "Apache-2.0",
- "version": "3.12.0",
+ "version": "3.13.0",
"files": [
"dist/"
],
@@ -157,7 +157,7 @@
},
"dependencies": {
"@coveo/bueno": "1.0.7",
- "@coveo/relay": "0.8.1",
+ "@coveo/relay": "0.7.10",
"@coveo/relay-event-types": "13.0.0",
"@microsoft/fetch-event-source": "2.0.1",
"@reduxjs/toolkit": "2.5.0",
@@ -180,7 +180,6 @@
"esbuild-plugin-alias-path": "2.0.2",
"eslint-plugin-canonical": "4.18.0",
"execa": "9.5.2",
- "install": "0.13.0",
"ts-node": "10.9.2",
"typedoc": "0.27.6",
"vitest": "2.1.8"
diff --git a/packages/quantic/CHANGELOG.md b/packages/quantic/CHANGELOG.md
index 63075dff32e..2bacea80d2e 100644
--- a/packages/quantic/CHANGELOG.md
+++ b/packages/quantic/CHANGELOG.md
@@ -1,3 +1,13 @@
+##
3.11.1 (2025-01-08)
+
+- fix(deps): update all dependencies j:kit-282 (#4753) ([8a2cae4](https://github.com/coveo/ui-kit/commits/8a2cae4)), closes [#4753](https://github.com/coveo/ui-kit/issues/4753)
+- fix(deps): update all dependencies j:kit-282 (#4808) ([c1ad91c](https://github.com/coveo/ui-kit/commits/c1ad91c)), closes [#4808](https://github.com/coveo/ui-kit/issues/4808)
+- fix(deps): update all dependencies j:kit-282 (major) (#4754) ([0a3e0d5](https://github.com/coveo/ui-kit/commits/0a3e0d5)), closes [#4754](https://github.com/coveo/ui-kit/issues/4754)
+- fix(deps): update all dependencies j:kit-282 (major) (#4813) ([4023170](https://github.com/coveo/ui-kit/commits/4023170)), closes [#4813](https://github.com/coveo/ui-kit/issues/4813)
+- fix(quantic): fixed issue with breadcrumb component when facet ids are used (#4791) ([87491c6](https://github.com/coveo/ui-kit/commits/87491c6)), closes [#4791](https://github.com/coveo/ui-kit/issues/4791)
+- docs(quantic): user actions toggle missing props (#4797) ([6d0c097](https://github.com/coveo/ui-kit/commits/6d0c097)), closes [#4797](https://github.com/coveo/ui-kit/issues/4797)
+- test(quantic): added playwright tests and unit test for quantic generated answer component (#4803) ([99ed76a](https://github.com/coveo/ui-kit/commits/99ed76a)), closes [#4803](https://github.com/coveo/ui-kit/issues/4803)
+
## 3.11.0 (2024-12-11)
- feat(quantic): make quantic notifications component dismissible (#4733) ([96cc465](https://github.com/coveo/ui-kit/commits/96cc465)), closes [#4733](https://github.com/coveo/ui-kit/issues/4733)
diff --git a/packages/quantic/build-static-resources.js b/packages/quantic/build-static-resources.js
index a2f29cc260b..8cbc1d1d490 100644
--- a/packages/quantic/build-static-resources.js
+++ b/packages/quantic/build-static-resources.js
@@ -5,14 +5,17 @@ const path = require('path');
const STATIC_RESOURCES_PATH = './force-app/main/default/staticresources';
const TEMP_DIR = '.tmp/quantic-compiled';
-const NODE_MODULES = '../../node_modules';
+
+function resolveLibraryPath(packageName, relativePath) {
+ return path.resolve(path.dirname(require.resolve(packageName)), relativePath);
+}
const LIBRARY_CONFIG = {
dompurify: {
directories: [`${STATIC_RESOURCES_PATH}/dompurify`],
files: [
{
- src: `${NODE_MODULES}/dompurify/dist/purify.min.js`,
+ src: resolveLibraryPath('dompurify', '../dist/purify.min.js'),
dest: `${STATIC_RESOURCES_PATH}/dompurify/purify.min.js`,
},
],
@@ -21,7 +24,7 @@ const LIBRARY_CONFIG = {
directories: [`${STATIC_RESOURCES_PATH}/marked`],
files: [
{
- src: `${NODE_MODULES}/marked/marked.min.js`,
+ src: resolveLibraryPath('marked', '../marked.min.js'),
dest: `${STATIC_RESOURCES_PATH}/marked/marked.min.js`,
},
],
@@ -33,11 +36,11 @@ const LIBRARY_CONFIG = {
],
files: [
{
- src: `${NODE_MODULES}/@coveo/bueno/dist/browser/bueno.js`,
+ src: resolveLibraryPath('@coveo/bueno', '../dist/browser/bueno.js'),
dest: `${STATIC_RESOURCES_PATH}/coveobueno/browser/bueno.js`,
},
{
- src: `${NODE_MODULES}/@coveo/bueno/dist/definitions`,
+ src: resolveLibraryPath('@coveo/bueno', '../dist/definitions'),
dest: `${STATIC_RESOURCES_PATH}/coveobueno/definitions`,
},
],
@@ -67,7 +70,7 @@ const LIBRARY_CONFIG = {
dest: `${STATIC_RESOURCES_PATH}/coveoheadless/recommendation/headless.js`,
},
{
- src: `${NODE_MODULES}/@coveo/headless/dist/definitions`,
+ src: resolveLibraryPath('@coveo/headless', '../dist/definitions'),
dest: `${STATIC_RESOURCES_PATH}/coveoheadless/definitions`,
},
],
diff --git a/packages/quantic/force-app/main/default/lwc/quanticBreadcrumbManager/__tests__/quanticBreadcrumbManager.test.js b/packages/quantic/force-app/main/default/lwc/quanticBreadcrumbManager/__tests__/quanticBreadcrumbManager.test.js
index 0a17eeee327..f5530407bb4 100644
--- a/packages/quantic/force-app/main/default/lwc/quanticBreadcrumbManager/__tests__/quanticBreadcrumbManager.test.js
+++ b/packages/quantic/force-app/main/default/lwc/quanticBreadcrumbManager/__tests__/quanticBreadcrumbManager.test.js
@@ -30,21 +30,25 @@ const functionsMocks = {
};
const selectors = {
- facetBreadcrumb: '[data-test="facet-breadcrumb"]',
- facetBreadcrumbValue: '[data-test="facet-breadcrumb-value"]',
- categoryFacetBreadcrumb: '[data-test="category-facet-breadcrumb"]',
- categoryFacetBreadcrumbValue: '[data-test="category-facet-breadcrumb-value"]',
- numericFacetBreadcrumb: '[data-test="numeric-facet-breadcrumb"]',
- numericFacetBreadcrumbValue: '[data-test="numeric-facet-breadcrumb-value"]',
- dateFacetBreadcrumb: '[data-test="date-facet-breadcrumb"]',
- dateFacetBreadcrumbValue: '[data-test="date-facet-breadcrumb-value"]',
+ initializationError: 'c-quantic-component-error',
+ breadcrumbShowMoreButton: '[data-testid="breadcrumb-manager__more-button"]',
+ breadcrumbClearAllFiltersButton:
+ '[data-testid="breadcrumb-manager__clear-button"]',
+ facetBreadcrumb: '[data-testid="facet-breadcrumb"]',
+ facetBreadcrumbValue: '[data-testid="facet-breadcrumb-value"]',
+ categoryFacetBreadcrumb: '[data-testid="category-facet-breadcrumb"]',
+ categoryFacetBreadcrumbValue:
+ '[data-testid="category-facet-breadcrumb-value"]',
+ numericFacetBreadcrumb: '[data-testid="numeric-facet-breadcrumb"]',
+ numericFacetBreadcrumbValue: '[data-testid="numeric-facet-breadcrumb-value"]',
+ dateFacetBreadcrumb: '[data-testid="date-facet-breadcrumb"]',
+ dateFacetBreadcrumbValue: '[data-testid="date-facet-breadcrumb-value"]',
};
const exampleEngine = {
id: 'dummy engine',
};
-const exampleFacetId = 'idOne';
-
+const defaultCollapseThreshold = 5;
let isInitialized = false;
function createTestComponent(options = {}) {
@@ -91,6 +95,15 @@ function mockSuccessfulHeadlessInitialization() {
};
}
+function mockErroneousHeadlessInitialization() {
+ // @ts-ignore
+ mockHeadlessLoader.initializeWithHeadless = (element) => {
+ if (element instanceof QuanticBreadcrumbManager) {
+ element.setInitializationError();
+ }
+ };
+}
+
function cleanup() {
// The jsdom instance is shared across test cases in a single file so reset the DOM
while (document.body.firstChild) {
@@ -101,6 +114,47 @@ function cleanup() {
}
describe('c-quantic-breadcrumb-manager', () => {
+ afterEach(() => {
+ breadcrumbManagerState = initialBreadcrumbManagerState;
+ storeState = initialStoreState;
+ cleanup();
+ });
+
+ describe('when an initialization error occurs', () => {
+ beforeEach(() => {
+ mockErroneousHeadlessInitialization();
+ });
+
+ it('should display the initialization error component', async () => {
+ const element = createTestComponent();
+ await flushPromises();
+
+ const errorComponent = element.shadowRoot.querySelector(
+ selectors.initializationError
+ );
+
+ expect(errorComponent).not.toBeNull();
+ });
+ });
+
+ describe('controller initialization', () => {
+ beforeEach(() => {
+ mockSuccessfulHeadlessInitialization();
+ prepareHeadlessState();
+ });
+
+ it('should build the breadcrumb manager and subscribe to state', async () => {
+ createTestComponent();
+ await flushPromises();
+
+ expect(functionsMocks.buildBreadcrumbManager).toHaveBeenCalledTimes(1);
+ expect(functionsMocks.buildBreadcrumbManager).toHaveBeenCalledWith(
+ exampleEngine
+ );
+ expect(functionsMocks.subscribe).toHaveBeenCalledTimes(1);
+ });
+ });
+
beforeEach(() => {
mockSuccessfulHeadlessInitialization();
prepareHeadlessState();
@@ -113,28 +167,33 @@ describe('c-quantic-breadcrumb-manager', () => {
cleanup();
});
- describe('facet breadcrumbs', () => {
+ describe('facet breadcrumbs rendering', () => {
const exampleFacetBreadcrumbs = [
{
field: 'fieldOne',
- facetId: exampleFacetId,
- values: [{value: 'one'}, {value: 'two'}],
+ facetId: 'facetIdOne',
+ values: [{value: {value: 'one'}}, {value: {value: 'two'}}],
+ },
+ {
+ field: 'fieldTwo',
+ facetId: 'facetIdTwo',
+ values: [{value: {value: 'three'}}, {value: {value: 'four'}}],
},
];
- beforeAll(() => {
+ beforeEach(() => {
breadcrumbManagerState = {
...breadcrumbManagerState,
facetBreadcrumbs: exampleFacetBreadcrumbs,
hasBreadcrumbs: true,
};
-
- storeState = {
- [exampleFacetId]: {
- facetId: exampleFacetId,
+ storeState = exampleFacetBreadcrumbs.reduce((acc, breadcrumb) => {
+ acc[breadcrumb.facetId] = {
+ facetId: breadcrumb.facetId,
format: (value) => value,
- },
- };
+ };
+ return acc;
+ }, {});
});
it('should properly display the breadcrumb values', async () => {
@@ -161,32 +220,196 @@ describe('c-quantic-breadcrumb-manager', () => {
);
const values = exampleFacetBreadcrumb.values.map((item) => item.value);
expect(labels).toEqual(values);
+
+ const breadcrumbClearAllFiltersButton =
+ element.shadowRoot.querySelector(
+ selectors.breadcrumbClearAllFiltersButton
+ );
+ expect(breadcrumbClearAllFiltersButton).not.toBeNull();
+ });
+ });
+
+ describe('when the number of selected values for one facet exceeds the collapse threshold', () => {
+ const testManyFacetBreadcrumbs = [
+ {
+ field: 'fieldOne',
+ facetId: 'facetIdOne',
+ values: [
+ {value: {value: 'one'}},
+ {value: {value: 'two'}},
+ {value: {value: 'three'}},
+ {value: {value: 'four'}},
+ {value: {value: 'five'}},
+ {value: {value: 'six'}},
+ ],
+ },
+ ];
+
+ beforeEach(() => {
+ breadcrumbManagerState = {
+ ...breadcrumbManagerState,
+ facetBreadcrumbs: testManyFacetBreadcrumbs,
+ hasBreadcrumbs: true,
+ };
+ storeState = testManyFacetBreadcrumbs.reduce((acc, breadcrumb) => {
+ acc[breadcrumb.facetId] = {
+ facetId: breadcrumb.facetId,
+ format: (value) => value,
+ };
+ return acc;
+ }, {});
+ });
+
+ it('should collapse the values and display a show more button', async () => {
+ const element = createTestComponent();
+ await flushPromises();
+
+ const facetBreadcrumbs = element.shadowRoot.querySelectorAll(
+ selectors.facetBreadcrumb
+ );
+ expect(facetBreadcrumbs.length).toBe(testManyFacetBreadcrumbs.length);
+
+ const firstFacetBreadcrumb = facetBreadcrumbs[0];
+ const facetBreadcrumbValues = Array.from(
+ firstFacetBreadcrumb.querySelectorAll(selectors.facetBreadcrumbValue)
+ );
+ expect(facetBreadcrumbValues.length).toBe(defaultCollapseThreshold);
+ facetBreadcrumbValues.forEach((facetBreadcrumbValue, index) => {
+ const facetValueLabel = facetBreadcrumbValue.label.value;
+ expect(facetValueLabel).toBe(
+ testManyFacetBreadcrumbs[0].values[index].value.value
+ );
+ });
+
+ const breadcrumbShowMoreButton = element.shadowRoot.querySelector(
+ selectors.breadcrumbShowMoreButton
+ );
+ expect(breadcrumbShowMoreButton).not.toBeNull();
+ });
+
+ it('should expand the values when the show more button is clicked', async () => {
+ const element = createTestComponent();
+ await flushPromises();
+
+ const facetBreadcrumbs = element.shadowRoot.querySelectorAll(
+ selectors.facetBreadcrumb
+ );
+ const firstFacetBreadcrumb = facetBreadcrumbs[0];
+
+ const facetBreadcrumbValues = Array.from(
+ firstFacetBreadcrumb.querySelectorAll(selectors.facetBreadcrumbValue)
+ );
+ expect(facetBreadcrumbValues.length).toBe(defaultCollapseThreshold);
+
+ const breadcrumbShowMoreButton = element.shadowRoot.querySelector(
+ selectors.breadcrumbShowMoreButton
+ );
+ expect(breadcrumbShowMoreButton).not.toBeNull();
+
+ breadcrumbShowMoreButton.click();
+ await flushPromises();
+
+ const facetBreadcrumbValuesAfterClick = Array.from(
+ firstFacetBreadcrumb.querySelectorAll(selectors.facetBreadcrumbValue)
+ );
+ expect(facetBreadcrumbValuesAfterClick.length).toBe(
+ testManyFacetBreadcrumbs[0].values.length
+ );
+ const breadcrumbShowMoreButtonAfterClick =
+ element.shadowRoot.querySelector(selectors.breadcrumbShowMoreButton);
+ expect(breadcrumbShowMoreButtonAfterClick).toBeNull();
+ });
+ });
+
+ describe('with a custom collapse threshold', () => {
+ const customCollapseThreshold = 3;
+ const testManyFacetBreadcrumbs = [
+ {
+ field: 'fieldOne',
+ facetId: 'facetIdOne',
+ values: [
+ {value: {value: 'one'}},
+ {value: {value: 'two'}},
+ {value: {value: 'three'}},
+ {value: {value: 'four'}},
+ {value: {value: 'five'}},
+ {value: {value: 'six'}},
+ ],
+ },
+ ];
+
+ beforeEach(() => {
+ breadcrumbManagerState = {
+ ...breadcrumbManagerState,
+ facetBreadcrumbs: testManyFacetBreadcrumbs,
+ hasBreadcrumbs: true,
+ };
+ storeState = testManyFacetBreadcrumbs.reduce((acc, breadcrumb) => {
+ acc[breadcrumb.facetId] = {
+ facetId: breadcrumb.facetId,
+ format: (value) => value,
+ };
+ return acc;
+ }, {});
+ });
+ it('should collapse the values over the custom collapse threshold and display a show more button', async () => {
+ const element = createTestComponent({
+ collapseThreshold: customCollapseThreshold,
+ });
+ await flushPromises();
+
+ const facetBreadcrumbs = element.shadowRoot.querySelectorAll(
+ selectors.facetBreadcrumb
+ );
+ expect(facetBreadcrumbs.length).toBe(testManyFacetBreadcrumbs.length);
+
+ const firstFacetBreadcrumb = facetBreadcrumbs[0];
+ const facetBreadcrumbValues = Array.from(
+ firstFacetBreadcrumb.querySelectorAll(selectors.facetBreadcrumbValue)
+ );
+ expect(facetBreadcrumbValues.length).toBe(customCollapseThreshold);
+ facetBreadcrumbValues.forEach((facetBreadcrumbValue, index) => {
+ const facetValueLabel = facetBreadcrumbValue.label.value;
+ expect(facetValueLabel).toBe(
+ testManyFacetBreadcrumbs[0].values[index].value.value
+ );
+ });
+
+ const breadcrumbShowMoreButton = element.shadowRoot.querySelector(
+ selectors.breadcrumbShowMoreButton
+ );
+ expect(breadcrumbShowMoreButton).not.toBeNull();
});
});
});
describe('category facet breadcrumbs', () => {
- const exampleCategoryFacetBreadcrumbs = [
+ const testCategoryFacetBreadcrumbs = [
{
- field: exampleFacetId,
- facetId: exampleFacetId,
+ field: 'fieldOne',
+ facetId: 'facetIdOne',
path: [{value: 'one'}, {value: 'two'}],
},
+ {
+ field: 'fieldTwo',
+ facetId: 'facetIdTwo',
+ path: [{value: 'three'}, {value: 'four'}],
+ },
];
- beforeAll(() => {
+ beforeEach(() => {
breadcrumbManagerState = {
...breadcrumbManagerState,
- categoryFacetBreadcrumbs: exampleCategoryFacetBreadcrumbs,
+ categoryFacetBreadcrumbs: testCategoryFacetBreadcrumbs,
hasBreadcrumbs: true,
};
-
- storeState = {
- [exampleFacetId]: {
- facetId: exampleFacetId,
+ storeState = testCategoryFacetBreadcrumbs.reduce((acc, breadcrumb) => {
+ acc[breadcrumb.facetId] = {
+ facetId: breadcrumb.facetId,
format: (item) => item.value,
- },
- };
+ };
+ return acc;
+ }, {});
});
it('should properly display the breadcrumb values', async () => {
@@ -198,10 +421,10 @@ describe('c-quantic-breadcrumb-manager', () => {
);
expect(categoryFacetBreadcrumbs.length).toBe(
- exampleCategoryFacetBreadcrumbs.length
+ testCategoryFacetBreadcrumbs.length
);
categoryFacetBreadcrumbs.forEach((categoryFacetBreadcrumb, index) => {
- const exampleFacetBreadcrumb = exampleCategoryFacetBreadcrumbs[index];
+ const exampleFacetBreadcrumb = testCategoryFacetBreadcrumbs[index];
const categoryFacetBreadcrumbValues = Array.from(
categoryFacetBreadcrumb.querySelectorAll(
selectors.categoryFacetBreadcrumbValue
@@ -217,30 +440,70 @@ describe('c-quantic-breadcrumb-manager', () => {
expect(labels).toEqual([values.join(' / ')]);
});
});
+
+ describe('with a custom category divider', () => {
+ it('should display the breadcrumb values with the custom divider', async () => {
+ const customCategoryDivider = '*';
+ const element = createTestComponent({
+ categoryDivider: customCategoryDivider,
+ });
+ await flushPromises();
+
+ const categoryFacetBreadcrumbs = element.shadowRoot.querySelectorAll(
+ selectors.categoryFacetBreadcrumb
+ );
+
+ expect(categoryFacetBreadcrumbs.length).toBe(
+ testCategoryFacetBreadcrumbs.length
+ );
+
+ categoryFacetBreadcrumbs.forEach((categoryFacetBreadcrumb, index) => {
+ const exampleFacetBreadcrumb = testCategoryFacetBreadcrumbs[index];
+ const categoryFacetBreadcrumbValues = Array.from(
+ categoryFacetBreadcrumb.querySelectorAll(
+ selectors.categoryFacetBreadcrumbValue
+ )
+ );
+
+ expect(categoryFacetBreadcrumbValues.length).toBe(1);
+
+ const labels = categoryFacetBreadcrumbValues.map(
+ (facetBreadcrumbValue) => facetBreadcrumbValue.label
+ );
+ const values = exampleFacetBreadcrumb.path.map((item) => item.value);
+ expect(labels).toEqual([values.join(` ${customCategoryDivider} `)]);
+ });
+ });
+ });
});
describe('numeric facet breadcrumbs', () => {
- const exampleNumericFacetBreadcrumbs = [
+ const testNumericFacetBreadcrumbs = [
{
field: 'fieldOne',
- facetId: exampleFacetId,
+ facetId: 'facetIdOne',
values: [{value: {start: 0, end: 1}}, {value: {start: 1, end: 2}}],
},
+ {
+ field: 'fieldTwo',
+ facetId: 'facetIdTwo',
+ values: [{value: {start: 10, end: 11}}, {value: {start: 20, end: 21}}],
+ },
];
beforeAll(() => {
breadcrumbManagerState = {
...breadcrumbManagerState,
- numericFacetBreadcrumbs: exampleNumericFacetBreadcrumbs,
+ numericFacetBreadcrumbs: testNumericFacetBreadcrumbs,
hasBreadcrumbs: true,
};
-
- storeState = {
- [exampleFacetId]: {
- facetId: exampleFacetId,
+ storeState = testNumericFacetBreadcrumbs.reduce((acc, breadcrumb) => {
+ acc[breadcrumb.facetId] = {
+ facetId: breadcrumb.facetId,
format: (item) => `${item.start} - ${item.end}`,
- },
- };
+ };
+ return acc;
+ }, {});
});
it('should properly display the breadcrumb values', async () => {
@@ -252,10 +515,10 @@ describe('c-quantic-breadcrumb-manager', () => {
);
expect(numericFacetBreadcrumbs.length).toBe(
- exampleNumericFacetBreadcrumbs.length
+ testNumericFacetBreadcrumbs.length
);
numericFacetBreadcrumbs.forEach((numericFacetBreadcrumb, index) => {
- const exampleFacetBreadcrumb = exampleNumericFacetBreadcrumbs[index];
+ const exampleFacetBreadcrumb = testNumericFacetBreadcrumbs[index];
const facetBreadcrumbValues = Array.from(
numericFacetBreadcrumb.querySelectorAll(
selectors.numericFacetBreadcrumbValue
@@ -278,30 +541,38 @@ describe('c-quantic-breadcrumb-manager', () => {
});
describe('date facet breadcrumbs', () => {
- const exampleDateFacetBreadcrumbs = [
+ const testDateFacetBreadcrumbs = [
{
field: 'fieldOne',
- facetId: exampleFacetId,
+ facetId: 'facetIdOne',
values: [
{value: {start: 'yesterday', end: 'today'}},
{value: {start: 'today', end: 'tomorrow'}},
],
},
+ {
+ field: 'fieldTwo',
+ facetId: 'facetIdTwo',
+ values: [
+ {value: {start: 'last week', end: 'today'}},
+ {value: {start: 'tomorrow', end: 'next week'}},
+ ],
+ },
];
beforeAll(() => {
breadcrumbManagerState = {
...breadcrumbManagerState,
- dateFacetBreadcrumbs: exampleDateFacetBreadcrumbs,
+ dateFacetBreadcrumbs: testDateFacetBreadcrumbs,
hasBreadcrumbs: true,
};
-
- storeState = {
- [exampleFacetId]: {
- facetId: exampleFacetId,
+ storeState = testDateFacetBreadcrumbs.reduce((acc, breadcrumb) => {
+ acc[breadcrumb.facetId] = {
+ facetId: breadcrumb.facetId,
format: (item) => `${item.start} - ${item.end}`,
- },
- };
+ };
+ return acc;
+ }, {});
});
it('should properly display the breadcrumb values', async () => {
@@ -312,12 +583,10 @@ describe('c-quantic-breadcrumb-manager', () => {
selectors.dateFacetBreadcrumb
);
- expect(dateFacetBreadcrumbs.length).toBe(
- exampleDateFacetBreadcrumbs.length
- );
+ expect(dateFacetBreadcrumbs.length).toBe(testDateFacetBreadcrumbs.length);
dateFacetBreadcrumbs.forEach((dateFacetBreadcrumb, index) => {
- const exampleFacetBreadcrumb = exampleDateFacetBreadcrumbs[index];
+ const exampleFacetBreadcrumb = testDateFacetBreadcrumbs[index];
const facetBreadcrumbValues = Array.from(
dateFacetBreadcrumb.querySelectorAll(
selectors.dateFacetBreadcrumbValue
diff --git a/packages/quantic/force-app/main/default/lwc/quanticBreadcrumbManager/e2e/fixture.ts b/packages/quantic/force-app/main/default/lwc/quanticBreadcrumbManager/e2e/fixture.ts
new file mode 100644
index 00000000000..fb28a695457
--- /dev/null
+++ b/packages/quantic/force-app/main/default/lwc/quanticBreadcrumbManager/e2e/fixture.ts
@@ -0,0 +1,76 @@
+import {BreadcrumbManagerObject} from './pageObject';
+import {quanticBase} from '../../../../../../playwright/fixtures/baseFixture';
+import {SearchObject} from '../../../../../../playwright/page-object/searchObject';
+import {
+ searchRequestRegex,
+ insightSearchRequestRegex,
+} from '../../../../../../playwright/utils/requests';
+import {InsightSetupObject} from '../../../../../../playwright/page-object/insightSetupObject';
+import {useCaseEnum} from '../../../../../../playwright/utils/useCase';
+
+const breadcrumbManagerUrl = 's/quantic-breadcrumb-manager';
+
+interface BreadcrumbManagerOptions {
+ categoryDivider: string;
+ collapseThreshold: number;
+}
+
+type QuanticBreadcrumbManagerE2EFixtures = {
+ breadcrumbManager: BreadcrumbManagerObject;
+ search: SearchObject;
+ options: Partial
;
+};
+
+type QuanticBreadcrumbManagerE2ESearchFixtures =
+ QuanticBreadcrumbManagerE2EFixtures & {
+ urlHash: string;
+ };
+
+type QuanticBreadcrumbManagerE2eInsightFixtures =
+ QuanticBreadcrumbManagerE2ESearchFixtures & {
+ insightSetup: InsightSetupObject;
+ };
+
+export const testSearch =
+ quanticBase.extend({
+ options: {},
+ urlHash: '',
+ search: async ({page}, use) => {
+ await use(new SearchObject(page, searchRequestRegex));
+ },
+ breadcrumbManager: async (
+ {page, options, configuration, search, urlHash},
+ use
+ ) => {
+ await page.goto(
+ urlHash ? `${breadcrumbManagerUrl}#${urlHash}` : breadcrumbManagerUrl
+ );
+ configuration.configure(options);
+ await search.waitForSearchResponse();
+ await use(new BreadcrumbManagerObject(page));
+ },
+ });
+
+export const testInsight =
+ quanticBase.extend({
+ options: {},
+ search: async ({page}, use) => {
+ await use(new SearchObject(page, insightSearchRequestRegex));
+ },
+ insightSetup: async ({page}, use) => {
+ await use(new InsightSetupObject(page));
+ },
+ breadcrumbManager: async (
+ {page, options, search, configuration, insightSetup},
+ use
+ ) => {
+ await page.goto(breadcrumbManagerUrl);
+ configuration.configure({...options, useCase: useCaseEnum.insight});
+ await insightSetup.waitForInsightInterfaceInitialization();
+ await search.performSearch();
+ await search.waitForSearchResponse();
+ await use(new BreadcrumbManagerObject(page));
+ },
+ });
+
+export {expect} from '@playwright/test';
diff --git a/packages/quantic/force-app/main/default/lwc/quanticBreadcrumbManager/e2e/pageObject.ts b/packages/quantic/force-app/main/default/lwc/quanticBreadcrumbManager/e2e/pageObject.ts
new file mode 100644
index 00000000000..d4bb658bd5e
--- /dev/null
+++ b/packages/quantic/force-app/main/default/lwc/quanticBreadcrumbManager/e2e/pageObject.ts
@@ -0,0 +1,282 @@
+import type {Locator, Page, Request} from '@playwright/test';
+import {isUaSearchEvent} from '../../../../../../playwright/utils/requests';
+
+const breadcrumbElementsSelectors = {
+ regularFacet: {
+ component: 'c-quantic-facet',
+ facetValueComponent: 'c-quantic-facet-value',
+ breadcrumbElementTestId: 'facet-breadcrumb',
+ breadcrumbValueElementTestId: 'facet-breadcrumb-value',
+ },
+ numericFacet: {
+ component: 'c-quantic-numeric-facet',
+ facetValueComponent: 'c-quantic-facet-value',
+ breadcrumbElementTestId: 'numeric-facet-breadcrumb',
+ breadcrumbValueElementTestId: 'numeric-facet-breadcrumb-value',
+ },
+ categoryFacet: {
+ component: 'c-quantic-category-facet',
+ facetValueComponent: 'c-quantic-category-facet-value',
+ breadcrumbElementTestId: 'category-facet-breadcrumb',
+ breadcrumbValueElementTestId: 'category-facet-breadcrumb-value',
+ },
+ timeframeFacet: {
+ component: 'c-quantic-timeframe-facet',
+ facetValueComponent: 'c-quantic-facet-value',
+ breadcrumbElementTestId: 'date-facet-breadcrumb',
+ breadcrumbValueElementTestId: 'date-facet-breadcrumb-value',
+ },
+};
+
+export class BreadcrumbManagerObject {
+ constructor(public page: Page) {
+ this.page = page;
+ }
+
+ async countAllFacetsBreadcrumb() {
+ return (
+ (await this.allRegularFacetBreadcrumb.count()) +
+ (await this.allNumericFacetBreadcrumb.count()) +
+ (await this.allCategoryFacetBreadcrumb.count()) +
+ (await this.allTimeframeFacetBreadcrumb.count())
+ );
+ }
+
+ /** REGULAR FACET */
+ get allRegularFacetBreadcrumb(): Locator {
+ return this.page.getByTestId(
+ breadcrumbElementsSelectors.regularFacet.breadcrumbElementTestId
+ );
+ }
+
+ get firstRegularFacetBreadcrumb(): Locator {
+ return this.page
+ .getByTestId(
+ breadcrumbElementsSelectors.regularFacet.breadcrumbElementTestId
+ )
+ .first();
+ }
+
+ async clickFirstRegularFacetBreadcrumbValue(): Promise {
+ await this.firstRegularFacetBreadcrumb
+ .getByTestId(
+ breadcrumbElementsSelectors.regularFacet.breadcrumbValueElementTestId
+ )
+ .click();
+ }
+
+ get firstRegularFacet(): Locator {
+ return this.page
+ .locator(breadcrumbElementsSelectors.regularFacet.component)
+ .first();
+ }
+
+ get firstRegularFacetValue(): Promise {
+ return this.firstRegularFacet
+ .locator(breadcrumbElementsSelectors.regularFacet.facetValueComponent)
+ .first()
+ .locator('.facet__value-text')
+ .textContent();
+ }
+
+ async clickFirstRegularFacetLink(): Promise {
+ await this.clickFirstFacetLink(this.firstRegularFacet);
+ }
+
+ async clickFirstFacetLink(facetLocator: Locator): Promise {
+ await facetLocator
+ .locator(breadcrumbElementsSelectors.regularFacet.facetValueComponent)
+ .first()
+ .click();
+ }
+
+ /** NUMERIC FACET */
+ get allNumericFacetBreadcrumb(): Locator {
+ return this.page.getByTestId(
+ breadcrumbElementsSelectors.numericFacet.breadcrumbElementTestId
+ );
+ }
+
+ get firstNumericFacetBreadcrumb(): Locator {
+ return this.page
+ .getByTestId(
+ breadcrumbElementsSelectors.numericFacet.breadcrumbElementTestId
+ )
+ .first();
+ }
+
+ async clickFirstNumericFacetBreadcrumbValue(): Promise {
+ await this.firstNumericFacetBreadcrumb
+ .getByTestId(
+ breadcrumbElementsSelectors.numericFacet.breadcrumbValueElementTestId
+ )
+ .click();
+ }
+
+ get firstNumericFacet(): Locator {
+ return this.page
+ .locator(breadcrumbElementsSelectors.numericFacet.component)
+ .first();
+ }
+
+ get firstNumericFacetValue(): Promise {
+ return this.firstNumericFacet
+ .locator(breadcrumbElementsSelectors.numericFacet.facetValueComponent)
+ .first()
+ .locator('.facet__value-text')
+ .textContent();
+ }
+
+ async clickFirstNumericFacetLink(): Promise {
+ await this.clickFirstFacetLink(this.firstNumericFacet);
+ }
+
+ /** DATE|TIMEFRAME FACET */
+ get allTimeframeFacetBreadcrumb(): Locator {
+ return this.page.getByTestId(
+ breadcrumbElementsSelectors.timeframeFacet.breadcrumbElementTestId
+ );
+ }
+
+ get firstTimeframeFacetBreadcrumb(): Locator {
+ return this.page
+ .getByTestId(
+ breadcrumbElementsSelectors.timeframeFacet.breadcrumbElementTestId
+ )
+ .first();
+ }
+
+ async clickFirstTimeframeFacetBreadcrumbValue(): Promise {
+ await this.firstTimeframeFacetBreadcrumb
+ .getByTestId(
+ breadcrumbElementsSelectors.timeframeFacet.breadcrumbValueElementTestId
+ )
+ .click();
+ }
+
+ get firstTimeframeFacet(): Locator {
+ return this.page
+ .locator(breadcrumbElementsSelectors.timeframeFacet.component)
+ .first();
+ }
+
+ get firstTimeframeFacetValue(): Promise {
+ return this.firstTimeframeFacet
+ .locator(breadcrumbElementsSelectors.timeframeFacet.facetValueComponent)
+ .first()
+ .locator('.facet__value-text')
+ .textContent();
+ }
+
+ async clickFirstTimeframeFacetLink(): Promise {
+ await this.clickFirstFacetLink(this.firstTimeframeFacet);
+ }
+
+ /** CATEGORY FACET */
+ get allCategoryFacetBreadcrumb(): Locator {
+ return this.page.getByTestId(
+ breadcrumbElementsSelectors.categoryFacet.breadcrumbElementTestId
+ );
+ }
+
+ get firstCategoryFacetBreadcrumb(): Locator {
+ return this.page
+ .getByTestId(
+ breadcrumbElementsSelectors.categoryFacet.breadcrumbElementTestId
+ )
+ .first();
+ }
+
+ async clickFirstCategoryFacetBreadcrumbValue(): Promise {
+ await this.firstCategoryFacetBreadcrumb
+ .getByTestId(
+ breadcrumbElementsSelectors.categoryFacet.breadcrumbValueElementTestId
+ )
+ .click();
+ }
+
+ get firstCategoryFacet(): Locator {
+ return this.page
+ .locator(breadcrumbElementsSelectors.categoryFacet.component)
+ .first();
+ }
+
+ get firstCategoryFacetValue(): Promise {
+ return this.firstCategoryFacet
+ .locator(breadcrumbElementsSelectors.categoryFacet.facetValueComponent)
+ .first()
+ .locator('.facet__value-option')
+ .locator('span:not(.facet__number-of-results)')
+ .textContent();
+ }
+
+ async clickFirstCategoryFacetLink(): Promise {
+ await this.clickFirstCategoryFacetValue(this.firstCategoryFacet);
+ }
+
+ async clickFirstCategoryFacetValue(facetLocator: Locator): Promise {
+ await facetLocator
+ .locator(breadcrumbElementsSelectors.categoryFacet.facetValueComponent)
+ .first()
+ .click();
+ }
+
+ get clearAllButton(): Locator {
+ return this.page.getByRole('button', {name: /Clear All Filters/i});
+ }
+
+ async clickClearAllButton(): Promise {
+ await this.clearAllButton.click();
+ }
+
+ async waitForBreadcrumbSearchUaAnalytics(
+ actionCause: string,
+ customChecker?: Function
+ ): Promise {
+ const uaRequest = this.page.waitForRequest((request) => {
+ if (isUaSearchEvent(request)) {
+ const requestBody = request.postDataJSON?.();
+ const {customData} = requestBody;
+
+ const expectedFields = {
+ actionCause: actionCause,
+ };
+ const matchesExpectedFields = Object.keys(expectedFields).every(
+ (key) => requestBody?.[key] === expectedFields[key]
+ );
+
+ return (
+ matchesExpectedFields &&
+ (customChecker ? customChecker(customData) : true)
+ );
+ }
+ return false;
+ });
+ return uaRequest;
+ }
+
+ async waitForBreadcrumbFacetUaAnalytics(
+ expectedCustomFields: Record
+ ): Promise {
+ return this.waitForBreadcrumbSearchUaAnalytics(
+ 'breadcrumbFacet',
+ (customData: Record) => {
+ return Object.keys(expectedCustomFields).every((key) =>
+ typeof expectedCustomFields[key] === 'object'
+ ? JSON.stringify(customData?.[key]) ===
+ JSON.stringify(expectedCustomFields[key])
+ : customData?.[key] === expectedCustomFields[key]
+ );
+ }
+ );
+ }
+
+ async waitForBreadcrumbResetAllUaAnalytics(
+ customChecker?: Function
+ ): Promise {
+ return this.waitForBreadcrumbSearchUaAnalytics(
+ 'breadcrumbResetAll',
+ customChecker
+ );
+ }
+}
diff --git a/packages/quantic/force-app/main/default/lwc/quanticBreadcrumbManager/e2e/quanticBreadcrumbManager.e2e.ts b/packages/quantic/force-app/main/default/lwc/quanticBreadcrumbManager/e2e/quanticBreadcrumbManager.e2e.ts
new file mode 100644
index 00000000000..5e92bfb403c
--- /dev/null
+++ b/packages/quantic/force-app/main/default/lwc/quanticBreadcrumbManager/e2e/quanticBreadcrumbManager.e2e.ts
@@ -0,0 +1,167 @@
+import {testSearch, testInsight} from './fixture';
+import {
+ useCaseEnum,
+ useCaseTestCases,
+} from '../../../../../../playwright/utils/useCase';
+
+const fixtures = {
+ search: testSearch,
+ insight: testInsight,
+};
+
+const datetimeFacetLabelToValue = {
+ 'Past week': 'past-1-week..now',
+ 'Past month': 'past-1-month..now',
+ 'Past 6 months': 'past-6-month..now',
+ 'Past year': 'past-1-year..now',
+ 'Past decade': 'past-10-year..now',
+};
+
+useCaseTestCases.forEach((useCase) => {
+ let test = fixtures[useCase.value];
+
+ test.describe(`quantic breadcrumb manager ${useCase.label}`, () => {
+ test.describe('when de-selecting a facet value', () => {
+ test.describe('with regular facet values', () => {
+ test('should trigger a new search and log analytics', async ({
+ breadcrumbManager,
+ search,
+ }) => {
+ const firstRegularFacetValue =
+ await breadcrumbManager.firstRegularFacetValue;
+ const searchResponsePromise = search.waitForSearchResponse();
+ await breadcrumbManager.clickFirstRegularFacetLink();
+ await searchResponsePromise;
+
+ const secondSearchResponsePromise = search.waitForSearchResponse();
+ const breadcrumbAnalyticsPromise =
+ breadcrumbManager.waitForBreadcrumbFacetUaAnalytics({
+ facetValue: firstRegularFacetValue,
+ });
+ await breadcrumbManager.clickFirstRegularFacetBreadcrumbValue();
+ await secondSearchResponsePromise;
+ await breadcrumbAnalyticsPromise;
+ });
+ });
+
+ test.describe('with numeric facet values', () => {
+ test('should trigger a new search and log analytics', async ({
+ breadcrumbManager,
+ search,
+ }) => {
+ const [facetRangeStart, facetRangeEnd] =
+ (await breadcrumbManager.firstNumericFacetValue)?.split(' - ') ??
+ [];
+ const searchResponsePromise = search.waitForSearchResponse();
+ await breadcrumbManager.clickFirstNumericFacetLink();
+ await searchResponsePromise;
+
+ const secondSearchResponsePromise = search.waitForSearchResponse();
+ // We use the facet display value to compare, but the display value is localized and can contain non-numeric characters while the analytics request doesn't contain those characters.
+ const breadcrumbAnalyticsPromise =
+ breadcrumbManager.waitForBreadcrumbFacetUaAnalytics({
+ facetRangeStart: facetRangeStart.replace(/[^\d]/g, ''),
+ facetRangeEnd: facetRangeEnd.replace(/[^\d]/g, ''),
+ });
+ await breadcrumbManager.clickFirstNumericFacetBreadcrumbValue();
+ await secondSearchResponsePromise;
+ await breadcrumbAnalyticsPromise;
+ });
+ });
+
+ test.describe('with timeframe|date facet values', () => {
+ test('should trigger a new search and log analytics', async ({
+ breadcrumbManager,
+ search,
+ }) => {
+ const facetValue = await breadcrumbManager.firstTimeframeFacetValue;
+ test.expect(facetValue).not.toBeNull();
+ const searchResponsePromise = search.waitForSearchResponse();
+ await breadcrumbManager.clickFirstTimeframeFacetLink();
+ await searchResponsePromise;
+
+ const secondSearchResponsePromise = search.waitForSearchResponse();
+ // We use the facet display value to compare, but the display value is prettified and is transformed to be human readable, while the analytics request isn't.
+ const [expectedFacetRangeStart, expectedFacetRangeEnd] =
+ datetimeFacetLabelToValue[facetValue!].split('..');
+ const breadcrumbAnalyticsPromise =
+ breadcrumbManager.waitForBreadcrumbFacetUaAnalytics({
+ facetRangeStart: expectedFacetRangeStart,
+ facetRangeEnd: expectedFacetRangeEnd,
+ });
+ await breadcrumbManager.clickFirstTimeframeFacetBreadcrumbValue();
+ await secondSearchResponsePromise;
+ await breadcrumbAnalyticsPromise;
+ });
+ });
+
+ test.describe('with category facet values', () => {
+ test('should trigger a new search and log analytics', async ({
+ breadcrumbManager,
+ search,
+ }) => {
+ const facetValue = await breadcrumbManager.firstCategoryFacetValue;
+ test.expect(facetValue).not.toBeNull();
+ const searchResponsePromise = search.waitForSearchResponse();
+ await breadcrumbManager.clickFirstCategoryFacetLink();
+ await searchResponsePromise;
+
+ const secondSearchResponsePromise = search.waitForSearchResponse();
+ const expectedFacetValue = facetValue!;
+ const breadcrumbAnalyticsPromise =
+ breadcrumbManager.waitForBreadcrumbFacetUaAnalytics({
+ categoryFacetPath: [expectedFacetValue],
+ });
+ await breadcrumbManager.clickFirstCategoryFacetBreadcrumbValue();
+ await secondSearchResponsePromise;
+ await breadcrumbAnalyticsPromise;
+ });
+ });
+ });
+
+ test.describe('when clicking on the clear all filters button', () => {
+ test('should send a clear all filters analytics event and clear all selected facets values', async ({
+ breadcrumbManager,
+ search,
+ }) => {
+ const searchResponsePromise = search.waitForSearchResponse();
+ await breadcrumbManager.clickFirstRegularFacetLink();
+ await searchResponsePromise;
+
+ const searchResponsePromiseTwo = search.waitForSearchResponse();
+ await breadcrumbManager.clickFirstTimeframeFacetLink();
+ await searchResponsePromiseTwo;
+
+ test.expect(breadcrumbManager.clearAllButton).toBeVisible();
+ const clearAllSearchResponsePromise = search.waitForSearchResponse();
+ const clearAllAnalyticsPromise =
+ breadcrumbManager.waitForBreadcrumbResetAllUaAnalytics();
+ await breadcrumbManager.clickClearAllButton();
+ await clearAllAnalyticsPromise;
+ const clearAllSearchResponse = await clearAllSearchResponsePromise;
+ const {facets} = await clearAllSearchResponse.json();
+ const activeFacets = facets.filter((facet: any) =>
+ facet.values.some((value: any) => value.state !== 'idle')
+ );
+ test.expect(activeFacets).toHaveLength(0);
+ });
+ });
+
+ if (useCase.value === useCaseEnum.search) {
+ test.describe('when loading with a facet already selected', () => {
+ const facetSelectedHash = 'f-filetype=YouTubeVideo';
+ test.use({
+ urlHash: facetSelectedHash,
+ });
+
+ test('should have a facet selected in the breadcrumb manager', async ({
+ breadcrumbManager,
+ }) => {
+ test
+ .expect(await breadcrumbManager.countAllFacetsBreadcrumb())
+ .toBe(1);
+ });
+ });
+ }
+ });
+});
diff --git a/packages/quantic/force-app/main/default/lwc/quanticBreadcrumbManager/quanticBreadcrumbManager.html b/packages/quantic/force-app/main/default/lwc/quanticBreadcrumbManager/quanticBreadcrumbManager.html
index 32c7eacbd6b..ee204e37ef2 100644
--- a/packages/quantic/force-app/main/default/lwc/quanticBreadcrumbManager/quanticBreadcrumbManager.html
+++ b/packages/quantic/force-app/main/default/lwc/quanticBreadcrumbManager/quanticBreadcrumbManager.html
@@ -9,29 +9,29 @@
- -
+
-
{breadcrumb.label}{labels.colon}
-
-
+
-
-
+
- -
+
-
{breadcrumb.label}{labels.colon}
- -
+
-
{breadcrumbValue.label}{labels.colon}
- -
+
-
{breadcrumb.label}{labels.colon}
-
-
+
@@ -74,7 +74,7 @@
diff --git a/packages/quantic/force-app/main/default/lwc/quanticUtils/__tests__/markdownUtils.test.js b/packages/quantic/force-app/main/default/lwc/quanticUtils/__tests__/markdownUtils.test.js
index e2cbe00e963..d59b452c98b 100644
--- a/packages/quantic/force-app/main/default/lwc/quanticUtils/__tests__/markdownUtils.test.js
+++ b/packages/quantic/force-app/main/default/lwc/quanticUtils/__tests__/markdownUtils.test.js
@@ -1,6 +1,6 @@
// This is the same js file as what we load inside the LWC via a static resource.
import {transformMarkdownToHtml} from 'c/quanticUtils';
-import {marked} from '../../../../../../node_modules/marked/lib/marked.cjs';
+import {marked} from '../../../../../../../../node_modules/marked/lib/marked.cjs';
const removeLineBreaks = (text) => text.replace(/\n/g, '');
diff --git a/packages/quantic/jsdoc-config.json b/packages/quantic/jsdoc-config.json
index 3a280906e9d..87809e7ef7a 100644
--- a/packages/quantic/jsdoc-config.json
+++ b/packages/quantic/jsdoc-config.json
@@ -4,8 +4,8 @@
},
"plugins": [
"plugins/markdown",
- "./node_modules/@ckeditor/jsdoc-plugins/lib/export-fixer/export-fixer.js",
- "../../node_modules/jsdoc-tsimport-plugin/index.js"
+ "@ckeditor/jsdoc-plugins/lib/export-fixer/export-fixer.js",
+ "jsdoc-tsimport-plugin/index.js"
],
"recurseDepth": 10,
"source": {
diff --git a/packages/quantic/package.json b/packages/quantic/package.json
index 7d08ef04a39..9708d9016eb 100644
--- a/packages/quantic/package.json
+++ b/packages/quantic/package.json
@@ -1,6 +1,6 @@
{
"name": "@coveo/quantic",
- "version": "3.11.0",
+ "version": "3.11.1",
"description": "A Salesforce Lightning Web Component (LWC) library for building modern UIs interfacing with the Coveo platform",
"author": "coveo.com",
"homepage": "https://coveo.com",
@@ -51,7 +51,7 @@
},
"dependencies": {
"@coveo/bueno": "1.0.7",
- "@coveo/headless": "3.12.0",
+ "@coveo/headless": "3.13.0",
"dompurify": "3.2.3",
"marked": "12.0.2",
"fs-extra": "11.2.0"
@@ -65,24 +65,20 @@
"@ckeditor/jsdoc-plugins": "43.0.1",
"@coveo/relay-event-types": "13.0.0",
"@coveo/release": "1.0.0",
- "@lwc/compiler": "5.3.0",
- "@lwc/eslint-plugin-lwc": "1.9.0",
"@octokit/graphql": "8.1.1",
"@octokit/graphql-schema": "15.25.0",
"@playwright/test": "1.49.1",
"@salesforce/eslint-config-lwc": "3.6.0",
- "@salesforce/eslint-plugin-lightning": "1.0.0",
- "@salesforce/sfdx-lwc-jest": "7.0.1",
+ "@salesforce/sfdx-lwc-jest": "5.1.0",
"@types/node": "22.10.2",
"@types/strip-color": "0.1.2",
"@types/wait-on": "5.3.4",
"chalk": "4.1.2",
"change-case": "4.1.2",
- "cypress": "13.17.0",
+ "cypress": "13.6.6",
"cypress-real-events": "1.13.0",
"cypress-repeat": "2.3.8",
"dotenv": "16.4.7",
- "eslint-plugin-jest": "28.10.0",
"jest": "29.7.0",
"jest-junit": "16.0.0",
"jsdoc": "4.0.4",
diff --git a/packages/samples/angular/package.json b/packages/samples/angular/package.json
index c0d7f781812..88f08ce841b 100644
--- a/packages/samples/angular/package.json
+++ b/packages/samples/angular/package.json
@@ -19,9 +19,8 @@
"@angular/platform-browser": "18.2.13",
"@angular/platform-browser-dynamic": "18.2.13",
"@angular/router": "18.2.13",
- "@coveo/atomic-angular": "3.2.8",
+ "@coveo/atomic-angular": "3.2.9",
"rxjs": "7.8.1",
- "tslib": "2.8.1",
"zone.js": "0.15.0"
},
"devDependencies": {
@@ -32,8 +31,6 @@
"@types/node": "22.10.2",
"@typescript-eslint/eslint-plugin": "7.18.0",
"gts": "5.3.1",
- "jest": "29.7.0",
- "ncp": "2.0.0",
"typescript": "5.5.4"
}
}
diff --git a/packages/samples/atomic-next/package.json b/packages/samples/atomic-next/package.json
index 53325af091e..e614343ffdf 100644
--- a/packages/samples/atomic-next/package.json
+++ b/packages/samples/atomic-next/package.json
@@ -4,9 +4,9 @@
"private": true,
"type": "module",
"dependencies": {
- "@coveo/atomic": "3.12.1",
- "@coveo/atomic-react": "3.2.8",
- "@coveo/headless": "3.12.0",
+ "@coveo/atomic": "3.13.0",
+ "@coveo/atomic-react": "3.2.9",
+ "@coveo/headless": "3.13.0",
"next": "14.2.20",
"react": "18.3.1",
"react-dom": "18.3.1"
@@ -15,7 +15,7 @@
"@types/node": "22.10.2",
"@types/react": "18.3.3",
"@types/react-dom": "18.3.0",
- "cypress": "13.17.0",
+ "cypress": "13.6.6",
"cypress-repeat": "2.3.8",
"ncp": "2.0.0",
"typescript": "5.5.4"
diff --git a/packages/samples/atomic-react/package.json b/packages/samples/atomic-react/package.json
index 21e6d6ae226..eda8f4b9cff 100644
--- a/packages/samples/atomic-react/package.json
+++ b/packages/samples/atomic-react/package.json
@@ -10,8 +10,8 @@
"build": "nx build"
},
"dependencies": {
- "@coveo/atomic-react": "3.2.8",
- "@coveo/headless": "3.12.0",
+ "@coveo/atomic-react": "3.2.9",
+ "@coveo/headless": "3.13.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 ee96fbdcdb0..794f54737db 100644
--- a/packages/samples/headless-commerce-react/package.json
+++ b/packages/samples/headless-commerce-react/package.json
@@ -4,7 +4,7 @@
"private": true,
"type": "module",
"dependencies": {
- "@coveo/headless": "3.12.0",
+ "@coveo/headless": "3.13.0",
"react": "18.3.1",
"react-dom": "18.3.1"
},
diff --git a/packages/samples/headless-commerce-ssr-remix/package.json b/packages/samples/headless-commerce-ssr-remix/package.json
index bcdf0d9aafb..9b2cb275485 100644
--- a/packages/samples/headless-commerce-ssr-remix/package.json
+++ b/packages/samples/headless-commerce-ssr-remix/package.json
@@ -22,8 +22,8 @@
"@remix-run/dev": "2.15.2",
"@types/react": "18.3.3",
"@types/react-dom": "18.3.0",
- "autoprefixer": "10.4.20",
"typescript": "5.5.4",
+ "vite": "5.4.11",
"vite-tsconfig-paths": "5.1.4"
}
}
diff --git a/packages/samples/headless-react/package.json b/packages/samples/headless-react/package.json
index 3891ba5027d..af63d2d9d92 100644
--- a/packages/samples/headless-react/package.json
+++ b/packages/samples/headless-react/package.json
@@ -8,8 +8,8 @@
"node": "^20.9.0 || ^22.11.0"
},
"dependencies": {
- "@coveo/auth": "2.0.5",
- "@coveo/headless": "3.12.0",
+ "@coveo/auth": "2.0.6",
+ "@coveo/headless": "3.13.0",
"@testing-library/jest-dom": "6.6.3",
"@testing-library/react": "14.3.1",
"@testing-library/user-event": "14.5.2",
@@ -52,10 +52,9 @@
"devDependencies": {
"@typescript-eslint/eslint-plugin": "8.7.0",
"@vitejs/plugin-react": "4.3.2",
- "cypress": "13.17.0",
+ "cypress": "13.6.6",
"cypress-repeat": "2.3.8",
"gts": "5.3.1",
- "patch-package": "6.4.7",
"vite": "5.4.11",
"vitest": "2.1.8"
}
diff --git a/packages/samples/headless-ssr-commerce/package.json b/packages/samples/headless-ssr-commerce/package.json
index b8b4b3a06cd..bd3c9fbb13d 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-react": "2.3.0",
+ "@coveo/headless-react": "2.4.0",
"next": "14.2.20",
"react": "18.3.1",
"react-dom": "18.3.1"
diff --git a/packages/samples/headless-ssr/package.json b/packages/samples/headless-ssr/package.json
index 48070c62791..198b721443f 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": "2.3.0",
- "@coveo/headless": "3.12.0",
+ "@coveo/headless-react": "2.4.0",
+ "@coveo/headless": "3.13.0",
"next": "14.2.20",
"react": "18.3.1",
"react-dom": "18.3.1"
@@ -21,7 +21,7 @@
"eslint": "8.57.1",
"eslint-config-react-app": "7.0.1",
"typescript": "5.5.4",
- "cypress": "13.17.0",
+ "cypress": "13.6.6",
"cypress-repeat": "2.3.8",
"cypress-web-vitals": "4.1.2"
}
diff --git a/packages/samples/iife/package.json b/packages/samples/iife/package.json
index 9e02ab60eb2..5ced5d68364 100644
--- a/packages/samples/iife/package.json
+++ b/packages/samples/iife/package.json
@@ -12,15 +12,15 @@
},
"dependencies": {
"@babel/standalone": "7.26.4",
- "@coveo/atomic": "3.12.1",
- "@coveo/atomic-hosted-page": "1.0.16",
- "@coveo/atomic-react": "3.2.8",
- "@coveo/headless": "3.12.0",
+ "@coveo/atomic": "3.13.0",
+ "@coveo/atomic-hosted-page": "1.0.17",
+ "@coveo/atomic-react": "3.2.9",
+ "@coveo/headless": "3.13.0",
"react": "18.3.1",
"react-dom": "18.3.1"
},
"devDependencies": {
- "cypress": "13.17.0",
+ "cypress": "13.6.6",
"cypress-repeat": "2.3.8",
"ncp": "2.0.0",
"resolve": "1.22.10",
diff --git a/packages/samples/stencil/package.json b/packages/samples/stencil/package.json
index 0bee41b4002..74bec1d19ab 100644
--- a/packages/samples/stencil/package.json
+++ b/packages/samples/stencil/package.json
@@ -8,15 +8,15 @@
"e2e:watch": "cypress open --browser chrome --e2e"
},
"dependencies": {
- "@coveo/atomic": "3.12.1",
+ "@coveo/atomic": "3.13.0",
"@coveo/bueno": "1.0.7",
- "@coveo/headless": "3.12.0",
+ "@coveo/headless": "3.13.0",
"@stencil/core": "4.20.0",
"stencil-router-v2": "0.6.0"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "7.18.0",
- "cypress": "13.17.0",
+ "cypress": "13.6.6",
"cypress-repeat": "2.3.8",
"gts": "5.3.1",
"rollup-plugin-html": "0.2.1"
diff --git a/packages/samples/vuejs/package.json b/packages/samples/vuejs/package.json
index be4899ea5a3..5f4d0c1dd60 100644
--- a/packages/samples/vuejs/package.json
+++ b/packages/samples/vuejs/package.json
@@ -13,14 +13,13 @@
},
"dependencies": {
"vue": "3.5.13",
- "@coveo/atomic": "3.12.1"
+ "@coveo/atomic": "3.13.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "5.2.1",
"typescript": "5.5.4",
"vite": "5.4.11",
- "vue-tsc": "2.2.0",
- "cypress": "13.17.0",
+ "cypress": "13.6.6",
"cypress-repeat": "2.3.8",
"ncp": "2.0.0"
}
diff --git a/renovate.json b/renovate.json
index 94cd4bbb86b..653caf8c6ce 100644
--- a/renovate.json
+++ b/renovate.json
@@ -176,5 +176,9 @@
"vulnerabilityAlerts": {
"enabled": false
},
- "ignoreDeps": ["eslint-plugin-canonical"]
+ "ignoreDeps": [
+ "eslint-plugin-canonical",
+ "@salesforce/sfdx-lwc-jest",
+ "cypress"
+ ]
}
diff --git a/tsconfig.json b/tsconfig.json
index 53d2116ffa8..f7d1d2a454e 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -3,10 +3,5 @@
"extends": "@tsconfig/node20/tsconfig.json",
"compilerOptions": {
"skipLibCheck": true
- },
- "ts-node": {
- "compilerOptions": {
- "module": "commonjs"
- }
}
}
diff --git a/utils/release/reify.mjs b/utils/release/reify.mjs
index ee5848f744d..f83ad892777 100755
--- a/utils/release/reify.mjs
+++ b/utils/release/reify.mjs
@@ -35,7 +35,9 @@ function buildDependencyGraph(rootNode) {
: node.edgesOut;
const workspaces = edgesOut.filter(
(edge) =>
- edge.to.package.name && rootNode.workspaces?.has(edge.to.package.name)
+ edge.to &&
+ edge.to.package.name &&
+ rootNode.workspaces?.has(edge.to.package.name)
);
return workspaces.map((edge) =>
edge.to instanceof Arborist.Link ? edge.to.target : edge.to
@@ -48,7 +50,7 @@ function buildDependencyGraph(rootNode) {
function addWorkspaceDependencies(node) {
const dependencies = getWorkspaceDependencies(node);
for (const dependency of dependencies) {
- if (!node.package.name || !dependency.package.name) {
+ if (!node.package.name || !dependency || !dependency.package.name) {
throw 'Workspaces must all have a name.';
}
if (node.package.name === dependency.package.name) {
@@ -61,13 +63,15 @@ function buildDependencyGraph(rootNode) {
const workspaces = getWorkspaceDependencies(rootNode);
for (const workspace of workspaces) {
- if (!workspace.package.name) {
+ if (!workspace || !workspace.package.name) {
throw 'Workspaces must all have a name.';
}
graph.addNode(workspace.package.name, workspace);
}
for (const workspace of workspaces) {
- addWorkspaceDependencies(workspace);
+ if (workspace) {
+ addWorkspaceDependencies(workspace);
+ }
}
return graph;
}