diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 257d854dca9..e2bac96de1d 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -56,7 +56,7 @@ jobs:
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
- ref: 'release/v2'
+ ref: 'release/v3'
- uses: ./.github/actions/setup
- name: Promote NPM package to production
run: npm run promote:npm:latest
@@ -67,7 +67,7 @@ jobs:
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
- ref: 'release/v2'
+ ref: 'release/v3'
- uses: ./.github/actions/setup
- name: Notify Docs
run: npm run notify:docs
@@ -84,7 +84,7 @@ jobs:
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
with:
- ref: 'release/v2'
+ ref: 'release/v3'
- uses: ./.github/actions/setup
- uses: ./.github/actions/setup-sfdx
- name: Promote SFDX package to production
diff --git a/.prettierignore b/.prettierignore
index 1bbac2f56e2..26b8de76282 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -22,4 +22,5 @@ packages/atomic/src/external-builds/**/*
packages/atomic/src/generated/**
packages/quantic/docs/out/quantic-docs.json
packages/samples/headless-react/build/**/*
-packages/samples/angular/src/lang/*.json
\ No newline at end of file
+packages/samples/angular/src/lang/*.json
+packages/samples/vuejs/public/lang/*.json
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index b65e805f9cf..639a98e9efa 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -55808,7 +55808,7 @@
},
"packages/atomic": {
"name": "@coveo/atomic",
- "version": "2.77.2",
+ "version": "2.78.0",
"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.79.0",
+ "@coveo/headless": "2.80.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.79.0"
+ "@coveo/headless": "2.80.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.2",
+ "@coveo/atomic": "2.78.0",
"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.79.0",
+ "@coveo/headless": "2.80.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.79.0"
+ "@coveo/headless": "2.80.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.2",
+ "version": "2.28.0",
"license": "Apache-2.0",
"dependencies": {
- "@coveo/atomic": "2.77.2",
+ "@coveo/atomic": "2.78.0",
"tslib": "2.6.3"
},
"peerDependencies": {
"@angular/common": "14 - 17",
"@angular/core": "14 - 17",
- "@coveo/headless": "2.79.0"
+ "@coveo/headless": "2.80.0"
}
},
"packages/atomic-hosted-page": {
"name": "@coveo/atomic-hosted-page",
- "version": "0.6.6",
+ "version": "0.6.7",
"license": "Apache-2.0",
"dependencies": {
"@coveo/bueno": "0.46.1",
- "@coveo/headless": "2.79.0",
+ "@coveo/headless": "2.80.0",
"@stencil/core": "4.20.0"
},
"devDependencies": {
@@ -56069,12 +56069,12 @@
},
"packages/atomic-react": {
"name": "@coveo/atomic-react",
- "version": "2.13.5",
+ "version": "2.14.0",
"dependencies": {
- "@coveo/atomic": "2.77.2"
+ "@coveo/atomic": "2.78.0"
},
"devDependencies": {
- "@coveo/headless": "2.79.0",
+ "@coveo/headless": "2.80.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.79.0",
+ "@coveo/headless": "2.80.0",
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
}
@@ -59225,7 +59225,7 @@
},
"packages/headless": {
"name": "@coveo/headless",
- "version": "2.79.0",
+ "version": "2.80.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.3",
+ "version": "1.1.4",
"license": "Apache-2.0",
"dependencies": {
- "@coveo/headless": "2.79.0"
+ "@coveo/headless": "2.80.0"
},
"devDependencies": {
"@coveo/release": "1.0.0",
@@ -59826,12 +59826,12 @@
},
"packages/quantic": {
"name": "@coveo/quantic",
- "version": "2.57.0",
+ "version": "2.57.1",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@coveo/bueno": "0.46.1",
- "@coveo/headless": "2.79.0",
+ "@coveo/headless": "2.80.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.2",
+ "@coveo/atomic-angular": "2.28.0",
"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.2",
- "@coveo/atomic-react": "2.13.5",
- "@coveo/headless": "2.79.0",
+ "@coveo/atomic": "2.78.0",
+ "@coveo/atomic-react": "2.14.0",
+ "@coveo/headless": "2.80.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.2",
- "@coveo/atomic-react": "2.13.5",
- "@coveo/headless": "2.79.0",
+ "@coveo/atomic": "2.78.0",
+ "@coveo/atomic-react": "2.14.0",
+ "@coveo/headless": "2.80.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.79.0",
+ "@coveo/headless": "2.80.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.79.0",
+ "@coveo/headless": "2.80.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.79.0",
- "@coveo/headless-react": "1.1.3",
+ "@coveo/headless": "2.80.0",
+ "@coveo/headless-react": "1.1.4",
"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.79.0",
+ "@coveo/headless": "2.80.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.2",
- "@coveo/atomic-hosted-page": "0.6.6",
- "@coveo/atomic-react": "2.13.5",
- "@coveo/headless": "2.79.0",
+ "@coveo/atomic": "2.78.0",
+ "@coveo/atomic-hosted-page": "0.6.7",
+ "@coveo/atomic-react": "2.14.0",
+ "@coveo/headless": "2.80.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.2",
- "@coveo/headless": "2.79.0",
+ "@coveo/atomic": "2.78.0",
+ "@coveo/headless": "2.80.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.2",
+ "@coveo/atomic": "2.78.0",
"vue": "^3.4.15"
},
"devDependencies": {
diff --git a/packages/atomic-angular/package.json b/packages/atomic-angular/package.json
index 0c22d6b8c29..bb5a9f5a691 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.2",
+ "@coveo/atomic": "2.78.0",
"rxjs": "7.8.1"
},
"peerDependencies": {
- "@coveo/headless": "2.79.0"
+ "@coveo/headless": "2.80.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "17.3.8",
"@angular/cli": "17.3.8",
"@angular/compiler-cli": "17.3.12",
- "@coveo/headless": "2.79.0",
+ "@coveo/headless": "2.80.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 f8027e3c64b..061da079dcf 100644
--- a/packages/atomic-angular/projects/atomic-angular/CHANGELOG.md
+++ b/packages/atomic-angular/projects/atomic-angular/CHANGELOG.md
@@ -1,3 +1,9 @@
+# 2.28.0 (2024-08-27)
+
+### Features
+
+- **atomic:** allow user to customize grid card click behavior/links ([#4287](https://github.com/coveo/ui-kit/issues/4287)) ([e5961ca](https://github.com/coveo/ui-kit/commits/e5961ca33490499a2916bb9eb8818d360ff85c18)), closes [#4267](https://github.com/coveo/ui-kit/issues/4267)
+
## 2.27.1 (2024-08-20)
### Bug Fixes
diff --git a/packages/atomic-angular/projects/atomic-angular/package.json b/packages/atomic-angular/projects/atomic-angular/package.json
index f4472094f35..58f450308bd 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.2",
+ "version": "2.28.0",
"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.79.0"
+ "@coveo/headless": "2.80.0"
},
"dependencies": {
- "@coveo/atomic": "2.77.2",
+ "@coveo/atomic": "2.78.0",
"tslib": "2.6.3"
}
}
diff --git a/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/atomic-angular.module.ts b/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/atomic-angular.module.ts
index 64a2bbc46c5..cd71a0561c3 100644
--- a/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/atomic-angular.module.ts
+++ b/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/atomic-angular.module.ts
@@ -17,6 +17,7 @@ AtomicCommerceFacets,
AtomicCommerceInterface,
AtomicCommerceLoadMoreProducts,
AtomicCommercePager,
+AtomicCommerceProductList,
AtomicCommerceProductsPerPage,
AtomicCommerceQuerySummary,
AtomicCommerceRefineModal,
@@ -128,6 +129,7 @@ AtomicCommerceFacets,
AtomicCommerceInterface,
AtomicCommerceLoadMoreProducts,
AtomicCommercePager,
+AtomicCommerceProductList,
AtomicCommerceProductsPerPage,
AtomicCommerceQuerySummary,
AtomicCommerceRefineModal,
diff --git a/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/components.ts b/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/components.ts
index 1fdba07a8bb..e35f1ed7f1d 100644
--- a/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/components.ts
+++ b/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/components.ts
@@ -274,6 +274,29 @@ export declare interface AtomicCommercePager extends Components.AtomicCommercePa
}
+@ProxyCmp({
+ inputs: ['density', 'display', 'gridCellLinkTarget', 'imageSize', 'numberOfPlaceholders'],
+ methods: ['setRenderFunction']
+})
+@Component({
+ selector: 'atomic-commerce-product-list',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ template: '',
+ // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
+ inputs: ['density', 'display', 'gridCellLinkTarget', 'imageSize', 'numberOfPlaceholders'],
+})
+export class AtomicCommerceProductList {
+ protected el: HTMLElement;
+ constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
+ c.detach();
+ this.el = r.nativeElement;
+ }
+}
+
+
+export declare interface AtomicCommerceProductList extends Components.AtomicCommerceProductList {}
+
+
@ProxyCmp({
inputs: ['choicesDisplayed', 'initialChoice']
})
@@ -561,7 +584,7 @@ export declare interface AtomicFieldCondition extends Components.AtomicFieldCond
@ProxyCmp({
- inputs: ['childField', 'collectionField', 'density', 'imageSize', 'numberOfFoldedResults', 'parentField'],
+ inputs: ['childField', 'collectionField', 'density', 'imageSize', 'numberOfFoldedResults', 'parentField', 'tabsExcluded', 'tabsIncluded'],
methods: ['setRenderFunction']
})
@Component({
@@ -569,7 +592,7 @@ export declare interface AtomicFieldCondition extends Components.AtomicFieldCond
changeDetection: ChangeDetectionStrategy.OnPush,
template: '',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
- inputs: ['childField', 'collectionField', 'density', 'imageSize', 'numberOfFoldedResults', 'parentField'],
+ inputs: ['childField', 'collectionField', 'density', 'imageSize', 'numberOfFoldedResults', 'parentField', 'tabsExcluded', 'tabsIncluded'],
})
export class AtomicFoldedResultList {
protected el: HTMLElement;
@@ -1279,14 +1302,14 @@ export declare interface AtomicRelevanceInspector extends Components.AtomicRelev
@ProxyCmp({
- inputs: ['classes', 'content', 'density', 'display', 'imageSize', 'result', 'stopPropagation']
+ inputs: ['classes', 'content', 'density', 'display', 'imageSize', 'linkContent', 'result', 'stopPropagation']
})
@Component({
selector: 'atomic-result',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
- inputs: ['classes', 'content', 'density', 'display', 'imageSize', 'result', 'stopPropagation'],
+ inputs: ['classes', 'content', 'density', 'display', 'imageSize', 'linkContent', 'result', 'stopPropagation'],
})
export class AtomicResult {
protected el: HTMLElement;
@@ -1498,7 +1521,7 @@ export declare interface AtomicResultLink extends Components.AtomicResultLink {}
@ProxyCmp({
- inputs: ['density', 'display', 'gridCellLinkTarget', 'imageSize'],
+ inputs: ['density', 'display', 'gridCellLinkTarget', 'imageSize', 'tabsExcluded', 'tabsIncluded'],
methods: ['setRenderFunction']
})
@Component({
@@ -1506,7 +1529,7 @@ export declare interface AtomicResultLink extends Components.AtomicResultLink {}
changeDetection: ChangeDetectionStrategy.OnPush,
template: '',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
- inputs: ['density', 'display', 'gridCellLinkTarget', 'imageSize'],
+ inputs: ['density', 'display', 'gridCellLinkTarget', 'imageSize', 'tabsExcluded', 'tabsIncluded'],
})
export class AtomicResultList {
protected el: HTMLElement;
diff --git a/packages/atomic-hosted-page/package.json b/packages/atomic-hosted-page/package.json
index 3d64bed6768..4d90b2f25ae 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.6",
+ "version": "0.6.7",
"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.79.0",
+ "@coveo/headless": "2.80.0",
"@stencil/core": "4.20.0"
},
"devDependencies": {
diff --git a/packages/atomic-react/CHANGELOG.md b/packages/atomic-react/CHANGELOG.md
index 0462330ccac..2d1cd4c28b1 100644
--- a/packages/atomic-react/CHANGELOG.md
+++ b/packages/atomic-react/CHANGELOG.md
@@ -1,3 +1,9 @@
+# 2.14.0 (2024-08-27)
+
+### Features
+
+- **atomic:** allow user to customize grid card click behavior/links ([#4287](https://github.com/coveo/ui-kit/issues/4287)) ([e5961ca](https://github.com/coveo/ui-kit/commits/e5961ca33490499a2916bb9eb8818d360ff85c18)), closes [#4267](https://github.com/coveo/ui-kit/issues/4267)
+
## 2.13.1 (2024-08-07)
# 2.13.0 (2024-07-31)
diff --git a/packages/atomic-react/package.json b/packages/atomic-react/package.json
index 37c7868bcb8..cbfe483b818 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.5",
+ "version": "2.14.0",
"description": "React specific wrapper for the Atomic component library",
"repository": {
"type": "git",
@@ -29,11 +29,11 @@
"commerce/"
],
"dependencies": {
- "@coveo/atomic": "2.77.2"
+ "@coveo/atomic": "2.78.0"
},
"devDependencies": {
"@coveo/release": "1.0.0",
- "@coveo/headless": "2.79.0",
+ "@coveo/headless": "2.80.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.79.0",
+ "@coveo/headless": "2.80.0",
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
}
diff --git a/packages/atomic-react/src/components/commerce/CommerceProductListWrapper.tsx b/packages/atomic-react/src/components/commerce/CommerceProductListWrapper.tsx
index d10f7441ca3..4769ae01562 100644
--- a/packages/atomic-react/src/components/commerce/CommerceProductListWrapper.tsx
+++ b/packages/atomic-react/src/components/commerce/CommerceProductListWrapper.tsx
@@ -3,7 +3,15 @@ import type {Product} from '@coveo/headless/commerce';
import React, {useEffect, useRef} from 'react';
import {createRoot} from 'react-dom/client';
import {renderToString} from 'react-dom/server';
-import {AtomicCommerceProductList} from '../stencil-generated/commerce';
+import {
+ AtomicCommerceProductList,
+ AtomicProductLink,
+} from '../stencil-generated/commerce';
+
+interface Template {
+ contentTemplate: JSX.Element;
+ linkTemplate: JSX.Element;
+}
/**
* The properties of the AtomicCommerceProductList component
@@ -13,7 +21,7 @@ interface WrapperProps extends AtomicJSX.AtomicCommerceProductList {
* A template function that takes a result item and outputs its target rendering as a JSX element.
* It can be used to conditionally render different type of result templates based on the properties of each result.
*/
- template: (result: Product) => JSX.Element;
+ template: (result: Product) => JSX.Element | Template;
}
/**
@@ -27,12 +35,30 @@ export const ListWrapper: React.FC = (props) => {
const commerceProductListRef =
useRef(null);
useEffect(() => {
- commerceProductListRef.current?.setRenderFunction((result, root) => {
- createRoot(root).render(template(result as Product));
- return renderToString(template(result as Product));
- });
+ commerceProductListRef.current?.setRenderFunction(
+ (product, root, linkContainer) => {
+ const templateResult = template(product as Product);
+ if (hasLinkTemplate(templateResult)) {
+ createRoot(linkContainer!).render(templateResult.linkTemplate);
+ createRoot(root).render(templateResult.contentTemplate);
+ return renderToString(templateResult.contentTemplate);
+ } else {
+ createRoot(root).render(templateResult);
+ createRoot(linkContainer!).render(
+
+ );
+ return renderToString(templateResult);
+ }
+ }
+ );
}, [commerceProductListRef]);
return (
);
};
+
+const hasLinkTemplate = (
+ template: JSX.Element | Template
+): template is Template => {
+ return (template as Template).linkTemplate !== undefined;
+};
diff --git a/packages/atomic-react/src/components/commerce/CommerceRecommendationListWrapper.tsx b/packages/atomic-react/src/components/commerce/CommerceRecommendationListWrapper.tsx
index 8bc4b731be8..dd9b5de1d80 100644
--- a/packages/atomic-react/src/components/commerce/CommerceRecommendationListWrapper.tsx
+++ b/packages/atomic-react/src/components/commerce/CommerceRecommendationListWrapper.tsx
@@ -3,7 +3,15 @@ import type {Product} from '@coveo/headless/commerce';
import React, {useEffect, useRef} from 'react';
import {createRoot} from 'react-dom/client';
import {renderToString} from 'react-dom/server';
-import {AtomicCommerceRecommendationList} from '../stencil-generated/commerce';
+import {
+ AtomicCommerceRecommendationList,
+ AtomicProductLink,
+} from '../stencil-generated/commerce';
+
+interface Template {
+ contentTemplate: JSX.Element;
+ linkTemplate: JSX.Element;
+}
/**
* The properties of the AtomicCommerceRecommendationList component
@@ -13,7 +21,7 @@ interface WrapperProps extends AtomicJSX.AtomicCommerceRecommendationList {
* A template function that takes a result item and outputs its target rendering as a JSX element.
* It can be used to conditionally render different type of result templates based on the properties of each result.
*/
- template: (result: Product) => JSX.Element;
+ template: (result: Product) => JSX.Element | Template;
}
/**
@@ -27,10 +35,22 @@ export const ListWrapper: React.FC = (props) => {
const commerceRecsListRef =
useRef(null);
useEffect(() => {
- commerceRecsListRef.current?.setRenderFunction((result, root) => {
- createRoot(root).render(template(result as Product));
- return renderToString(template(result as Product));
- });
+ commerceRecsListRef.current?.setRenderFunction(
+ (product, root, linkContainer) => {
+ const templateResult = template(product as Product);
+ if (hasLinkTemplate(templateResult)) {
+ createRoot(linkContainer!).render(templateResult.linkTemplate);
+ createRoot(root).render(templateResult.contentTemplate);
+ return renderToString(templateResult.contentTemplate);
+ } else {
+ createRoot(root).render(templateResult);
+ createRoot(linkContainer!).render(
+
+ );
+ return renderToString(templateResult);
+ }
+ }
+ );
}, [commerceRecsListRef]);
return (
= (props) => {
/>
);
};
+
+const hasLinkTemplate = (
+ template: JSX.Element | Template
+): template is Template => {
+ return (template as Template).linkTemplate !== undefined;
+};
diff --git a/packages/atomic-react/src/components/recommendation/RecsListWrapper.tsx b/packages/atomic-react/src/components/recommendation/RecsListWrapper.tsx
index 43a594887cd..21f610cae81 100644
--- a/packages/atomic-react/src/components/recommendation/RecsListWrapper.tsx
+++ b/packages/atomic-react/src/components/recommendation/RecsListWrapper.tsx
@@ -3,7 +3,12 @@ import type {Result} from '@coveo/headless/recommendation';
import React, {useEffect, useRef} from 'react';
import {createRoot} from 'react-dom/client';
import {renderToString} from 'react-dom/server';
-import {AtomicRecsList} from '../stencil-generated/search';
+import {AtomicRecsList, AtomicResultLink} from '../stencil-generated/search';
+
+interface Template {
+ contentTemplate: JSX.Element;
+ linkTemplate: JSX.Element;
+}
/**
* The properties of the AtomicRecsList component
@@ -13,7 +18,7 @@ interface WrapperProps extends AtomicJSX.AtomicRecsList {
* A template function that takes a result item and outputs its target rendering as a JSX element.
* It can be used to conditionally render different type of result templates based on the properties of each result.
*/
- template: (result: Result) => JSX.Element;
+ template: (result: Result) => JSX.Element | Template;
}
/**
@@ -26,10 +31,26 @@ export const RecsListWrapper: React.FC = (props) => {
const {template, ...otherProps} = props;
const recsListRef = useRef(null);
useEffect(() => {
- recsListRef.current?.setRenderFunction((result, root) => {
- createRoot(root).render(template(result as Result));
- return renderToString(template(result as Result));
+ recsListRef.current?.setRenderFunction((result, root, linkContainer) => {
+ const templateResult = template(result as Result);
+ if (hasLinkTemplate(templateResult)) {
+ createRoot(linkContainer!).render(templateResult.linkTemplate);
+ createRoot(root).render(templateResult.contentTemplate);
+ return renderToString(templateResult.contentTemplate);
+ } else {
+ createRoot(root).render(templateResult);
+ createRoot(linkContainer!).render(
+
+ );
+ return renderToString(templateResult);
+ }
});
}, [recsListRef]);
return ;
};
+
+const hasLinkTemplate = (
+ template: JSX.Element | Template
+): template is Template => {
+ return (template as Template).linkTemplate !== undefined;
+};
diff --git a/packages/atomic-react/src/components/search/ResultListWrapper.tsx b/packages/atomic-react/src/components/search/ResultListWrapper.tsx
index e685d9ba2fe..183c1489a04 100644
--- a/packages/atomic-react/src/components/search/ResultListWrapper.tsx
+++ b/packages/atomic-react/src/components/search/ResultListWrapper.tsx
@@ -3,7 +3,12 @@ import type {Result} from '@coveo/headless';
import React, {useEffect, useRef} from 'react';
import {createRoot} from 'react-dom/client';
import {renderToString} from 'react-dom/server';
-import {AtomicResultList} from '../stencil-generated/search';
+import {AtomicResultLink, AtomicResultList} from '../stencil-generated/search';
+
+interface Template {
+ contentTemplate: JSX.Element;
+ linkTemplate: JSX.Element;
+}
/**
* The properties of the AtomicResultList component
@@ -13,7 +18,7 @@ interface WrapperProps extends AtomicJSX.AtomicResultList {
* A template function that takes a result item and outputs its target rendering as a JSX element.
* It can be used to conditionally render different type of result templates based on the properties of each result.
*/
- template: (result: Result) => JSX.Element;
+ template: (result: Result) => JSX.Element | Template;
}
/**
@@ -26,10 +31,26 @@ export const ResultListWrapper: React.FC = (props) => {
const {template, ...otherProps} = props;
const resultListRef = useRef(null);
useEffect(() => {
- resultListRef.current?.setRenderFunction((result, root) => {
- createRoot(root).render(template(result as Result));
- return renderToString(template(result as Result));
+ resultListRef.current?.setRenderFunction((result, root, linkContainer) => {
+ const templateResult = template(result as Result);
+ if (hasLinkTemplate(templateResult)) {
+ createRoot(linkContainer!).render(templateResult.linkTemplate);
+ createRoot(root).render(templateResult.contentTemplate);
+ return renderToString(templateResult.contentTemplate);
+ } else {
+ createRoot(root).render(templateResult);
+ createRoot(linkContainer!).render(
+
+ );
+ return renderToString(templateResult);
+ }
});
}, [resultListRef]);
return ;
};
+
+const hasLinkTemplate = (
+ template: JSX.Element | Template
+): template is Template => {
+ return (template as Template).linkTemplate !== undefined;
+};
diff --git a/packages/atomic/CHANGELOG.md b/packages/atomic/CHANGELOG.md
index 257308a2725..402186acb0f 100644
--- a/packages/atomic/CHANGELOG.md
+++ b/packages/atomic/CHANGELOG.md
@@ -1,3 +1,16 @@
+# 2.78.0 (2024-08-27)
+
+### Bug Fixes
+
+- **atomic:** Facets don't update correctly in the refine modal when changing tabs ([#4280](https://github.com/coveo/ui-kit/issues/4280)) ([221e1bc](https://github.com/coveo/ui-kit/commits/221e1bcce68f5d74e092edf561dd6b401178c3e3))
+- **atomic:** prevent item click when carousel buttons are clicked ([#4297](https://github.com/coveo/ui-kit/issues/4297)) ([67d0416](https://github.com/coveo/ui-kit/commits/67d0416470b8b1fa77488da4bd8e534ffcc715eb))
+- **atomic:** search-box suggestions should be resilient to search-box redirection-url changes ([#4289](https://github.com/coveo/ui-kit/issues/4289)) ([19cabeb](https://github.com/coveo/ui-kit/commits/19cabeb2665a21a71f883c731ef4d5167cb1841d))
+
+### Features
+
+- **atomic:** allow user to customize grid card click behavior/links ([#4287](https://github.com/coveo/ui-kit/issues/4287)) ([e5961ca](https://github.com/coveo/ui-kit/commits/e5961ca33490499a2916bb9eb8818d360ff85c18)), closes [#4267](https://github.com/coveo/ui-kit/issues/4267)
+- **headless commerce ssr:** add SSR FacetGenerator ([#4290](https://github.com/coveo/ui-kit/issues/4290)) ([3f957f9](https://github.com/coveo/ui-kit/commits/3f957f9334708501a5fbaf73720e1646e6ea4611))
+
## 2.77.1 (2024-08-20)
### Bug Fixes
diff --git a/packages/atomic/cypress/e2e/result-list/result-components/result-link.cypress.ts b/packages/atomic/cypress/e2e/result-list/result-components/result-link.cypress.ts
index 1f4ece4229b..23cdfea9cdb 100644
--- a/packages/atomic/cypress/e2e/result-list/result-components/result-link.cypress.ts
+++ b/packages/atomic/cypress/e2e/result-list/result-components/result-link.cypress.ts
@@ -81,7 +81,7 @@ describe('Result Link Component', () => {
});
it('should render an "atomic-result-text" component containing the title', () => {
- ResultLinkSelectors.firstInResult().should('have.text', title);
+ ResultLinkSelectors.firstInResult().first().should('have.text', title);
});
});
diff --git a/packages/atomic/cypress/e2e/result-list/result-components/result-text-selectors.ts b/packages/atomic/cypress/e2e/result-list/result-components/result-text-selectors.ts
index 4806a1cdd37..bd24d7265e1 100644
--- a/packages/atomic/cypress/e2e/result-list/result-components/result-text-selectors.ts
+++ b/packages/atomic/cypress/e2e/result-list/result-components/result-text-selectors.ts
@@ -5,6 +5,6 @@ export const resultTextComponent = 'atomic-result-text';
export const ResultTextSelectors = {
shadow: () => cy.get(resultTextComponent),
firstInResult: () =>
- ResultListSelectors.firstResult().find(resultTextComponent),
+ ResultListSelectors.firstResult().find(resultTextComponent).first(),
highlight: () => ResultTextSelectors.firstInResult().find('b'),
};
diff --git a/packages/atomic/package.json b/packages/atomic/package.json
index 935250ba696..1083abc7b8b 100644
--- a/packages/atomic/package.json
+++ b/packages/atomic/package.json
@@ -1,6 +1,6 @@
{
"name": "@coveo/atomic",
- "version": "2.77.2",
+ "version": "2.78.0",
"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.79.0",
+ "@coveo/headless": "2.80.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.79.0"
+ "@coveo/headless": "2.80.0"
},
"license": "Apache-2.0",
"engines": {
diff --git a/packages/atomic/playwright.config.ts b/packages/atomic/playwright.config.ts
index f8542191478..a773b2e7a0c 100644
--- a/packages/atomic/playwright.config.ts
+++ b/packages/atomic/playwright.config.ts
@@ -26,14 +26,14 @@ export default defineConfig({
viewport: DEFAULT_DESKTOP_VIEWPORT,
},
},
- {
- name: 'firefox',
- use: {...devices['Desktop Firefox'], viewport: DEFAULT_DESKTOP_VIEWPORT},
- },
- {
- name: 'webkit',
- use: {...devices['Desktop Safari'], viewport: DEFAULT_DESKTOP_VIEWPORT},
- },
+ // {
+ // name: 'firefox',
+ // use: {...devices['Desktop Firefox'], viewport: DEFAULT_DESKTOP_VIEWPORT},
+ // },
+ // {
+ // name: 'webkit',
+ // use: {...devices['Desktop Safari'], viewport: DEFAULT_DESKTOP_VIEWPORT},
+ // },
],
expect: {
timeout: 7 * 1000,
diff --git a/packages/atomic/src/components.d.ts b/packages/atomic/src/components.d.ts
index c2bc9d757d5..7efa28b1938 100644
--- a/packages/atomic/src/components.d.ts
+++ b/packages/atomic/src/components.d.ts
@@ -479,6 +479,9 @@ export namespace Components {
*/
"previousButtonIcon": string;
}
+ /**
+ * @alpha The `atomic-commerce-product-list` component is responsible for displaying products.
+ */
interface AtomicCommerceProductList {
/**
* The spacing of various elements in the product list, including the gap between products, the gap between parts of a product, and the font sizes of different parts in a product.
@@ -491,6 +494,7 @@ export namespace Components {
/**
* The target location to open the product link (see [target](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#target)). This property is only leveraged when `display` is `grid`.
* @defaultValue `_self`
+ * @deprecated - Instead of using this property, provide an `atomic-product-link` in the `link` slot of the `atomic-product-template` component.
*/
"gridCellLinkTarget": ItemTarget;
/**
@@ -593,6 +597,7 @@ export namespace Components {
/**
* The [target](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#target) location to open the product link. This property is ignored unless the `display` property is set to `grid`.
* @defaultValue `_self`
+ * @deprecated - Instead of using this property, provide an `atomic-product-link` in the `link` slot of the `atomic-product-template` component.
*/
"gridCellLinkTarget": ItemTarget;
/**
@@ -992,6 +997,14 @@ export namespace Components {
* Sets a rendering function to bypass the standard HTML template mechanism for rendering results. You can use this function while working with web frameworks that don't use plain HTML syntax, e.g., React, Angular or Vue. Do not use this method if you integrate Atomic in a plain HTML deployment.
*/
"setRenderFunction": (resultRenderingFunction: ItemRenderingFunction) => Promise;
+ /**
+ * The tabs on which this folded result list must not be displayed. This property should not be used at the same time as `tabs-included`. Set this property as a stringified JSON array, e.g., ```html ``` If you don't set this property, the folded result list can be displayed on any tab. Otherwise, the folded result list won't be displayed on any of the specified tabs.
+ */
+ "tabsExcluded": string[] | string;
+ /**
+ * The tabs on which the folded result list can be displayed. This property should not be used at the same time as `tabs-excluded`. Set this property as a stringified JSON array, e.g., ```html ``` If you don't set this property, the folded result list can be displayed on any tab. Otherwise, the folded result list can only be displayed on the specified tabs.
+ */
+ "tabsIncluded": string[] | string;
}
/**
* The `atomic-format-currency` component is used for formatting currencies.
@@ -1638,6 +1651,7 @@ export namespace Components {
/**
* The target location to open the result link (see [target](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#target)). This property is only leveraged when `display` is `grid`.
* @defaultValue `_self`
+ * @deprecated - Instead of using this property, provide an `atomic-result-link` in the `link` slot of the `atomic-result-template` component.
*/
"gridCellLinkTarget": ItemTarget1;
/**
@@ -1929,6 +1943,11 @@ export namespace Components {
* The InteractiveProduct item.
*/
"interactiveProduct": InteractiveProduct;
+ /**
+ * The product link to use when the product is clicked in a grid layout.
+ * @default - An `atomic-result-link` without any customization.
+ */
+ "linkContent": ParentNode;
"loadingFlag"?: string;
/**
* The product item.
@@ -2373,6 +2392,7 @@ export namespace Components {
/**
* The target location to open the result link (see [target](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#target)). This property is only leveraged when `display` is `grid`.
* @defaultValue `_self`
+ * @deprecated - Instead of using this property, provide an `atomic-result-link` in the `link` slot of the `atomic-result-template` component.
*/
"gridCellLinkTarget": ItemTarget;
/**
@@ -2544,6 +2564,11 @@ export namespace Components {
* The InteractiveResult item.
*/
"interactiveResult": InteractiveResult;
+ /**
+ * The result link to use when the result is clicked in a grid layout.
+ * @default - An `atomic-result-link` without any customization.
+ */
+ "linkContent": ParentNode;
"loadingFlag"?: string;
/**
* Internal function used by atomic-recs-list in advanced setups, which lets you bypass the standard HTML template system. Particularly useful for Atomic React
@@ -2723,6 +2748,7 @@ export namespace Components {
/**
* The target location to open the result link (see [target](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#target)). This property is only leveraged when `display` is `grid`.
* @defaultValue `_self`
+ * @deprecated - Instead of using this property, provide an `atomic-result-link` in the `link` slot of the `atomic-result-template` component.
*/
"gridCellLinkTarget": ItemTarget;
/**
@@ -2734,6 +2760,14 @@ export namespace Components {
* @param resultRenderingFunction
*/
"setRenderFunction": (resultRenderingFunction: ItemRenderingFunction) => Promise;
+ /**
+ * The tabs on which this result list must not be displayed. This property should not be used at the same time as `tabs-included`. Set this property as a stringified JSON array, e.g., ```html ``` If you don't set this property, the result list can be displayed on any tab. Otherwise, the result list won't be displayed on any of the specified tabs.
+ */
+ "tabsExcluded": string[] | string;
+ /**
+ * The tabs on which the result list can be displayed. This property should not be used at the same time as `tabs-excluded`. Set this property as a stringified JSON array, e.g., ```html ``` If you don't set this property, the result list can be displayed on any tab. Otherwise, the result list can only be displayed on the specified tabs.
+ */
+ "tabsIncluded": string[] | string;
}
/**
* The `atomic-result-localized-text` component renders a target i18n localized string using the values of a target field.
@@ -2923,8 +2957,6 @@ export namespace Components {
}
/**
* A [result template](https://docs.coveo.com/en/atomic/latest/usage/displaying-results#defining-a-result-template) determines the format of the query results, depending on the conditions that are defined for each template.
- * A `template` element must be the child of an `atomic-result-template`, and either an `atomic-result-list` or `atomic-folded-result-list` must be the parent of each `atomic-result-template`.
- * **Note:** Any `