From ff39242eacf7b201fca4907a24a4dd537d242221 Mon Sep 17 00:00:00 2001 From: ylakhdar Date: Fri, 27 Sep 2024 10:23:12 -0400 Subject: [PATCH] fix(atomic): remove product currency glitch on render (#4408) Remove `` component from `` and format the price with the currency directly on the component. Also, moved reusable code in utils/ https://coveord.atlassian.net/browse/KIT-3476 --- .../atomic-product-numeric-field-value.tsx | 23 +------ .../atomic-product-price.tsx | 68 ++++++++++++++----- .../product-template-components/error.ts | 6 ++ .../product-utils.ts | 13 ++++ .../common/atomic-rating/atomic-rating.tsx | 5 +- .../listing-surf-accessories.html | 2 +- 6 files changed, 75 insertions(+), 42 deletions(-) create mode 100644 packages/atomic/src/components/commerce/product-template-components/error.ts diff --git a/packages/atomic/src/components/commerce/product-template-components/atomic-product-numeric-field-value/atomic-product-numeric-field-value.tsx b/packages/atomic/src/components/commerce/product-template-components/atomic-product-numeric-field-value/atomic-product-numeric-field-value.tsx index 632657e51bc..15295522b03 100644 --- a/packages/atomic/src/components/commerce/product-template-components/atomic-product-numeric-field-value/atomic-product-numeric-field-value.tsx +++ b/packages/atomic/src/components/commerce/product-template-components/atomic-product-numeric-field-value/atomic-product-numeric-field-value.tsx @@ -1,4 +1,4 @@ -import {Product, ProductTemplatesHelpers} from '@coveo/headless/commerce'; +import {Product} from '@coveo/headless/commerce'; import {Component, Prop, Element, State, Listen} from '@stencil/core'; import {Bindings} from '../../../../components'; import {InitializeBindings} from '../../../../utils/initialization-utils'; @@ -7,6 +7,7 @@ import { NumberFormatter, } from '../../../common/formats/format-common'; import {ProductContext} from '../product-template-decorators'; +import {parseValue} from '../product-utils'; /** * @alpha @@ -43,24 +44,6 @@ export class AtomicProductNumber { this.formatter = event.detail; } - private parseValue() { - const value = ProductTemplatesHelpers.getProductProperty( - this.product, - this.field - ); - if (value === null) { - return null; - } - const valueAsNumber = parseFloat(`${value}`); - if (Number.isNaN(valueAsNumber)) { - this.error = new Error( - `Could not parse "${value}" from field "${this.field}" as a number.` - ); - return null; - } - return valueAsNumber; - } - private formatValue(value: number) { try { return this.formatter(value, this.bindings.i18n.languages as string[]); @@ -71,7 +54,7 @@ export class AtomicProductNumber { } private updateValueToDisplay() { - const value = this.parseValue(); + const value = parseValue(this.product, this.field); if (value !== null) { this.valueToDisplay = this.formatValue(value); } diff --git a/packages/atomic/src/components/commerce/product-template-components/atomic-product-price/atomic-product-price.tsx b/packages/atomic/src/components/commerce/product-template-components/atomic-product-price/atomic-product-price.tsx index 6220cf32401..43fd9d1655b 100644 --- a/packages/atomic/src/components/commerce/product-template-components/atomic-product-price/atomic-product-price.tsx +++ b/packages/atomic/src/components/commerce/product-template-components/atomic-product-price/atomic-product-price.tsx @@ -10,8 +10,10 @@ import { InitializableComponent, InitializeBindings, } from '../../../../utils/initialization-utils'; +import {defaultCurrencyFormatter} from '../../../common/formats/format-common'; import {CommerceBindings} from '../../atomic-commerce-interface/atomic-commerce-interface'; import {ProductContext} from '../product-template-decorators'; +import {parseValue} from '../product-utils'; /** * @alpha @@ -35,31 +37,61 @@ export class AtomicProductPrice this.context = buildContext(this.bindings.engine); } - public render() { - const hasPromotionalPrice = + private formatValue(value: number) { + try { + const {currency} = this.contextState; + const formatter = defaultCurrencyFormatter(currency); + return formatter(value, this.bindings.i18n.languages as string[]); + } catch (error) { + this.error = error as Error; + return value.toString(); + } + } + + private parse(field: string) { + try { + return parseValue(this.product, field); + } catch (error) { + this.error = error as Error; + return null; + } + } + + private getFormattedValue(field: string) { + const value = this.parse(field); + if (value !== null) { + return this.formatValue(value); + } + } + + private get hasPromotionalPrice() { + return ( this.product.ec_promo_price !== null && this.product.ec_price !== null && - this.product.ec_promo_price < this.product.ec_price; + this.product.ec_promo_price < this.product.ec_price + ); + } + + public render() { + const mainPrice = this.getFormattedValue( + this.hasPromotionalPrice ? 'ec_promo_price' : 'ec_price' + ); - const {currency} = this.contextState; + const originalPrice = this.hasPromotionalPrice + ? this.getFormattedValue('ec_price') + : null; return (
- - - - {hasPromotionalPrice && ( - - - + {mainPrice} +
+ {originalPrice && ( +
+ {originalPrice} +
)} ); diff --git a/packages/atomic/src/components/commerce/product-template-components/error.ts b/packages/atomic/src/components/commerce/product-template-components/error.ts new file mode 100644 index 00000000000..133a3855d34 --- /dev/null +++ b/packages/atomic/src/components/commerce/product-template-components/error.ts @@ -0,0 +1,6 @@ +export class FieldValueIsNaNError extends Error { + constructor(field: string, value?: {}) { + super(`Could not parse "${value}" from field "${field}" as a number.`); + this.name = 'FieldValueIsNaNError'; + } +} diff --git a/packages/atomic/src/components/commerce/product-template-components/product-utils.ts b/packages/atomic/src/components/commerce/product-template-components/product-utils.ts index bce0b69cb26..2f7f54f5573 100644 --- a/packages/atomic/src/components/commerce/product-template-components/product-utils.ts +++ b/packages/atomic/src/components/commerce/product-template-components/product-utils.ts @@ -1,6 +1,19 @@ import {Product, ProductTemplatesHelpers} from '@coveo/headless/commerce'; import {readFromObject} from '../../../utils/object-utils'; import {CommerceBindings} from '../atomic-commerce-interface/atomic-commerce-interface'; +import {FieldValueIsNaNError} from './error'; + +export function parseValue(product: Product, field: string) { + const value = ProductTemplatesHelpers.getProductProperty(product, field); + if (value === null) { + return null; + } + const valueAsNumber = parseFloat(`${value}`); + if (Number.isNaN(valueAsNumber)) { + throw new FieldValueIsNaNError(field, value); + } + return valueAsNumber; +} export function getStringValueFromProductOrNull( product: Product, diff --git a/packages/atomic/src/components/common/atomic-rating/atomic-rating.tsx b/packages/atomic/src/components/common/atomic-rating/atomic-rating.tsx index 42584ed4462..0c7c8ee75f8 100644 --- a/packages/atomic/src/components/common/atomic-rating/atomic-rating.tsx +++ b/packages/atomic/src/components/common/atomic-rating/atomic-rating.tsx @@ -1,5 +1,6 @@ import {FunctionalComponent, h, VNode} from '@stencil/core'; import {i18n} from 'i18next'; +import {FieldValueIsNaNError} from '../../commerce/product-template-components/error'; interface RatingProps { i18n: i18n; @@ -72,9 +73,7 @@ export const computeNumberOfStars = ( } const valueAsNumber = parseFloat(`${value}`); if (Number.isNaN(valueAsNumber)) { - throw new Error( - `Could not parse "${value}" from field "${field}" as a number.` - ); + throw new FieldValueIsNaNError(field, value); } return valueAsNumber; }; diff --git a/packages/atomic/src/pages/examples/commerce-website/listing-surf-accessories.html b/packages/atomic/src/pages/examples/commerce-website/listing-surf-accessories.html index 0a3d4917b41..b4a3e82f08d 100644 --- a/packages/atomic/src/pages/examples/commerce-website/listing-surf-accessories.html +++ b/packages/atomic/src/pages/examples/commerce-website/listing-surf-accessories.html @@ -55,7 +55,7 @@

Surf accessories

- +