diff --git a/packages/atomic/src/components.d.ts b/packages/atomic/src/components.d.ts index f8f359d8e36..4967119bedf 100644 --- a/packages/atomic/src/components.d.ts +++ b/packages/atomic/src/components.d.ts @@ -440,6 +440,16 @@ export namespace Components { */ "maxWithoutQuery"?: number; } + interface AtomicCommerceText { + /** + * The count value used for plurals. + */ + "count"?: number; + /** + * The string key value. + */ + "value": string; + } /** * The `atomic-component-error` is used by other components to return errors. This doesn't require any configuration. */ @@ -1524,6 +1534,16 @@ export namespace Components { */ "getTemplate": () => Promise | null>; } + interface AtomicProductText { + /** + * The locale key for the text to display when the configured field has no value. + */ + "default"?: string; + /** + * The product field which the component should use. This will look in the Product object first, and then in the product.additionalFields object for the fields. + */ + "field": string; + } /** * The `atomic-query-error` component handles fatal errors when performing a query on the index or Search API. When the error is known, it displays a link to relevant documentation link for debugging purposes. When the error is unknown, it displays a small text area with the JSON content of the error. */ @@ -3078,6 +3098,12 @@ declare global { prototype: HTMLAtomicCommerceSearchBoxRecentQueriesElement; new (): HTMLAtomicCommerceSearchBoxRecentQueriesElement; }; + interface HTMLAtomicCommerceTextElement extends Components.AtomicCommerceText, HTMLStencilElement { + } + var HTMLAtomicCommerceTextElement: { + prototype: HTMLAtomicCommerceTextElement; + new (): HTMLAtomicCommerceTextElement; + }; /** * The `atomic-component-error` is used by other components to return errors. This doesn't require any configuration. */ @@ -3727,6 +3753,12 @@ declare global { prototype: HTMLAtomicProductTemplateElement; new (): HTMLAtomicProductTemplateElement; }; + interface HTMLAtomicProductTextElement extends Components.AtomicProductText, HTMLStencilElement { + } + var HTMLAtomicProductTextElement: { + prototype: HTMLAtomicProductTextElement; + new (): HTMLAtomicProductTextElement; + }; /** * The `atomic-query-error` component handles fatal errors when performing a query on the index or Search API. When the error is known, it displays a link to relevant documentation link for debugging purposes. When the error is unknown, it displays a small text area with the JSON content of the error. */ @@ -4567,6 +4599,7 @@ declare global { "atomic-commerce-search-box": HTMLAtomicCommerceSearchBoxElement; "atomic-commerce-search-box-query-suggestions": HTMLAtomicCommerceSearchBoxQuerySuggestionsElement; "atomic-commerce-search-box-recent-queries": HTMLAtomicCommerceSearchBoxRecentQueriesElement; + "atomic-commerce-text": HTMLAtomicCommerceTextElement; "atomic-component-error": HTMLAtomicComponentErrorElement; "atomic-did-you-mean": HTMLAtomicDidYouMeanElement; "atomic-external": HTMLAtomicExternalElement; @@ -4637,6 +4670,7 @@ declare global { "atomic-product": HTMLAtomicProductElement; "atomic-product-link": HTMLAtomicProductLinkElement; "atomic-product-template": HTMLAtomicProductTemplateElement; + "atomic-product-text": HTMLAtomicProductTextElement; "atomic-query-error": HTMLAtomicQueryErrorElement; "atomic-query-summary": HTMLAtomicQuerySummaryElement; "atomic-quickview": HTMLAtomicQuickviewElement; @@ -5069,6 +5103,16 @@ declare namespace LocalJSX { */ "maxWithoutQuery"?: number; } + interface AtomicCommerceText { + /** + * The count value used for plurals. + */ + "count"?: number; + /** + * The string key value. + */ + "value": string; + } /** * The `atomic-component-error` is used by other components to return errors. This doesn't require any configuration. */ @@ -6115,6 +6159,16 @@ declare namespace LocalJSX { */ "conditions"?: ProductTemplateCondition[]; } + interface AtomicProductText { + /** + * The locale key for the text to display when the configured field has no value. + */ + "default"?: string; + /** + * The product field which the component should use. This will look in the Product object first, and then in the product.additionalFields object for the fields. + */ + "field": string; + } /** * The `atomic-query-error` component handles fatal errors when performing a query on the index or Search API. When the error is known, it displays a link to relevant documentation link for debugging purposes. When the error is unknown, it displays a small text area with the JSON content of the error. */ @@ -7395,6 +7449,7 @@ declare namespace LocalJSX { "atomic-commerce-search-box": AtomicCommerceSearchBox; "atomic-commerce-search-box-query-suggestions": AtomicCommerceSearchBoxQuerySuggestions; "atomic-commerce-search-box-recent-queries": AtomicCommerceSearchBoxRecentQueries; + "atomic-commerce-text": AtomicCommerceText; "atomic-component-error": AtomicComponentError; "atomic-did-you-mean": AtomicDidYouMean; "atomic-external": AtomicExternal; @@ -7465,6 +7520,7 @@ declare namespace LocalJSX { "atomic-product": AtomicProduct; "atomic-product-link": AtomicProductLink; "atomic-product-template": AtomicProductTemplate; + "atomic-product-text": AtomicProductText; "atomic-query-error": AtomicQueryError; "atomic-query-summary": AtomicQuerySummary; "atomic-quickview": AtomicQuickview; @@ -7598,6 +7654,7 @@ declare module "@stencil/core" { * The `atomic-commerce-search-box-recent-queries` component can be added as a child of an `atomic-commerce-search-box` component, allowing for the configuration of recent query suggestions. */ "atomic-commerce-search-box-recent-queries": LocalJSX.AtomicCommerceSearchBoxRecentQueries & JSXBase.HTMLAttributes; + "atomic-commerce-text": LocalJSX.AtomicCommerceText & JSXBase.HTMLAttributes; /** * The `atomic-component-error` is used by other components to return errors. This doesn't require any configuration. */ @@ -7764,6 +7821,7 @@ declare module "@stencil/core" { "atomic-product": LocalJSX.AtomicProduct & JSXBase.HTMLAttributes; "atomic-product-link": LocalJSX.AtomicProductLink & JSXBase.HTMLAttributes; "atomic-product-template": LocalJSX.AtomicProductTemplate & JSXBase.HTMLAttributes; + "atomic-product-text": LocalJSX.AtomicProductText & JSXBase.HTMLAttributes; /** * The `atomic-query-error` component handles fatal errors when performing a query on the index or Search API. When the error is known, it displays a link to relevant documentation link for debugging purposes. When the error is unknown, it displays a small text area with the JSON content of the error. */ diff --git a/packages/atomic/src/components/commerce/atomic-commerce-text/atomic-commerce-text.tsx b/packages/atomic/src/components/commerce/atomic-commerce-text/atomic-commerce-text.tsx new file mode 100644 index 00000000000..d76dd4c5f9a --- /dev/null +++ b/packages/atomic/src/components/commerce/atomic-commerce-text/atomic-commerce-text.tsx @@ -0,0 +1,47 @@ +import {Component, Prop, State} from '@stencil/core'; +import { + InitializableComponent, + InitializeBindings, +} from '../../../utils/initialization-utils'; +import {CommerceBindings} from '../atomic-commerce-interface/atomic-commerce-interface'; + +/** + * @internal + * The `atomic-commerce-text` component leverages the I18n translation module through the atomic-commerce-interface. + */ +@Component({ + tag: 'atomic-commerce-text', + shadow: true, +}) +export class AtomicCommerceText + implements InitializableComponent +{ + @InitializeBindings() public bindings!: CommerceBindings; + + private strings = { + value: () => + this.bindings.i18n.t(this.value, { + count: this.count, + }), + }; + @State() public error!: Error; + + /** + * The string key value. + */ + @Prop({reflect: true}) public value!: string; + /** + * The count value used for plurals. + */ + @Prop({reflect: true}) public count?: number; + + public connectedCallback() { + if (!this.value) { + this.error = new Error('The "value" attribute must be defined.'); + } + } + + public render() { + return this.strings.value(); + } +} diff --git a/packages/atomic/src/components/commerce/result-template-components/atomic-product-link/atomic-product-link.pcss b/packages/atomic/src/components/commerce/product-template-components/atomic-product-link/atomic-product-link.pcss similarity index 100% rename from packages/atomic/src/components/commerce/result-template-components/atomic-product-link/atomic-product-link.pcss rename to packages/atomic/src/components/commerce/product-template-components/atomic-product-link/atomic-product-link.pcss diff --git a/packages/atomic/src/components/commerce/result-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 similarity index 95% rename from packages/atomic/src/components/commerce/result-template-components/atomic-product-link/atomic-product-link.tsx rename to packages/atomic/src/components/commerce/product-template-components/atomic-product-link/atomic-product-link.tsx index 06229b78dcf..8940b8d719d 100644 --- a/packages/atomic/src/components/commerce/result-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 @@ -14,7 +14,7 @@ import {CommerceBindings} from '../../atomic-commerce-interface/atomic-commerce- import { InteractiveProductContext, ProductContext, -} from '../../product-template-components/product-template-decorators'; +} from '../product-template-decorators'; /** * @internal @@ -94,11 +94,10 @@ export class AtomicProductLink {this.hasDefaultSlot ? ( ) : ( - + > )} ); diff --git a/packages/atomic/src/components/commerce/product-template-components/atomic-product-text/atomic-product-text.tsx b/packages/atomic/src/components/commerce/product-template-components/atomic-product-text/atomic-product-text.tsx new file mode 100644 index 00000000000..a9ff0d7f8fa --- /dev/null +++ b/packages/atomic/src/components/commerce/product-template-components/atomic-product-text/atomic-product-text.tsx @@ -0,0 +1,92 @@ +import {Product, ProductTemplatesHelpers} from '@coveo/headless/commerce'; +import {Component, h, Prop, Element} from '@stencil/core'; +import {isArray} from 'lodash'; +import {getFieldValueCaption} from '../../../../utils/field-utils'; +import { + InitializableComponent, + InitializeBindings, +} from '../../../../utils/initialization-utils'; +import {CommerceBindings} from '../../atomic-commerce-interface/atomic-commerce-interface'; +import {ProductContext} from '../product-template-decorators'; +import {getStringValueFromProductOrNull} from '../product-utils'; + +/** + * @internal + * The `atomic-product-text` component renders the value of a string product field. + */ +@Component({ + tag: 'atomic-product-text', + shadow: false, +}) +export class AtomicProductText + implements InitializableComponent +{ + @InitializeBindings() public bindings!: CommerceBindings; + public error!: Error; + + @ProductContext() private product!: Product; + + @Element() private host!: HTMLElement; + + /** + * The product field which the component should use. + * This will look in the Product object first, and then in the product.additionalFields object for the fields. + */ + @Prop({reflect: true}) public field!: string; + /** + * The locale key for the text to display when the configured field has no value. + */ + @Prop({reflect: true}) public default?: string; + + private possiblyWarnOnBadFieldType() { + const productValueRaw = ProductTemplatesHelpers.getProductProperty( + this.product, + this.field + ); + if (isArray(productValueRaw)) { + this.bindings.engine.logger.error( + `atomic-product-text cannot be used with multi value field "${this.field}" with values "${productValueRaw}".`, + this + ); + } + } + + public render() { + const productValueAsString = getStringValueFromProductOrNull( + this.product, + this.field + ); + + if (!productValueAsString && !this.default) { + this.possiblyWarnOnBadFieldType(); + this.host.remove(); + return; + } + + if (!productValueAsString && this.default) { + this.possiblyWarnOnBadFieldType(); + return ( + + ); + } + + if (productValueAsString !== null) { + this.possiblyWarnOnBadFieldType(); + return ( + + ); + } + } +} 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 new file mode 100644 index 00000000000..fba0726ad90 --- /dev/null +++ b/packages/atomic/src/components/commerce/product-template-components/product-utils.ts @@ -0,0 +1,14 @@ +import {Product, ProductTemplatesHelpers} from '@coveo/headless/commerce'; + +export function getStringValueFromProductOrNull( + product: Product, + field: string +) { + const value = ProductTemplatesHelpers.getProductProperty(product, field); + + if (typeof value !== 'string' || value.trim() === '') { + return null; + } + + return value; +} diff --git a/packages/atomic/src/pages/examples/commerce-website/listing-pants.html b/packages/atomic/src/pages/examples/commerce-website/listing-pants.html index 0a8f42047d6..05e37307bfc 100644 --- a/packages/atomic/src/pages/examples/commerce-website/listing-pants.html +++ b/packages/atomic/src/pages/examples/commerce-website/listing-pants.html @@ -9,14 +9,16 @@ @@ -27,6 +29,7 @@

Product listing page

+ 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 43af2edbf41..f3952a66276 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 @@ -9,14 +9,16 @@ @@ -37,7 +39,7 @@

Product listing page

- + @@ -49,7 +51,7 @@

Product listing page

- + diff --git a/packages/atomic/src/pages/examples/commerce-website/listing-towels.html b/packages/atomic/src/pages/examples/commerce-website/listing-towels.html index 27545e9c233..dfad3cab86c 100644 --- a/packages/atomic/src/pages/examples/commerce-website/listing-towels.html +++ b/packages/atomic/src/pages/examples/commerce-website/listing-towels.html @@ -9,14 +9,16 @@ @@ -37,7 +39,7 @@

Product listing page

- + @@ -49,7 +51,7 @@

Product listing page

- + diff --git a/packages/atomic/src/pages/examples/commerce-website/search.html b/packages/atomic/src/pages/examples/commerce-website/search.html index 93484e6eaa5..ac7101c3643 100644 --- a/packages/atomic/src/pages/examples/commerce-website/search.html +++ b/packages/atomic/src/pages/examples/commerce-website/search.html @@ -47,7 +47,7 @@

Search page

- + @@ -59,7 +59,7 @@

Search page

- +