diff --git a/packages/atomic/src/components/commerce/atomic-commerce-product-list/atomic-commerce-product-list.tsx b/packages/atomic/src/components/commerce/atomic-commerce-product-list/atomic-commerce-product-list.tsx index bb384d08964..7b337b8a9b3 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-product-list/atomic-commerce-product-list.tsx +++ b/packages/atomic/src/components/commerce/atomic-commerce-product-list/atomic-commerce-product-list.tsx @@ -217,12 +217,24 @@ export class AtomicCommerceProductList ); } + private logWarningIfNeeded(message?: string) { + if (message) { + this.bindings.engine.logger.warn(message); + } + } + + private getInteractiveProduct(product: Product) { + const parentController = + this.bindings.interfaceElement.type === 'product-listing' + ? this.productListing + : this.search; + + return parentController.interactiveProduct({options: {product}}); + } + private getPropsForAtomicProduct(product: Product) { return { - // TODO: add back once interactive result is implemented for products in KIT-3149 - /* interactiveResult: buildInteractiveResult(this.bindings.engine, { - options: {result}, - }), */ + interactiveProduct: this.getInteractiveProduct(product), product, renderingFunction: this.itemRenderingFunction, loadingFlag: this.loadingFlag, @@ -243,6 +255,7 @@ export class AtomicCommerceProductList private renderAsGrid() { return this.productState.products.map((product, i) => { const propsForAtomicProduct = this.getPropsForAtomicProduct(product); + const {interactiveProduct} = propsForAtomicProduct; return ( element && this.productListCommon.setNewResultRef(element, i) } - select={function (): void { - throw new Error('Function not implemented. TODO KIT-3149'); + select={() => { + this.logWarningIfNeeded(interactiveProduct.warningMessage); + interactiveProduct.select(); }} - beginDelayedSelect={function (): void { - throw new Error('Function not implemented. TODO KIT-3149'); + beginDelayedSelect={() => { + this.logWarningIfNeeded(interactiveProduct.warningMessage); + interactiveProduct.beginDelayedSelect(); }} - cancelPendingSelect={function (): void { - throw new Error('Function not implemented. TODO KIT-3149'); + cancelPendingSelect={() => { + this.logWarningIfNeeded(interactiveProduct.warningMessage); + interactiveProduct.cancelPendingSelect(); }} > - + ); }); @@ -328,7 +342,6 @@ export class AtomicCommerceProductList const propsForAtomicProduct = this.getPropsForAtomicProduct(product); return ( element && this.productListCommon.setNewResultRef(element, i) diff --git a/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/atomic-commerce-recommendation-interface.tsx b/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/atomic-commerce-recommendation-interface.tsx index 2d759fb2e86..7a7f0e9cfe8 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/atomic-commerce-recommendation-interface.tsx +++ b/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/atomic-commerce-recommendation-interface.tsx @@ -126,12 +126,6 @@ export class AtomicCommerceRecommendationInterface this, 'CoveoAtomic' ); - - if (!this.commonInterfaceHelper.engineIsCreated()) { - return; - } - - this.contextController = buildContext(this.bindings.engine); } public connectedCallback() { @@ -233,6 +227,10 @@ export class AtomicCommerceRecommendationInterface }; } + private initContext() { + this.contextController = buildContext(this.bindings.engine); + } + private initAriaLive() { if ( Array.from(this.host.children).some( @@ -246,6 +244,7 @@ export class AtomicCommerceRecommendationInterface private async internalInitialization(initEngine: () => void) { await this.commonInterfaceHelper.onInitialization(initEngine); + this.initContext(); } private addResourceBundle( diff --git a/packages/atomic/src/components/commerce/atomic-commerce-recommendation-list/atomic-commerce-recommendation-list.tsx b/packages/atomic/src/components/commerce/atomic-commerce-recommendation-list/atomic-commerce-recommendation-list.tsx index 53d086d578f..a66c8c58f75 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-recommendation-list/atomic-commerce-recommendation-list.tsx +++ b/packages/atomic/src/components/commerce/atomic-commerce-recommendation-list/atomic-commerce-recommendation-list.tsx @@ -302,29 +302,27 @@ export class AtomicCommerceRecommendationList ); } - private getAtomicProductProps(recommendation: Product) { + private logWarningIfNeeded(message?: string) { + if (message) { + this.bindings.engine.logger.warn(message); + } + } + + private getAtomicProductProps(product: Product) { return { interactiveProduct: this.recommendations.interactiveProduct({ - options: { - product: { - ...recommendation, - name: recommendation.ec_name ?? '', - price: this.getPrice(recommendation), - productId: recommendation.permanentid, - }, - position: this.currentIndex, - }, + options: {product}, }), - product: recommendation, + product: product, renderingFunction: this.itemRenderingFunction, loadingFlag: this.loadingFlag, key: this.itemListCommon.getResultId( - recommendation.permanentid, + product.permanentid, this.recommendationsState.responseId, this.density, this.imageSize ), - content: this.productTemplateProvider.getTemplateContent(recommendation), + content: this.productTemplateProvider.getTemplateContent(product), store: this.bindings.store, density: this.density, display: this.display, @@ -332,20 +330,6 @@ export class AtomicCommerceRecommendationList }; } - private getPrice(recommendation: Product) { - if (recommendation.ec_price === undefined) { - return 0; - } - - if (recommendation.ec_promo_price === undefined) { - return recommendation.ec_price; - } - - return recommendation.ec_promo_price > recommendation.ec_price - ? recommendation.ec_promo_price - : recommendation.ec_price; - } - private computeListDisplayClasses() { const displayPlaceholders = !this.bindings.store.isAppLoaded(); @@ -359,7 +343,8 @@ export class AtomicCommerceRecommendationList } private renderAsGrid(product: Product, i: number) { - const atomicProductProps = this.getAtomicProductProps(product); + const propsForAtomicProduct = this.getAtomicProductProps(product); + const {interactiveProduct} = propsForAtomicProduct; return ( { + this.logWarningIfNeeded(interactiveProduct.warningMessage); + interactiveProduct.select(); + }} + beginDelayedSelect={() => { + this.logWarningIfNeeded(interactiveProduct.warningMessage); + interactiveProduct.beginDelayedSelect(); + }} + cancelPendingSelect={() => { + this.logWarningIfNeeded(interactiveProduct.warningMessage); + interactiveProduct.cancelPendingSelect(); + }} setRef={(element) => element && this.itemListCommon.setNewResultRef(element, i) } > - + ); } diff --git a/packages/atomic/src/components/commerce/product-template-components/atomic-product-link/atomic-product-link.tsx b/packages/atomic/src/components/commerce/product-template-components/atomic-product-link/atomic-product-link.tsx index 8940b8d719d..2bbd1525066 100644 --- a/packages/atomic/src/components/commerce/product-template-components/atomic-product-link/atomic-product-link.tsx +++ b/packages/atomic/src/components/commerce/product-template-components/atomic-product-link/atomic-product-link.tsx @@ -56,6 +56,12 @@ export class AtomicProductLink private linkAttributes?: Attr[]; private stopPropagation?: boolean; + private logWarningIfNeed(warning?: string) { + if (warning) { + this.bindings.engine.logger.warn(warning); + } + } + public initialize() { this.host.dispatchEvent( buildCustomEvent( @@ -78,16 +84,27 @@ export class AtomicProductLink ? this.product.clickUri : 'test'; + if (!this.interactiveProduct) { + return; + } + + const {warningMessage} = this.interactiveProduct; + return ( this.interactiveProduct.select()} - onBeginDelayedSelect={() => - this.interactiveProduct.beginDelayedSelect() - } - onCancelPendingSelect={() => - this.interactiveProduct.cancelPendingSelect() - } + onSelect={() => { + this.logWarningIfNeed(warningMessage); + this.interactiveProduct.select(); + }} + onBeginDelayedSelect={() => { + this.logWarningIfNeed(warningMessage); + this.interactiveProduct.beginDelayedSelect(); + }} + onCancelPendingSelect={() => { + this.logWarningIfNeed(warningMessage); + this.interactiveProduct.cancelPendingSelect(); + }} attributes={this.linkAttributes} stopPropagation={this.stopPropagation} > diff --git a/packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.tsx b/packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.tsx index a9f274347ea..00f03a731fd 100644 --- a/packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.tsx +++ b/packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.tsx @@ -3,8 +3,6 @@ import { buildInstantProducts, Product, InstantProducts, - InteractiveProduct, - CommerceEngine, } from '@coveo/headless/commerce'; import {Component, Element, State, h, Prop, Method} from '@stencil/core'; import {InitializableComponent} from '../../../../utils/initialization-utils'; @@ -34,14 +32,6 @@ export type AriaLabelGenerator = ( product: Product ) => string | undefined; -// TODO: KIT-3165 Uncomment once the `buildInteractiveInstantProduct` function is implemented in headless. -function buildInteractiveInstantProduct( - _engine: CommerceEngine<{}>, - _arg: {options: {product: Product}} -): InteractiveProduct { - return {} as InteractiveProduct; -} - /** * The `atomic-commerce-search-box-instant-products` component can be added as a child of an `atomic-search-box` component, allowing for the configuration of instant results behavior. * @@ -141,6 +131,9 @@ export class AtomicCommerceSearchBoxInstantProducts const elements: SearchBoxSuggestionElement[] = products.map( (product: Product) => { + const interactiveProduct = this.instantProducts.interactiveProduct({ + options: {product}, + }); const partialItem = getPartialInstantItemElement( this.bindings.i18n, this.ariaLabelGenerator?.(this.bindings, product) || product.ec_name!, @@ -153,12 +146,7 @@ export class AtomicCommerceSearchBoxInstantProducts key={`instant-product-${encodeForDomAttribute(product.permanentid)}`} part="outline" product={product} - interactiveProduct={buildInteractiveInstantProduct( - this.bindings.engine, - { - options: {product}, - } - )} + interactiveProduct={interactiveProduct} display={this.display} density={this.density} imageSize={this.imageSize} diff --git a/packages/atomic/src/pages/examples/commerce-website/cart.html b/packages/atomic/src/pages/examples/commerce-website/cart.html index 63c1ac23be5..ebde5777aab 100644 --- a/packages/atomic/src/pages/examples/commerce-website/cart.html +++ b/packages/atomic/src/pages/examples/commerce-website/cart.html @@ -12,9 +12,12 @@ import {commerceEngine} from './engine.mjs'; (async () => { - await customElements.whenDefined('atomic-commerce-interface'); - const commerceInterface = document.querySelector('atomic-commerce-recommendation-interface'); - await commerceInterface.initializeWithEngine(commerceEngine); + await customElements.whenDefined('atomic-commerce-recommendation-interface'); + const recommendationInterfaces = document.querySelectorAll('atomic-commerce-recommendation-interface'); + + for (const recommendationInterface of recommendationInterfaces) { + await recommendationInterface.initializeWithEngine(commerceEngine); + } })();