-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Commerce Atomic): add product price component (#3918)
This PR adds the product price template component. This component displays the value of ec_price, in the specified currency. Additionally, it displays the value of ec_promo_price when that value is lower. <img width="1000" alt="image" src="https://github.com/coveo/ui-kit/assets/1728218/682cd999-7b50-41fd-b9da-c28541551cc3"> --------- Co-authored-by: Frederic Beaudoin <fbeaudoin@coveo.com>
- Loading branch information
1 parent
a5da5ce
commit 67c9d80
Showing
8 changed files
with
210 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
91 changes: 91 additions & 0 deletions
91
...late-components/atomic-product-numeric-field-value/atomic-product-numeric-field-value.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import {Product, ProductTemplatesHelpers} from '@coveo/headless/commerce'; | ||
import {Component, Prop, Element, State, Listen} from '@stencil/core'; | ||
import {Bindings} from '../../../../components'; | ||
import {InitializeBindings} from '../../../../utils/initialization-utils'; | ||
import { | ||
defaultNumberFormatter, | ||
NumberFormatter, | ||
} from '../../../common/formats/format-common'; | ||
import {ProductContext} from '../product-template-decorators'; | ||
|
||
/** | ||
* @internal | ||
* The `atomic-product-numeric-field-value` component renders the value of a number product field. | ||
* | ||
* The number can be formatted by adding a `atomic-format-number`, `atomic-format-currency` or `atomic-format-unit` component into this component. | ||
*/ | ||
@Component({ | ||
tag: 'atomic-product-numeric-field-value', | ||
shadow: false, | ||
}) | ||
export class AtomicProductNumber { | ||
@InitializeBindings() public bindings!: Bindings; | ||
@ProductContext() private product!: Product; | ||
|
||
@Element() host!: HTMLElement; | ||
|
||
@State() public error!: Error; | ||
|
||
/** | ||
* The field that the component should use. | ||
* The component will try to find this field in the `Product.additionalFields` object unless it finds it in the `Product` object first. | ||
*/ | ||
@Prop({reflect: true}) field!: string; | ||
|
||
@State() formatter: NumberFormatter = defaultNumberFormatter; | ||
|
||
@State() valueToDisplay: string | null = null; | ||
|
||
@Listen('atomic/numberFormat') | ||
public setFormat(event: CustomEvent<NumberFormatter>) { | ||
event.preventDefault(); | ||
event.stopPropagation(); | ||
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[]); | ||
} catch (error) { | ||
this.error = error as Error; | ||
return value.toString(); | ||
} | ||
} | ||
|
||
private updateValueToDisplay() { | ||
const value = this.parseValue(); | ||
if (value !== null) { | ||
this.valueToDisplay = this.formatValue(value); | ||
} | ||
} | ||
|
||
componentWillRender() { | ||
this.updateValueToDisplay(); | ||
} | ||
|
||
public render() { | ||
if (this.valueToDisplay === null) { | ||
this.host.remove(); | ||
return; | ||
} | ||
return this.valueToDisplay; | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
...onents/commerce/product-template-components/atomic-product-price/atomic-product-price.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import {Product} from '@coveo/headless/commerce'; | ||
import {Component, h, Prop} from '@stencil/core'; | ||
import { | ||
InitializableComponent, | ||
InitializeBindings, | ||
} from '../../../../utils/initialization-utils'; | ||
import {CommerceBindings} from '../../atomic-commerce-interface/atomic-commerce-interface'; | ||
import {ProductContext} from '../product-template-decorators'; | ||
|
||
/** | ||
* @internal | ||
* The `atomic-product-price` component renders the price of a product. | ||
*/ | ||
@Component({ | ||
tag: 'atomic-product-price', | ||
shadow: false, | ||
}) | ||
export class AtomicProductPrice | ||
implements InitializableComponent<CommerceBindings> | ||
{ | ||
@InitializeBindings() public bindings!: CommerceBindings; | ||
public error!: Error; | ||
|
||
@ProductContext() private product!: Product; | ||
|
||
/** | ||
* The currency to use in currency formatting. Allowed values are the [ISO 4217 currency codes](https://www.six-group.com/en/products-services/financial-information/data-standards.html#scrollTo=maintenance-agency), such as "USD" for the US dollar, "EUR" for the euro, or "CNY" for the Chinese RMB. | ||
*/ | ||
@Prop({reflect: true}) public currency: string = 'USD'; | ||
|
||
public render() { | ||
const hasPromotionalPrice = | ||
this.product?.ec_promo_price !== undefined && | ||
this.product?.ec_price !== undefined && | ||
this.product?.ec_promo_price < this.product?.ec_price; | ||
|
||
return ( | ||
<div> | ||
<atomic-product-numeric-field-value | ||
class={`mx-1 ${hasPromotionalPrice && 'text-error'}`} | ||
field={hasPromotionalPrice ? 'ec_promo_price' : 'ec_price'} | ||
> | ||
<atomic-format-currency | ||
currency={this.currency} | ||
></atomic-format-currency> | ||
</atomic-product-numeric-field-value> | ||
{hasPromotionalPrice && ( | ||
<atomic-product-numeric-field-value | ||
class="mx-1 text-xl line-through" | ||
field="ec_price" | ||
> | ||
<atomic-format-currency | ||
currency={this.currency} | ||
></atomic-format-currency> | ||
</atomic-product-numeric-field-value> | ||
)} | ||
</div> | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters