diff --git a/packages/template-retail-react-app/app/components/_app/index.jsx b/packages/template-retail-react-app/app/components/_app/index.jsx
index 429dc7c24c..4046ede86f 100644
--- a/packages/template-retail-react-app/app/components/_app/index.jsx
+++ b/packages/template-retail-react-app/app/components/_app/index.jsx
@@ -57,7 +57,7 @@ import {useCurrentBasket} from '@salesforce/retail-react-app/app/hooks/use-curre
import {withCommerceSdkReact} from '@salesforce/retail-react-app/app/components/with-commerce-sdk-react/with-commerce-sdk-react'
// Localization
-import {FormattedNumber, IntlProvider} from 'react-intl'
+import {IntlProvider} from 'react-intl'
// Others
import {watchOnlineStatus, flatten, isServer} from '@salesforce/retail-react-app/app/utils/utils'
diff --git a/packages/template-retail-react-app/app/components/display-price/index.jsx b/packages/template-retail-react-app/app/components/display-price/index.jsx
index f94b3c856c..960ff004e7 100644
--- a/packages/template-retail-react-app/app/components/display-price/index.jsx
+++ b/packages/template-retail-react-app/app/components/display-price/index.jsx
@@ -66,7 +66,7 @@ const DisplayPrice = ({priceData, currency}) => {
other {}
}
{isASet, select,
- true {From {salePrice}}
+ true {From {salePrice}}
false
{
{
diff --git a/packages/template-retail-react-app/app/components/display-price/index.test.js b/packages/template-retail-react-app/app/components/display-price/index.test.js
index 336591a695..08c45711bc 100644
--- a/packages/template-retail-react-app/app/components/display-price/index.test.js
+++ b/packages/template-retail-react-app/app/components/display-price/index.test.js
@@ -29,9 +29,10 @@ describe('DisplayPrice', function () {
const {container} = renderWithProviders()
const currentPriceTag = container.querySelectorAll('b')
const strikethroughPriceTag = container.querySelectorAll('s')
- expect(within(currentPriceTag[0]).getByText(/£90\.00/i)).toBeDefined()
+ // From and salePrice are in two separate b tags
+ expect(within(currentPriceTag[1]).getByText(/£90\.00/i)).toBeDefined()
expect(within(strikethroughPriceTag[0]).getByText(/£100\.00/i)).toBeDefined()
- expect(currentPriceTag).toHaveLength(1)
+ expect(currentPriceTag).toHaveLength(2)
expect(strikethroughPriceTag).toHaveLength(1)
})
diff --git a/packages/template-retail-react-app/app/components/product-tile/index.jsx b/packages/template-retail-react-app/app/components/product-tile/index.jsx
index 64c168c4b4..953b38e97c 100644
--- a/packages/template-retail-react-app/app/components/product-tile/index.jsx
+++ b/packages/template-retail-react-app/app/components/product-tile/index.jsx
@@ -5,7 +5,7 @@
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
-import React, {useMemo, useRef} from 'react'
+import React, {useRef} from 'react'
import PropTypes from 'prop-types'
import {HeartIcon, HeartSolidIcon} from '@salesforce/retail-react-app/app/components/icons'
import DisplayPrice from '@salesforce/retail-react-app/app/components/display-price'
@@ -26,12 +26,11 @@ import DynamicImage from '@salesforce/retail-react-app/app/components/dynamic-im
import {useIntl} from 'react-intl'
// Other
-import {productUrlBuilder, rebuildPathWithParams} from '@salesforce/retail-react-app/app/utils/url'
+import {productUrlBuilder} from '@salesforce/retail-react-app/app/utils/url'
import Link from '@salesforce/retail-react-app/app/components/link'
import withRegistration from '@salesforce/retail-react-app/app/components/with-registration'
import {getPriceData} from '@salesforce/retail-react-app/app/utils/product-utils'
import {useCurrency} from '@salesforce/retail-react-app/app/hooks'
-import {PRICE_DISPLAY_FORMAT} from '@salesforce/retail-react-app/app/constants'
const IconButtonWithRegistration = withRegistration(IconButton)
diff --git a/packages/template-retail-react-app/app/components/product-tile/index.test.js b/packages/template-retail-react-app/app/components/product-tile/index.test.js
index 0b4a1df8b4..6398f2973b 100644
--- a/packages/template-retail-react-app/app/components/product-tile/index.test.js
+++ b/packages/template-retail-react-app/app/components/product-tile/index.test.js
@@ -9,6 +9,7 @@ import ProductTile, {Skeleton} from '@salesforce/retail-react-app/app/components
import {renderWithProviders} from '@salesforce/retail-react-app/app/utils/test-utils'
import {fireEvent, within} from '@testing-library/react'
import {
+ mockMasterProductHitWithMultipleVariants,
mockMasterProductHitWithOneVariant,
mockProductSearchItem,
mockProductSetHit,
@@ -50,29 +51,30 @@ test('Remove from wishlist cannot be muti-clicked', () => {
expect(onClick).toHaveBeenCalledTimes(1)
})
-test('renders exact price with strikethrough price for master product can be filtered down to one variant ', () => {
- const {getByText, container} = renderWithProviders(
-
+test('renders exact price with strikethrough price for master product can be has various variants', () => {
+ const {queryByText, getByText, container} = renderWithProviders(
+
)
- expect(getByText(/black flat front wool suit/i)).toBeInTheDocument()
- expect(getByText(/£191\.99/i)).toBeInTheDocument()
- expect(getByText(/£320\.00/i)).toBeInTheDocument()
+ expect(getByText(/Black Single Pleat Athletic Fit Wool Suit - Edit/i)).toBeInTheDocument()
+ expect(queryByText(/from/i)).toBeInTheDocument()
const salePriceTag = container.querySelectorAll('b')
const strikethroughPriceTag = container.querySelectorAll('s')
- expect(within(salePriceTag[0]).getByText(/£191\.99/i)).toBeDefined()
- expect(within(strikethroughPriceTag[0]).getByText(/£320\.00/i)).toBeDefined()
- expect(salePriceTag).toHaveLength(1)
+ expect(within(salePriceTag[1]).getByText(/£191\.99/i)).toBeDefined()
+ expect(within(strikethroughPriceTag[0]).getByText(/£223\.99/i)).toBeDefined()
+ // From and price are in separate b tag
+ expect(salePriceTag).toHaveLength(2)
expect(strikethroughPriceTag).toHaveLength(1)
})
-test('renders exact price with strikethrough price for master product can be filtered down to one variant ', () => {
- const {getByText, container} = renderWithProviders(
+test('renders exact price with strikethrough price for master product can be filtered down to one variant', () => {
+ const {getByText, queryByText, container} = renderWithProviders(
)
expect(getByText(/black flat front wool suit/i)).toBeInTheDocument()
expect(getByText(/£191\.99/i)).toBeInTheDocument()
expect(getByText(/£320\.00/i)).toBeInTheDocument()
+ expect(queryByText(/from/i)).not.toBeInTheDocument()
const salePriceTag = container.querySelectorAll('b')
const strikethroughPriceTag = container.querySelectorAll('s')
@@ -82,13 +84,13 @@ test('renders exact price with strikethrough price for master product can be fil
expect(strikethroughPriceTag).toHaveLength(1)
})
-test('Product set - does not render strike through price', () => {
- const {getByText, queryByText, container} = renderWithProviders(
+test('Product set - shows range From X where X is the lowest price child', () => {
+ const {getByText, queryByText} = renderWithProviders(
)
expect(getByText(/Winter Look/i)).toBeInTheDocument()
- expect(queryByText(/from/i)).not.toBeInTheDocument()
- expect(queryByText(/£40\.16/i)).not.toBeInTheDocument()
+ expect(queryByText(/from/i)).toBeInTheDocument()
+ expect(queryByText(/£40\.16/i)).toBeInTheDocument()
expect(queryByText(/£44\.16/i)).not.toBeInTheDocument()
})
diff --git a/packages/template-retail-react-app/app/components/product-view/index.jsx b/packages/template-retail-react-app/app/components/product-view/index.jsx
index bb27225950..e12107f837 100644
--- a/packages/template-retail-react-app/app/components/product-view/index.jsx
+++ b/packages/template-retail-react-app/app/components/product-view/index.jsx
@@ -63,7 +63,8 @@ const ProductViewHeader = ({name, currency, priceData, category}) => {
ProductViewHeader.propTypes = {
name: PropTypes.string,
currency: PropTypes.string,
- category: PropTypes.array
+ category: PropTypes.array,
+ priceData: PropTypes.object
}
const ButtonWithRegistration = withRegistration(Button)
diff --git a/packages/template-retail-react-app/app/components/product-view/index.test.js b/packages/template-retail-react-app/app/components/product-view/index.test.js
index f9ab13682f..e13c583c94 100644
--- a/packages/template-retail-react-app/app/components/product-view/index.test.js
+++ b/packages/template-retail-react-app/app/components/product-view/index.test.js
@@ -174,8 +174,9 @@ test('renders a product set properly - child item', () => {
expect(variationAttributes).toHaveLength(2)
expect(quantityPicker).toBeInTheDocument()
- // What should _not_ exist:
- expect(fromLabels).toHaveLength(0)
+ // since setProducts are master products, as pricing now display From X (cross) Y where X Y are sale and lis price respectively
+ // of the variant that has lowest price (including promotional price)
+ expect(fromLabels).toHaveLength(2)
})
test('validateOrderability callback is called when adding a set to cart', async () => {
diff --git a/packages/template-retail-react-app/app/mocks/product-search-hit-data.js b/packages/template-retail-react-app/app/mocks/product-search-hit-data.js
index 6984cb7437..9970e08998 100644
--- a/packages/template-retail-react-app/app/mocks/product-search-hit-data.js
+++ b/packages/template-retail-react-app/app/mocks/product-search-hit-data.js
@@ -1,3 +1,10 @@
+/*
+ * Copyright (c) 2024, salesforce.com, inc.
+ * All rights reserved.
+ * SPDX-License-Identifier: BSD-3-Clause
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
+ */
+
const mockProductSearchItem = {
currency: 'USD',
image: {
diff --git a/packages/template-retail-react-app/app/static/translations/compiled/en-GB.json b/packages/template-retail-react-app/app/static/translations/compiled/en-GB.json
index 6b9d746054..77e69087e3 100644
--- a/packages/template-retail-react-app/app/static/translations/compiled/en-GB.json
+++ b/packages/template-retail-react-app/app/static/translations/compiled/en-GB.json
@@ -2815,8 +2815,14 @@
{
"children": [
{
- "type": 1,
- "value": "salePrice"
+ "children": [
+ {
+ "type": 1,
+ "value": "salePrice"
+ }
+ ],
+ "type": 8,
+ "value": "price"
}
],
"type": 8,
diff --git a/packages/template-retail-react-app/app/static/translations/compiled/en-US.json b/packages/template-retail-react-app/app/static/translations/compiled/en-US.json
index 6b9d746054..77e69087e3 100644
--- a/packages/template-retail-react-app/app/static/translations/compiled/en-US.json
+++ b/packages/template-retail-react-app/app/static/translations/compiled/en-US.json
@@ -2815,8 +2815,14 @@
{
"children": [
{
- "type": 1,
- "value": "salePrice"
+ "children": [
+ {
+ "type": 1,
+ "value": "salePrice"
+ }
+ ],
+ "type": 8,
+ "value": "price"
}
],
"type": 8,
diff --git a/packages/template-retail-react-app/app/static/translations/compiled/en-XA.json b/packages/template-retail-react-app/app/static/translations/compiled/en-XA.json
index bd99089899..382f633a41 100644
--- a/packages/template-retail-react-app/app/static/translations/compiled/en-XA.json
+++ b/packages/template-retail-react-app/app/static/translations/compiled/en-XA.json
@@ -5627,8 +5627,14 @@
{
"children": [
{
- "type": 1,
- "value": "salePrice"
+ "children": [
+ {
+ "type": 1,
+ "value": "salePrice"
+ }
+ ],
+ "type": 8,
+ "value": "price"
}
],
"type": 8,
diff --git a/packages/template-retail-react-app/app/utils/test-utils.js b/packages/template-retail-react-app/app/utils/test-utils.js
index d9e9cabfd6..ba4c9ccf33 100644
--- a/packages/template-retail-react-app/app/utils/test-utils.js
+++ b/packages/template-retail-react-app/app/utils/test-utils.js
@@ -26,7 +26,6 @@ import {createUrlTemplate} from '@salesforce/retail-react-app/app/utils/url'
import {getSiteByReference} from '@salesforce/retail-react-app/app/utils/site-utils'
import jwt from 'jsonwebtoken'
import userEvent from '@testing-library/user-event'
-import {Text} from '@chakra-ui/react'
// This JWT's payload is special
// it includes 3 fields that commerce-sdk-react cares:
// exp, isb and sub
diff --git a/packages/template-retail-react-app/app/utils/utils.test.js b/packages/template-retail-react-app/app/utils/utils.test.js
index 76840ff6d0..f9388477b7 100644
--- a/packages/template-retail-react-app/app/utils/utils.test.js
+++ b/packages/template-retail-react-app/app/utils/utils.test.js
@@ -198,7 +198,7 @@ describe('getSmallestValByKey', function () {
}
]
const val = getSmallestValByProperty(data, 'price')
- expect(val).toEqual(9)
+ expect(val).toBe(9)
})
test('should undefined if array is not passed in', () => {
const data = {
diff --git a/packages/template-retail-react-app/translations/en-GB.json b/packages/template-retail-react-app/translations/en-GB.json
index 74ed825ced..42282a53a8 100644
--- a/packages/template-retail-react-app/translations/en-GB.json
+++ b/packages/template-retail-react-app/translations/en-GB.json
@@ -1067,7 +1067,7 @@
"defaultMessage": "Remove {product} from wishlist"
},
"product_tile.price_display": {
- "defaultMessage": "{isMaster, select, true { { isRange, select, true { { isOnSale, select, true {From} fales { { hasRepresentedProduct, select, true {From} false {From} other {} } } other {From} } } false {} other {} } } false {} other {} } {isASet, select, true {From {salePrice}} false { { isOnSale, select, true { {salePrice} {listPrice} } false { { hasRepresentedProduct, select, true {{salePrice}} false {{salePrice}} other {{salePrice}} } } other {{salePrice}} } } other {} }"
+ "defaultMessage": "{isMaster, select, true { { isRange, select, true { { isOnSale, select, true {From} fales { { hasRepresentedProduct, select, true {From} false {From} other {} } } other {From} } } false {} other {} } } false {} other {} } {isASet, select, true {From {salePrice}} false { { isOnSale, select, true { {salePrice} {listPrice} } false { { hasRepresentedProduct, select, true {{salePrice}} false {{salePrice}} other {{salePrice}} } } other {{salePrice}} } } other {} }"
},
"product_view.button.add_set_to_cart": {
"defaultMessage": "Add Set to Cart"
diff --git a/packages/template-retail-react-app/translations/en-US.json b/packages/template-retail-react-app/translations/en-US.json
index 74ed825ced..42282a53a8 100644
--- a/packages/template-retail-react-app/translations/en-US.json
+++ b/packages/template-retail-react-app/translations/en-US.json
@@ -1067,7 +1067,7 @@
"defaultMessage": "Remove {product} from wishlist"
},
"product_tile.price_display": {
- "defaultMessage": "{isMaster, select, true { { isRange, select, true { { isOnSale, select, true {From} fales { { hasRepresentedProduct, select, true {From} false {From} other {} } } other {From} } } false {} other {} } } false {} other {} } {isASet, select, true {From {salePrice}} false { { isOnSale, select, true { {salePrice} {listPrice} } false { { hasRepresentedProduct, select, true {{salePrice}} false {{salePrice}} other {{salePrice}} } } other {{salePrice}} } } other {} }"
+ "defaultMessage": "{isMaster, select, true { { isRange, select, true { { isOnSale, select, true {From} fales { { hasRepresentedProduct, select, true {From} false {From} other {} } } other {From} } } false {} other {} } } false {} other {} } {isASet, select, true {From {salePrice}} false { { isOnSale, select, true { {salePrice} {listPrice} } false { { hasRepresentedProduct, select, true {{salePrice}} false {{salePrice}} other {{salePrice}} } } other {{salePrice}} } } other {} }"
},
"product_view.button.add_set_to_cart": {
"defaultMessage": "Add Set to Cart"