diff --git a/packages/template-retail-react-app/app/components/item-variant/item-price.jsx b/packages/template-retail-react-app/app/components/item-variant/item-price.jsx index 8507622c42..62f83d6d2e 100644 --- a/packages/template-retail-react-app/app/components/item-variant/item-price.jsx +++ b/packages/template-retail-react-app/app/components/item-variant/item-price.jsx @@ -78,7 +78,7 @@ const ItemPrice = ({currency, align = 'right', baseDirection = 'column', ...prop {hasDiscount && ( diff --git a/packages/template-retail-react-app/app/pages/account/wishlist/index.jsx b/packages/template-retail-react-app/app/pages/account/wishlist/index.jsx index fe643db51d..46d481c22c 100644 --- a/packages/template-retail-react-app/app/pages/account/wishlist/index.jsx +++ b/packages/template-retail-react-app/app/pages/account/wishlist/index.jsx @@ -4,15 +4,15 @@ * 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 */ -import React, {useEffect, useState} from 'react' +import React, {useState} from 'react' import {Stack, Heading} from '@chakra-ui/layout' import {FormattedMessage, useIntl} from 'react-intl' import {Box, Flex, Skeleton} from '@chakra-ui/react' +import {useProducts, useShopperCustomersMutation} from 'commerce-sdk-react-preview' -import useCustomer from '../../../commerce-api/hooks/useCustomer' import useNavigation from '../../../hooks/use-navigation' -import useWishlist from '../../../hooks/use-wishlist' import {useToast} from '../../../hooks/use-toast' +import {useWishList} from '../../../hooks/use-wish-list' import PageActionPlaceHolder from '../../../components/page-action-placeholder' import {HeartIcon} from '../../../components/icons' @@ -21,69 +21,101 @@ import WishlistPrimaryAction from './partials/wishlist-primary-action' import WishlistSecondaryButtonGroup from './partials/wishlist-secondary-button-group' import {API_ERROR_MESSAGE} from '../../../constants' +import {useCurrentCustomer} from '../../../hooks/use-current-customer' const numberOfSkeletonItems = 3 const AccountWishlist = () => { - const customer = useCustomer() const navigate = useNavigation() const {formatMessage} = useIntl() const toast = useToast() + const [selectedItem, setSelectedItem] = useState(undefined) - const [localQuantity, setLocalQuantity] = useState({}) const [isWishlistItemLoading, setWishlistItemLoading] = useState(false) - const wishlist = useWishlist() - const handleActionClicked = (itemId) => { - setWishlistItemLoading(!!itemId) + const {data: wishListData, isLoading: isWishListLoading} = useWishList() + const productIds = wishListData?.customerProductListItems?.map((item) => item.productId) + + const {data: productsData, isLoading: isProductsLoading} = useProducts( + {parameters: {ids: productIds?.join(','), allImages: true}}, + {enabled: productIds?.length > 0} + ) + + const wishListItems = wishListData?.customerProductListItems?.map((item, i) => { + return { + ...item, + product: productsData?.data?.[i] + } + }) + + const updateCustomerProductListItem = useShopperCustomersMutation( + 'updateCustomerProductListItem' + ) + const deleteCustomerProductListItem = useShopperCustomersMutation( + 'deleteCustomerProductListItem' + ) + const {data: customer} = useCurrentCustomer() + + const handleSecondaryAction = async (itemId, promise) => { + setWishlistItemLoading(true) setSelectedItem(itemId) + + try { + await promise + // No need to handle error here, as the inner component will take care of it + } finally { + setWishlistItemLoading(false) + setSelectedItem(undefined) + } } const handleItemQuantityChanged = async (quantity, item) => { - // This local state allows the dropdown to show the desired quantity - // while the API call to update it is happening. - setLocalQuantity({...localQuantity, [item.productId]: quantity}) - setWishlistItemLoading(true) + let isValidChange = false setSelectedItem(item.productId) + + const body = { + ...item, + quantity: parseInt(quantity) + } + // To meet expected schema, remove the custom `product` we added + delete body.product + + const parameters = { + customerId: customer.customerId, + itemId: item.id, + listId: wishListData?.id + } + + const mutation = + parseInt(quantity) > 0 + ? updateCustomerProductListItem.mutateAsync({body, parameters}) + : deleteCustomerProductListItem.mutateAsync({parameters}) + try { - await wishlist.updateListItem({ - ...item, - quantity: parseInt(quantity) - }) - } catch { + await mutation + isValidChange = true + setSelectedItem(undefined) + } catch (err) { toast({ title: formatMessage(API_ERROR_MESSAGE), status: 'error' }) } - setWishlistItemLoading(false) - setSelectedItem(undefined) - setLocalQuantity({...localQuantity, [item.productId]: undefined}) + + // If true, the quantity picker would immediately update its number + // without waiting for the invalidated lists data to finish refetching + return isValidChange } - useEffect(() => { - if (customer.isRegistered) { - // We want to reset the wishlist here - // because it is possible that a user - // adds an item to the wishlist on another page - // and the wishlist page may not have enough - // data to render the page. - // Reset the wishlist will make sure the - // initialization state is correct. - if (wishlist.isInitialized) { - wishlist.reset() - } - - wishlist.init({detail: true}) - } - }, [customer.isRegistered]) + const isPageLoading = wishListItems ? isProductsLoading : isWishListLoading return ( - {!wishlist.hasDetail && ( + + {isPageLoading && ( {new Array(numberOfSkeletonItems).fill(0).map((i, idx) => ( { )} - {wishlist.hasDetail && wishlist.isEmpty && ( + {!isPageLoading && !wishListItems && ( } @@ -129,18 +161,21 @@ const AccountWishlist = () => { /> )} - {wishlist.hasDetail && - !wishlist.isEmpty && - wishlist.items.map((item) => ( + {!isPageLoading && + wishListItems && + wishListItems.map((item) => ( } onItemQuantityChange={(quantity) => handleItemQuantityChanged(quantity, item) @@ -148,7 +183,7 @@ const AccountWishlist = () => { secondaryActions={ } /> diff --git a/packages/template-retail-react-app/app/pages/account/wishlist/index.mock.js b/packages/template-retail-react-app/app/pages/account/wishlist/index.mock.js new file mode 100644 index 0000000000..791cc1786e --- /dev/null +++ b/packages/template-retail-react-app/app/pages/account/wishlist/index.mock.js @@ -0,0 +1,1481 @@ +/* + * Copyright (c) 2023, Salesforce, 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 + */ + +export const mockedEmptyWishList = { + limit: 1, + data: [ + { + creationDate: '2023-03-07T19:57:28.000Z', + event: {}, + id: 'e41812c0545ec3cafcbae8c6e8', + lastModified: '2023-03-07T19:57:28.000Z', + public: false, + type: 'wish_list' + } + ], + total: 1 +} + +export const mockedProductLists = { + limit: 1, + data: [ + { + creationDate: '2023-01-20T21:23:38.000Z', + customerProductListItems: [ + { + id: '2790d16db84d81f1280d1fcf53', + priority: 1, + productId: 'P0150M', + public: false, + purchasedQuantity: 0, + quantity: 13, + type: 'product' + }, + { + id: '6f29c03c299b52d1acb3a9d4fd', + priority: 1, + productId: 'fall-lookM', + public: false, + purchasedQuantity: 0, + quantity: 18, + type: 'product' + }, + { + id: '5fb9c25457924db4da03e1db7a', + priority: 1, + productId: 'Spring-look-2M', + public: false, + purchasedQuantity: 0, + quantity: 6, + type: 'product' + }, + { + id: 'd6a2ebfd81275d58c53ac5138f', + priority: 1, + productId: '25518009M', + public: false, + purchasedQuantity: 0, + quantity: 1, + type: 'product' + }, + { + id: 'e2777b597ce49264c51bf4ad15', + priority: 1, + productId: 'P0048M', + public: false, + purchasedQuantity: 0, + quantity: 1, + type: 'product' + }, + { + id: '2bbf46c9459da726b9bb2d1f68', + priority: 1, + productId: '25565826M', + public: false, + purchasedQuantity: 0, + quantity: 1, + type: 'product' + }, + { + id: '9863569ead5c16ff2aee5f2e32', + priority: 1, + productId: '701642819462M', + public: false, + purchasedQuantity: 0, + quantity: 1, + type: 'product' + } + ], + event: {}, + id: '1fb1fa64b0ecd15d9e9321ab5c', + lastModified: '2023-03-10T21:50:40.766Z', + name: 'PWA wishlist', + public: false, + type: 'wish_list' + } + ], + total: 1 +} + +export const mockedWishListProducts = { + limit: 7, + data: [ + { + currency: 'GBP', + id: 'P0150M', + imageGroups: [ + { + images: [ + { + alt: 'Upright Case (33L - 3.7Kg), , large', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw7e542522/images/large/P0150_001.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw7e542522/images/large/P0150_001.jpg', + title: 'Upright Case (33L - 3.7Kg), ' + } + ], + viewType: 'large' + }, + { + images: [ + { + alt: 'Upright Case (33L - 3.7Kg), , medium', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9261a750/images/medium/P0150_001.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9261a750/images/medium/P0150_001.jpg', + title: 'Upright Case (33L - 3.7Kg), ' + } + ], + viewType: 'medium' + }, + { + images: [ + { + alt: 'Upright Case (33L - 3.7Kg), , small', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw01bfd69a/images/small/P0150_001.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw01bfd69a/images/small/P0150_001.jpg', + title: 'Upright Case (33L - 3.7Kg), ' + } + ], + viewType: 'small' + } + ], + inventory: { + ats: 2, + backorderable: false, + id: 'inventory_m', + orderable: true, + preorderable: false, + stockLevel: 2 + }, + longDescription: + '1682 ballistic nylon and genuine leather inserts |Pull-out metallic handle for wheeling|Top and side handles|Cabin size for convenient travelling|TSA lock for security', + minOrderQuantity: 1, + name: 'Upright Case (33L - 3.7Kg)', + pageDescription: + 'This practical case is perfect for business – with no need to check in as luggage due to its cabin size dimensions – or for any no-fuss travel.', + pageKeywords: + 'Commerce Cloud, P0150, T10 150 Upright 50, General Accessories, Accessories, Packs and Gear', + pageTitle: 'Commerce Cloud - Upright Case (33L - 3.7Kg)', + price: 63.99, + pricePerUnit: 63.99, + primaryCategoryId: 'mens-accessories-luggage', + shortDescription: + 'This practical and functional case is perfect for business – with no need to check in as luggage due to its cabin size dimensions – or for any convenient no-fuss travel any time any where. You can pull along for comfort or carry by the handle, and with plenty of space inside and a large front pocket with additional zippered pocket, there’s plenty of useful and compact storage.', + slugUrl: + 'https://zzrf-001.dx.commercecloud.salesforce.com/s/RefArchGlobal/upright-case-33l-3.7kg/P0150M.html?lang=default', + stepQuantity: 1, + type: { + item: true + }, + c_styleNumber: 'P0150', + c_tabDescription: + 'This practical and functional case is perfect for business – with no need to check in as luggage due to its cabin size dimensions – or for any convenient no-fuss travel any time any where. You can pull along for comfort or carry by the handle, and with plenty of space inside and a large front pocket with additional zippered pocket, there’s plenty of useful and compact storage.', + c_tabDetails: + '1682 ballistic nylon and genuine leather inserts |Pull-out metallic handle for wheeling|Top and side handles|Cabin size for convenient travelling|TSA lock for security' + }, + { + currency: 'GBP', + id: 'fall-lookM', + imageGroups: [ + { + images: [ + { + alt: 'Fall Look, , large', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa54c4e5d/images/large/PG.10241411.JJZ01XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa54c4e5d/images/large/PG.10241411.JJZ01XX.PZ.jpg', + title: 'Fall Look, ' + } + ], + viewType: 'large' + }, + { + images: [ + { + alt: 'Fall Look, , medium', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbb906a14/images/medium/PG.10241411.JJZ01XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbb906a14/images/medium/PG.10241411.JJZ01XX.PZ.jpg', + title: 'Fall Look, ' + } + ], + viewType: 'medium' + }, + { + images: [ + { + alt: 'Fall Look, , small', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5539afa2/images/small/PG.10241411.JJZ01XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5539afa2/images/small/PG.10241411.JJZ01XX.PZ.jpg', + title: 'Fall Look, ' + } + ], + viewType: 'small' + } + ], + longDescription: 'Fall Look', + minOrderQuantity: 1, + name: 'Fall Look', + price: 23.04, + priceMax: 56.96, + pricePerUnit: 23.04, + pricePerUnitMax: 56.96, + primaryCategoryId: 'womens-outfits', + shortDescription: 'Fall Look', + slugUrl: + 'https://zzrf-001.dx.commercecloud.salesforce.com/s/RefArchGlobal/womens/clothing/outfits/fall-lookM.html?lang=default', + stepQuantity: 1, + type: { + set: true + } + }, + { + currency: 'GBP', + id: 'Spring-look-2M', + imageGroups: [ + { + images: [ + { + alt: 'Spring Has Sprung, , large', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdbf2acb6/images/large/PG.10236198.JJ3KRXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdbf2acb6/images/large/PG.10236198.JJ3KRXX.PZ.jpg', + title: 'Spring Has Sprung, ' + } + ], + viewType: 'large' + }, + { + images: [ + { + alt: 'Spring Has Sprung, , medium', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwfc2b10cb/images/medium/PG.10236198.JJ3KRXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwfc2b10cb/images/medium/PG.10236198.JJ3KRXX.PZ.jpg', + title: 'Spring Has Sprung, ' + } + ], + viewType: 'medium' + }, + { + images: [ + { + alt: 'Spring Has Sprung, , small', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbbb4da09/images/small/PG.10236198.JJ3KRXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbbb4da09/images/small/PG.10236198.JJ3KRXX.BZ.jpg', + title: 'Spring Has Sprung, ' + } + ], + viewType: 'small' + } + ], + longDescription: 'Top\r\nJeans\r\nNecklace\r\nShoes', + minOrderQuantity: 1, + name: 'Spring Has Sprung', + price: 24.96, + priceMax: 70.4, + pricePerUnit: 24.96, + pricePerUnitMax: 70.4, + primaryCategoryId: 'womens-outfits', + shortDescription: 'Cool styles for Spring', + slugUrl: + 'https://zzrf-001.dx.commercecloud.salesforce.com/s/RefArchGlobal/womens/clothing/outfits/Spring-look-2M.html?lang=default', + stepQuantity: 1, + type: { + set: true + } + }, + { + currency: 'GBP', + id: '25518009M', + imageGroups: [ + { + images: [ + { + alt: 'Extend Tab Straight Leg Pant, , large', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbde9a921/images/large/PG.10211049.JJ9LMXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbde9a921/images/large/PG.10211049.JJ9LMXX.PZ.jpg', + title: 'Extend Tab Straight Leg Pant, ' + }, + { + alt: 'Extend Tab Straight Leg Pant, , large', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3b2c7f13/images/large/PG.10211049.JJ9LMXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3b2c7f13/images/large/PG.10211049.JJ9LMXX.BZ.jpg', + title: 'Extend Tab Straight Leg Pant, ' + } + ], + viewType: 'large' + }, + { + images: [ + { + alt: 'Extend Tab Straight Leg Pant, Marine Wash, large', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbde9a921/images/large/PG.10211049.JJ9LMXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbde9a921/images/large/PG.10211049.JJ9LMXX.PZ.jpg', + title: 'Extend Tab Straight Leg Pant, Marine Wash' + }, + { + alt: 'Extend Tab Straight Leg Pant, Marine Wash, large', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3b2c7f13/images/large/PG.10211049.JJ9LMXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3b2c7f13/images/large/PG.10211049.JJ9LMXX.BZ.jpg', + title: 'Extend Tab Straight Leg Pant, Marine Wash' + } + ], + variationAttributes: [ + { + id: 'color', + values: [ + { + value: 'JJ9LMXX' + } + ] + } + ], + viewType: 'large' + }, + { + images: [ + { + alt: 'Extend Tab Straight Leg Pant, , medium', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf852acb0/images/medium/PG.10211049.JJ9LMXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf852acb0/images/medium/PG.10211049.JJ9LMXX.PZ.jpg', + title: 'Extend Tab Straight Leg Pant, ' + }, + { + alt: 'Extend Tab Straight Leg Pant, , medium', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw28eac377/images/medium/PG.10211049.JJ9LMXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw28eac377/images/medium/PG.10211049.JJ9LMXX.BZ.jpg', + title: 'Extend Tab Straight Leg Pant, ' + } + ], + viewType: 'medium' + }, + { + images: [ + { + alt: 'Extend Tab Straight Leg Pant, Marine Wash, medium', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf852acb0/images/medium/PG.10211049.JJ9LMXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf852acb0/images/medium/PG.10211049.JJ9LMXX.PZ.jpg', + title: 'Extend Tab Straight Leg Pant, Marine Wash' + }, + { + alt: 'Extend Tab Straight Leg Pant, Marine Wash, medium', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw28eac377/images/medium/PG.10211049.JJ9LMXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw28eac377/images/medium/PG.10211049.JJ9LMXX.BZ.jpg', + title: 'Extend Tab Straight Leg Pant, Marine Wash' + } + ], + variationAttributes: [ + { + id: 'color', + values: [ + { + value: 'JJ9LMXX' + } + ] + } + ], + viewType: 'medium' + }, + { + images: [ + { + alt: 'Extend Tab Straight Leg Pant, , small', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf5169852/images/small/PG.10211049.JJ9LMXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf5169852/images/small/PG.10211049.JJ9LMXX.PZ.jpg', + title: 'Extend Tab Straight Leg Pant, ' + }, + { + alt: 'Extend Tab Straight Leg Pant, , small', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw08ed3309/images/small/PG.10211049.JJ9LMXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw08ed3309/images/small/PG.10211049.JJ9LMXX.BZ.jpg', + title: 'Extend Tab Straight Leg Pant, ' + } + ], + viewType: 'small' + }, + { + images: [ + { + alt: 'Extend Tab Straight Leg Pant, Marine Wash, small', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf5169852/images/small/PG.10211049.JJ9LMXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf5169852/images/small/PG.10211049.JJ9LMXX.PZ.jpg', + title: 'Extend Tab Straight Leg Pant, Marine Wash' + }, + { + alt: 'Extend Tab Straight Leg Pant, Marine Wash, small', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw08ed3309/images/small/PG.10211049.JJ9LMXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw08ed3309/images/small/PG.10211049.JJ9LMXX.BZ.jpg', + title: 'Extend Tab Straight Leg Pant, Marine Wash' + } + ], + variationAttributes: [ + { + id: 'color', + values: [ + { + value: 'JJ9LMXX' + } + ] + } + ], + viewType: 'small' + }, + { + images: [ + { + alt: 'Extend Tab Straight Leg Pant, Marine Wash, swatch', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5314a78e/images/swatch/PG.10211049.JJ9LMXX.CP.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5314a78e/images/swatch/PG.10211049.JJ9LMXX.CP.jpg', + title: 'Extend Tab Straight Leg Pant, Marine Wash' + } + ], + variationAttributes: [ + { + id: 'color', + values: [ + { + value: 'JJ9LMXX' + } + ] + } + ], + viewType: 'swatch' + } + ], + inventory: { + ats: 700, + backorderable: false, + id: 'inventory_m', + orderable: true, + preorderable: false, + stockLevel: 700 + }, + longDescription: + 'Classically designed, this bootleg jean is perfect to pair with a great Commerce Cloud Store top.', + master: { + masterId: '25518009M', + orderable: true, + price: 28.15 + }, + minOrderQuantity: 1, + name: 'Extend Tab Straight Leg Pant', + pageDescription: + 'Classically designed, this bootleg jean is perfect to pair with a great Commerce Cloud Store top.', + pageTitle: 'Extend Tab Straight Leg Pant', + price: 28.15, + pricePerUnit: 28.15, + primaryCategoryId: 'womens-clothing-bottoms', + shortDescription: + 'Classically designed, this bootleg jean is perfect to pair with a great Commerce Cloud Store top.', + slugUrl: + 'https://zzrf-001.dx.commercecloud.salesforce.com/s/RefArchGlobal/extend-tab-straight-leg-pant/25518009M.html?lang=default', + stepQuantity: 1, + type: { + master: true + }, + validFrom: { + default: '2010-10-21T04:00:00.000Z' + }, + variants: [ + { + orderable: true, + price: 28.15, + productId: '701642819431M', + variationValues: { + color: 'JJ9LMXX', + size: '016' + } + }, + { + orderable: true, + price: 28.15, + productId: '701642819400M', + variationValues: { + color: 'JJ9LMXX', + size: '010' + } + }, + { + orderable: true, + price: 28.15, + productId: '701642819462M', + variationValues: { + color: 'JJ9LMXX', + size: '008' + } + }, + { + orderable: true, + price: 28.15, + productId: '701642819417M', + variationValues: { + color: 'JJ9LMXX', + size: '012' + } + }, + { + orderable: true, + price: 28.15, + productId: '701642819424M', + variationValues: { + color: 'JJ9LMXX', + size: '014' + } + }, + { + orderable: true, + price: 28.15, + productId: '701642819448M', + variationValues: { + color: 'JJ9LMXX', + size: '004' + } + }, + { + orderable: true, + price: 28.15, + productId: '701642819455M', + variationValues: { + color: 'JJ9LMXX', + size: '006' + } + } + ], + variationAttributes: [ + { + id: 'color', + name: 'Color', + values: [ + { + name: 'Marine Wash', + orderable: true, + value: 'JJ9LMXX' + } + ] + }, + { + id: 'size', + name: 'Size', + values: [ + { + name: '4', + orderable: true, + value: '004' + }, + { + name: '6', + orderable: true, + value: '006' + }, + { + name: '8', + orderable: true, + value: '008' + }, + { + name: '10', + orderable: true, + value: '010' + }, + { + name: '12', + orderable: true, + value: '012' + }, + { + name: '14', + orderable: true, + value: '014' + }, + { + name: '16', + orderable: true, + value: '016' + } + ] + } + ], + c_isNewtest: true + }, + { + currency: 'GBP', + id: 'P0048M', + imageGroups: [ + { + images: [ + { + alt: 'Laptop Briefcase with wheels (37L), , large', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw7cb2d401/images/large/P0048_001.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw7cb2d401/images/large/P0048_001.jpg', + title: 'Laptop Briefcase with wheels (37L), ' + } + ], + viewType: 'large' + }, + { + images: [ + { + alt: 'Laptop Briefcase with wheels (37L), , medium', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdeace89e/images/medium/P0048_001.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdeace89e/images/medium/P0048_001.jpg', + title: 'Laptop Briefcase with wheels (37L), ' + } + ], + viewType: 'medium' + }, + { + images: [ + { + alt: 'Laptop Briefcase with wheels (37L), , small', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw0dd2ff5d/images/small/P0048_001.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw0dd2ff5d/images/small/P0048_001.jpg', + title: 'Laptop Briefcase with wheels (37L), ' + } + ], + viewType: 'small' + } + ], + inventory: { + ats: 10, + backorderable: true, + id: 'inventory_m', + inStockDate: '2011-08-17T00:00:00.000Z', + orderable: true, + preorderable: false, + stockLevel: 0 + }, + longDescription: + '1682 ballistic nylon and genuine leather inserts| Spacious main storage compartment for documents and binders|Removable, padded laptop sleeve with D-rings for carrying with shoulder strap|Change handle system and cantilever wheels|Zip pull in gunmetal with black rubber insert Leather “comfort” insert detailed handle|Internal storage pockets for CD-Rom and peripherals|Real leather inserts', + minOrderQuantity: 1, + name: 'Laptop Briefcase with wheels (37L)', + pageDescription: + 'Perfect for business travel, this briefcase is ultra practical with plenty of space for your laptop and all its extras.', + pageKeywords: + 'Commerce Cloud, P0048, Packs and Gear Laptop Briefcase with Long Sleeve, Black, Wheelie Bag, Computer Bag, Cases, Luggage', + pageTitle: 'Commerce Cloud - Laptop Briefcase with wheels (37L)', + price: 63.99, + pricePerUnit: 63.99, + primaryCategoryId: 'mens-accessories-luggage', + shortDescription: + 'Perfect for business travel, this briefcase is ultra practical with plenty of space for your laptop and all its extras, as well as storage for documents, paperwork and all your essential items. The wheeled system allows you to travel comfortably with your work and when you reach your destination, you can remove the laptop compartment and carry over your shoulder to meetings. It’s the business.', + slugUrl: + 'https://zzrf-001.dx.commercecloud.salesforce.com/s/RefArchGlobal/laptop-briefcasewheels-37l/P0048M.html?lang=default', + stepQuantity: 1, + type: { + item: true + }, + c_styleNumber: 'P0048', + c_tabDescription: + 'Perfect for business travel, this briefcase is ultra practical with plenty of space for your laptop and all its extras, as well as storage for documents, paperwork and all your essential items. The wheeled system allows you to travel comfortably with your work and when you reach your destination, you can remove the laptop compartment and carry over your shoulder to meetings. It’s the business.', + c_tabDetails: + '1682 ballistic nylon and genuine leather inserts| Spacious main storage compartment for documents and binders|Removable, padded laptop sleeve with D-rings for carrying with shoulder strap|Change handle system and cantilever wheels|Zip pull in gunmetal with black rubber insert Leather “comfort” insert detailed handle|Internal storage pockets for CD-Rom and peripherals|Real leather inserts' + }, + { + currency: 'GBP', + id: '25565826M', + imageGroups: [ + { + images: [ + { + alt: 'Scoop Neck Tee With Applique, , large', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdbf2acb6/images/large/PG.10236198.JJ3KRXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdbf2acb6/images/large/PG.10236198.JJ3KRXX.PZ.jpg', + title: 'Scoop Neck Tee With Applique, ' + }, + { + alt: 'Scoop Neck Tee With Applique, , large', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw74cc6310/images/large/PG.10236198.JJ3KRXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw74cc6310/images/large/PG.10236198.JJ3KRXX.BZ.jpg', + title: 'Scoop Neck Tee With Applique, ' + } + ], + viewType: 'large' + }, + { + images: [ + { + alt: 'Scoop Neck Tee With Applique, seagrass, large', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdbf2acb6/images/large/PG.10236198.JJ3KRXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwdbf2acb6/images/large/PG.10236198.JJ3KRXX.PZ.jpg', + title: 'Scoop Neck Tee With Applique, seagrass' + }, + { + alt: 'Scoop Neck Tee With Applique, seagrass, large', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw74cc6310/images/large/PG.10236198.JJ3KRXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw74cc6310/images/large/PG.10236198.JJ3KRXX.BZ.jpg', + title: 'Scoop Neck Tee With Applique, seagrass' + } + ], + variationAttributes: [ + { + id: 'color', + values: [ + { + value: 'JJ3KRXX' + } + ] + } + ], + viewType: 'large' + }, + { + images: [ + { + alt: 'Scoop Neck Tee With Applique, Blue, large', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwab758c62/images/large/PG.10236198.JJGG3XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwab758c62/images/large/PG.10236198.JJGG3XX.PZ.jpg', + title: 'Scoop Neck Tee With Applique, Blue' + }, + { + alt: 'Scoop Neck Tee With Applique, Blue, large', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa0ae8bbb/images/large/PG.10236198.JJGG3XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwa0ae8bbb/images/large/PG.10236198.JJGG3XX.BZ.jpg', + title: 'Scoop Neck Tee With Applique, Blue' + } + ], + variationAttributes: [ + { + id: 'color', + values: [ + { + value: 'JJGG3XX' + } + ] + } + ], + viewType: 'large' + }, + { + images: [ + { + alt: 'Scoop Neck Tee With Applique, , medium', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwfc2b10cb/images/medium/PG.10236198.JJ3KRXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwfc2b10cb/images/medium/PG.10236198.JJ3KRXX.PZ.jpg', + title: 'Scoop Neck Tee With Applique, ' + }, + { + alt: 'Scoop Neck Tee With Applique, , medium', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwb8ca1480/images/medium/PG.10236198.JJ3KRXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwb8ca1480/images/medium/PG.10236198.JJ3KRXX.BZ.jpg', + title: 'Scoop Neck Tee With Applique, ' + } + ], + viewType: 'medium' + }, + { + images: [ + { + alt: 'Scoop Neck Tee With Applique, seagrass, medium', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwfc2b10cb/images/medium/PG.10236198.JJ3KRXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwfc2b10cb/images/medium/PG.10236198.JJ3KRXX.PZ.jpg', + title: 'Scoop Neck Tee With Applique, seagrass' + }, + { + alt: 'Scoop Neck Tee With Applique, seagrass, medium', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwb8ca1480/images/medium/PG.10236198.JJ3KRXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwb8ca1480/images/medium/PG.10236198.JJ3KRXX.BZ.jpg', + title: 'Scoop Neck Tee With Applique, seagrass' + } + ], + variationAttributes: [ + { + id: 'color', + values: [ + { + value: 'JJ3KRXX' + } + ] + } + ], + viewType: 'medium' + }, + { + images: [ + { + alt: 'Scoop Neck Tee With Applique, Blue, medium', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw2584fdaa/images/medium/PG.10236198.JJGG3XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw2584fdaa/images/medium/PG.10236198.JJGG3XX.PZ.jpg', + title: 'Scoop Neck Tee With Applique, Blue' + }, + { + alt: 'Scoop Neck Tee With Applique, Blue, medium', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw2915b5fc/images/medium/PG.10236198.JJGG3XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw2915b5fc/images/medium/PG.10236198.JJGG3XX.BZ.jpg', + title: 'Scoop Neck Tee With Applique, Blue' + } + ], + variationAttributes: [ + { + id: 'color', + values: [ + { + value: 'JJGG3XX' + } + ] + } + ], + viewType: 'medium' + }, + { + images: [ + { + alt: 'Scoop Neck Tee With Applique, , small', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw384b69b6/images/small/PG.10236198.JJ3KRXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw384b69b6/images/small/PG.10236198.JJ3KRXX.PZ.jpg', + title: 'Scoop Neck Tee With Applique, ' + }, + { + alt: 'Scoop Neck Tee With Applique, , small', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbbb4da09/images/small/PG.10236198.JJ3KRXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbbb4da09/images/small/PG.10236198.JJ3KRXX.BZ.jpg', + title: 'Scoop Neck Tee With Applique, ' + } + ], + viewType: 'small' + }, + { + images: [ + { + alt: 'Scoop Neck Tee With Applique, seagrass, small', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw384b69b6/images/small/PG.10236198.JJ3KRXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw384b69b6/images/small/PG.10236198.JJ3KRXX.PZ.jpg', + title: 'Scoop Neck Tee With Applique, seagrass' + }, + { + alt: 'Scoop Neck Tee With Applique, seagrass, small', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbbb4da09/images/small/PG.10236198.JJ3KRXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbbb4da09/images/small/PG.10236198.JJ3KRXX.BZ.jpg', + title: 'Scoop Neck Tee With Applique, seagrass' + } + ], + variationAttributes: [ + { + id: 'color', + values: [ + { + value: 'JJ3KRXX' + } + ] + } + ], + viewType: 'small' + }, + { + images: [ + { + alt: 'Scoop Neck Tee With Applique, Blue, small', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf19ea495/images/small/PG.10236198.JJGG3XX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf19ea495/images/small/PG.10236198.JJGG3XX.PZ.jpg', + title: 'Scoop Neck Tee With Applique, Blue' + }, + { + alt: 'Scoop Neck Tee With Applique, Blue, small', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwd7712389/images/small/PG.10236198.JJGG3XX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwd7712389/images/small/PG.10236198.JJGG3XX.BZ.jpg', + title: 'Scoop Neck Tee With Applique, Blue' + } + ], + variationAttributes: [ + { + id: 'color', + values: [ + { + value: 'JJGG3XX' + } + ] + } + ], + viewType: 'small' + }, + { + images: [ + { + alt: 'Scoop Neck Tee With Applique, seagrass, swatch', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9502cf77/images/swatch/PG.10236198.JJ3KRXX.CP.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw9502cf77/images/swatch/PG.10236198.JJ3KRXX.CP.jpg', + title: 'Scoop Neck Tee With Applique, seagrass' + } + ], + variationAttributes: [ + { + id: 'color', + values: [ + { + value: 'JJ3KRXX' + } + ] + } + ], + viewType: 'swatch' + }, + { + images: [ + { + alt: 'Scoop Neck Tee With Applique, Blue, swatch', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4671d198/images/swatch/PG.10236198.JJGG3XX.CP.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4671d198/images/swatch/PG.10236198.JJGG3XX.CP.jpg', + title: 'Scoop Neck Tee With Applique, Blue' + } + ], + variationAttributes: [ + { + id: 'color', + values: [ + { + value: 'JJGG3XX' + } + ] + } + ], + viewType: 'swatch' + } + ], + inventory: { + ats: 799, + backorderable: false, + id: 'inventory_m', + orderable: true, + preorderable: false, + stockLevel: 799 + }, + longDescription: 'We love this scoop neck tee with applique. Perfect for weekend wear.', + master: { + masterId: '25565826M', + orderable: true, + price: 24.96 + }, + minOrderQuantity: 1, + name: 'Scoop Neck Tee With Applique', + pageDescription: 'We love this scoop neck tee with applique. Perfect for weekend wear.', + pageTitle: 'Scoop Neck Tee With Applique', + price: 24.96, + pricePerUnit: 24.96, + primaryCategoryId: 'womens-clothing-tops', + productPromotions: [ + { + calloutMsg: 'Buy one Long Center Seam Skirt and get 2 tops', + promotionId: 'ChoiceOfBonusProdect-ProductLevel-ruleBased' + }, + { + calloutMsg: '$50 Fixed Products Amount Above 100', + promotionId: '$50FixedProductsAmountAbove100' + }, + { + calloutMsg: 'Bonus Product for Order Amounts Above 250', + promotionId: 'BonusProductOnOrderOfAmountABove250' + } + ], + shortDescription: + 'We love this scoop neck tee with applique. Perfect for weekend wear.', + slugUrl: + 'https://zzrf-001.dx.commercecloud.salesforce.com/s/RefArchGlobal/scoop-neck-tee-with-applique/25565826M.html?lang=default', + stepQuantity: 1, + type: { + master: true + }, + validFrom: { + default: '2011-02-04T05:00:00.000Z' + }, + variants: [ + { + orderable: true, + price: 24.96, + productId: '701644059262M', + variationValues: { + color: 'JJGG3XX', + size: '9SM' + } + }, + { + orderable: true, + price: 24.96, + productId: '701644059224M', + variationValues: { + color: 'JJ3KRXX', + size: '9LG' + } + }, + { + orderable: true, + price: 24.96, + productId: '701644059248M', + variationValues: { + color: 'JJ3KRXX', + size: '9SM' + } + }, + { + orderable: true, + price: 24.96, + productId: '701644059255M', + variationValues: { + color: 'JJ3KRXX', + size: '9XL' + } + }, + { + orderable: true, + price: 24.96, + productId: '701644059286M', + variationValues: { + color: 'JJGG3XX', + size: '9LG' + } + }, + { + orderable: true, + price: 24.96, + productId: '701644059293M', + variationValues: { + color: 'JJGG3XX', + size: '9XL' + } + }, + { + orderable: true, + price: 24.96, + productId: '701644059231M', + variationValues: { + color: 'JJ3KRXX', + size: '9MD' + } + }, + { + orderable: true, + price: 24.96, + productId: '701644059279M', + variationValues: { + color: 'JJGG3XX', + size: '9MD' + } + } + ], + variationAttributes: [ + { + id: 'color', + name: 'Color', + values: [ + { + name: 'Blue', + orderable: true, + value: 'JJGG3XX' + }, + { + name: 'seagrass', + orderable: true, + value: 'JJ3KRXX' + } + ] + }, + { + id: 'size', + name: 'Size', + values: [ + { + name: 'S', + orderable: true, + value: '9SM' + }, + { + name: 'M', + orderable: true, + value: '9MD' + }, + { + name: 'L', + orderable: true, + value: '9LG' + }, + { + name: 'XL', + orderable: true, + value: '9XL' + } + ] + } + ] + }, + { + currency: 'GBP', + id: '701642819462M', + imageGroups: [ + { + images: [ + { + alt: 'Extend Tab Straight Leg Pant, , large', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbde9a921/images/large/PG.10211049.JJ9LMXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbde9a921/images/large/PG.10211049.JJ9LMXX.PZ.jpg', + title: 'Extend Tab Straight Leg Pant, ' + }, + { + alt: 'Extend Tab Straight Leg Pant, , large', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3b2c7f13/images/large/PG.10211049.JJ9LMXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3b2c7f13/images/large/PG.10211049.JJ9LMXX.BZ.jpg', + title: 'Extend Tab Straight Leg Pant, ' + } + ], + viewType: 'large' + }, + { + images: [ + { + alt: 'Extend Tab Straight Leg Pant, Marine Wash, large', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbde9a921/images/large/PG.10211049.JJ9LMXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwbde9a921/images/large/PG.10211049.JJ9LMXX.PZ.jpg', + title: 'Extend Tab Straight Leg Pant, Marine Wash' + }, + { + alt: 'Extend Tab Straight Leg Pant, Marine Wash, large', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3b2c7f13/images/large/PG.10211049.JJ9LMXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3b2c7f13/images/large/PG.10211049.JJ9LMXX.BZ.jpg', + title: 'Extend Tab Straight Leg Pant, Marine Wash' + } + ], + variationAttributes: [ + { + id: 'color', + values: [ + { + value: 'JJ9LMXX' + } + ] + } + ], + viewType: 'large' + }, + { + images: [ + { + alt: 'Extend Tab Straight Leg Pant, , medium', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf852acb0/images/medium/PG.10211049.JJ9LMXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf852acb0/images/medium/PG.10211049.JJ9LMXX.PZ.jpg', + title: 'Extend Tab Straight Leg Pant, ' + }, + { + alt: 'Extend Tab Straight Leg Pant, , medium', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw28eac377/images/medium/PG.10211049.JJ9LMXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw28eac377/images/medium/PG.10211049.JJ9LMXX.BZ.jpg', + title: 'Extend Tab Straight Leg Pant, ' + } + ], + viewType: 'medium' + }, + { + images: [ + { + alt: 'Extend Tab Straight Leg Pant, Marine Wash, medium', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf852acb0/images/medium/PG.10211049.JJ9LMXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf852acb0/images/medium/PG.10211049.JJ9LMXX.PZ.jpg', + title: 'Extend Tab Straight Leg Pant, Marine Wash' + }, + { + alt: 'Extend Tab Straight Leg Pant, Marine Wash, medium', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw28eac377/images/medium/PG.10211049.JJ9LMXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw28eac377/images/medium/PG.10211049.JJ9LMXX.BZ.jpg', + title: 'Extend Tab Straight Leg Pant, Marine Wash' + } + ], + variationAttributes: [ + { + id: 'color', + values: [ + { + value: 'JJ9LMXX' + } + ] + } + ], + viewType: 'medium' + }, + { + images: [ + { + alt: 'Extend Tab Straight Leg Pant, , small', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf5169852/images/small/PG.10211049.JJ9LMXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf5169852/images/small/PG.10211049.JJ9LMXX.PZ.jpg', + title: 'Extend Tab Straight Leg Pant, ' + }, + { + alt: 'Extend Tab Straight Leg Pant, , small', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw08ed3309/images/small/PG.10211049.JJ9LMXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw08ed3309/images/small/PG.10211049.JJ9LMXX.BZ.jpg', + title: 'Extend Tab Straight Leg Pant, ' + } + ], + viewType: 'small' + }, + { + images: [ + { + alt: 'Extend Tab Straight Leg Pant, Marine Wash, small', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf5169852/images/small/PG.10211049.JJ9LMXX.PZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf5169852/images/small/PG.10211049.JJ9LMXX.PZ.jpg', + title: 'Extend Tab Straight Leg Pant, Marine Wash' + }, + { + alt: 'Extend Tab Straight Leg Pant, Marine Wash, small', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw08ed3309/images/small/PG.10211049.JJ9LMXX.BZ.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw08ed3309/images/small/PG.10211049.JJ9LMXX.BZ.jpg', + title: 'Extend Tab Straight Leg Pant, Marine Wash' + } + ], + variationAttributes: [ + { + id: 'color', + values: [ + { + value: 'JJ9LMXX' + } + ] + } + ], + viewType: 'small' + }, + { + images: [ + { + alt: 'Extend Tab Straight Leg Pant, Marine Wash, swatch', + disBaseLink: + 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5314a78e/images/swatch/PG.10211049.JJ9LMXX.CP.jpg', + link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5314a78e/images/swatch/PG.10211049.JJ9LMXX.CP.jpg', + title: 'Extend Tab Straight Leg Pant, Marine Wash' + } + ], + variationAttributes: [ + { + id: 'color', + values: [ + { + value: 'JJ9LMXX' + } + ] + } + ], + viewType: 'swatch' + } + ], + inventory: { + ats: 100, + backorderable: false, + id: 'inventory_m', + orderable: true, + preorderable: false, + stockLevel: 100 + }, + longDescription: + 'Classically designed, this bootleg jean is perfect to pair with a great Commerce Cloud Store top.', + master: { + masterId: '25518009M', + orderable: true, + price: 28.15 + }, + minOrderQuantity: 1, + name: 'Extend Tab Straight Leg Pant', + pageDescription: + 'Classically designed, this bootleg jean is perfect to pair with a great Commerce Cloud Store top.', + pageTitle: 'Extend Tab Straight Leg Pant', + price: 28.15, + pricePerUnit: 28.15, + shortDescription: + 'Classically designed, this bootleg jean is perfect to pair with a great Commerce Cloud Store top.', + slugUrl: + 'https://zzrf-001.dx.commercecloud.salesforce.com/s/RefArchGlobal/extend-tab-straight-leg-pant/701642819462M.html?lang=default', + stepQuantity: 1, + type: { + variant: true + }, + unitMeasure: '', + unitQuantity: 0, + upc: '701642819462', + validFrom: { + default: '2010-10-21T04:00:00.000Z' + }, + variants: [ + { + orderable: true, + price: 28.15, + productId: '701642819431M', + variationValues: { + color: 'JJ9LMXX', + size: '016' + } + }, + { + orderable: true, + price: 28.15, + productId: '701642819400M', + variationValues: { + color: 'JJ9LMXX', + size: '010' + } + }, + { + orderable: true, + price: 28.15, + productId: '701642819462M', + variationValues: { + color: 'JJ9LMXX', + size: '008' + } + }, + { + orderable: true, + price: 28.15, + productId: '701642819417M', + variationValues: { + color: 'JJ9LMXX', + size: '012' + } + }, + { + orderable: true, + price: 28.15, + productId: '701642819424M', + variationValues: { + color: 'JJ9LMXX', + size: '014' + } + }, + { + orderable: true, + price: 28.15, + productId: '701642819448M', + variationValues: { + color: 'JJ9LMXX', + size: '004' + } + }, + { + orderable: true, + price: 28.15, + productId: '701642819455M', + variationValues: { + color: 'JJ9LMXX', + size: '006' + } + } + ], + variationAttributes: [ + { + id: 'color', + name: 'Color', + values: [ + { + name: 'Marine Wash', + orderable: true, + value: 'JJ9LMXX' + } + ] + }, + { + id: 'size', + name: 'Size', + values: [ + { + name: '4', + orderable: true, + value: '004' + }, + { + name: '6', + orderable: true, + value: '006' + }, + { + name: '8', + orderable: true, + value: '008' + }, + { + name: '10', + orderable: true, + value: '010' + }, + { + name: '12', + orderable: true, + value: '012' + }, + { + name: '14', + orderable: true, + value: '014' + }, + { + name: '16', + orderable: true, + value: '016' + } + ] + } + ], + variationValues: { + color: 'JJ9LMXX', + size: '008' + }, + c_color: 'JJ9LMXX', + c_isNewtest: true, + c_refinementColor: 'blue', + c_size: '008', + c_width: 'Z' + } + ], + total: 7 +} diff --git a/packages/template-retail-react-app/app/pages/account/wishlist/index.test.js b/packages/template-retail-react-app/app/pages/account/wishlist/index.test.js index 208b34b938..d52403206f 100644 --- a/packages/template-retail-react-app/app/pages/account/wishlist/index.test.js +++ b/packages/template-retail-react-app/app/pages/account/wishlist/index.test.js @@ -7,339 +7,9 @@ import React from 'react' import AccountWishlist from '.' import {renderWithProviders} from '../../../utils/test-utils' -import {screen} from '@testing-library/react' -import userEvent from '@testing-library/user-event' -import useWishlist from '../../../hooks/use-wishlist' - -const mockData = { - creationDate: '2021-09-13T23:29:23.396Z', - customerProductListItems: [ - { - id: '98ca9a3a9c8ee803543dc45cdc', - priority: 1, - productId: '25518837M', - public: false, - purchasedQuantity: 0, - quantity: 4, - type: 'product', - product: { - currency: 'GBP', - id: '25518837M', - imageGroups: [ - { - images: [ - { - alt: 'Ruffle Front V-Neck Cardigan, , large', - disBaseLink: - 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4cd0a798/images/large/PG.10216885.JJ169XX.PZ.jpg', - link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4cd0a798/images/large/PG.10216885.JJ169XX.PZ.jpg', - title: 'Ruffle Front V-Neck Cardigan, ' - }, - { - alt: 'Ruffle Front V-Neck Cardigan, , large', - disBaseLink: - 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf67d39ef/images/large/PG.10216885.JJ169XX.BZ.jpg', - link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwf67d39ef/images/large/PG.10216885.JJ169XX.BZ.jpg', - title: 'Ruffle Front V-Neck Cardigan, ' - } - ], - viewType: 'large' - }, - { - images: [ - { - alt: 'Ruffle Front V-Neck Cardigan, , medium', - disBaseLink: - 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc31527c1/images/medium/PG.10216885.JJ169XX.PZ.jpg', - link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc31527c1/images/medium/PG.10216885.JJ169XX.PZ.jpg', - title: 'Ruffle Front V-Neck Cardigan, ' - }, - { - alt: 'Ruffle Front V-Neck Cardigan, , medium', - disBaseLink: - 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3b11511c/images/medium/PG.10216885.JJ169XX.BZ.jpg', - link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3b11511c/images/medium/PG.10216885.JJ169XX.BZ.jpg', - title: 'Ruffle Front V-Neck Cardigan, ' - } - ], - viewType: 'medium' - }, - { - images: [ - { - alt: 'Ruffle Front V-Neck Cardigan, , small', - disBaseLink: - 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4ada76e4/images/small/PG.10216885.JJ169XX.PZ.jpg', - link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw4ada76e4/images/small/PG.10216885.JJ169XX.PZ.jpg', - title: 'Ruffle Front V-Neck Cardigan, ' - }, - { - alt: 'Ruffle Front V-Neck Cardigan, , small', - disBaseLink: - 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5c3e4bf4/images/small/PG.10216885.JJ169XX.BZ.jpg', - link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5c3e4bf4/images/small/PG.10216885.JJ169XX.BZ.jpg', - title: 'Ruffle Front V-Neck Cardigan, ' - } - ], - viewType: 'small' - } - ], - inventory: { - ats: 1597, - backorderable: false, - id: 'inventory_m', - orderable: true, - preorderable: false, - stockLevel: 1597 - }, - longDescription: - "This flirty ruffle cardigan can take you from day to night. Don't leave home with out a great pair of Commerce Cloud Store earrings.", - master: { - masterId: '25518837M', - orderable: true, - price: 26.23 - }, - minOrderQuantity: 1, - name: 'Ruffle Front V-Neck Cardigan', - pageDescription: - "This flirty ruffle cardigan can take you from day to night. Don't leave home with out a great pair of Commerce Cloud Store earrings.", - pageTitle: 'Ruffle Front V-Neck Cardigan', - price: 26.23, - pricePerUnit: 26.23, - primaryCategoryId: 'womens-clothing-tops', - productPromotions: [ - { - calloutMsg: '$50 Fixed Products Amount Above 100', - promotionId: '$50FixedProductsAmountAbove100' - }, - { - calloutMsg: 'Buy one Long Center Seam Skirt and get 2 tops', - promotionId: 'ChoiceOfBonusProdect-ProductLevel-ruleBased' - } - ], - shortDescription: - "This flirty ruffle cardigan can take you from day to night. Don't leave home with out a great pair of Commerce Cloud Store earrings.", - stepQuantity: 1, - type: { - master: true - }, - validFrom: { - default: '2010-11-18T05:00:00.000Z' - }, - variants: [ - { - orderable: true, - price: 26.23, - productId: '701642873211M', - variationValues: { - color: 'JJ169XX', - size: '9LG' - } - }, - { - orderable: true, - price: 26.23, - productId: '701642873334M', - variationValues: { - color: 'JJ908XX', - size: '9LG' - } - }, - { - orderable: true, - price: 26.23, - productId: '701642873297M', - variationValues: { - color: 'JJ8UTXX', - size: '9LG' - } - }, - { - orderable: true, - price: 26.23, - productId: '701642873372M', - variationValues: { - color: 'JJI15XX', - size: '9LG' - } - }, - { - orderable: true, - price: 26.23, - productId: '701642873402M', - variationValues: { - color: 'JJI15XX', - size: '9XL' - } - }, - { - orderable: true, - price: 26.23, - productId: '701642873303M', - variationValues: { - color: 'JJ8UTXX', - size: '9MD' - } - }, - { - orderable: true, - price: 26.23, - productId: '701642873310M', - variationValues: { - color: 'JJ8UTXX', - size: '9SM' - } - }, - { - orderable: true, - price: 26.23, - productId: '701642873228M', - variationValues: { - color: 'JJ169XX', - size: '9MD' - } - }, - { - orderable: true, - price: 26.23, - productId: '701642873235M', - variationValues: { - color: 'JJ169XX', - size: '9SM' - } - }, - { - orderable: true, - price: 26.23, - productId: '701642873396M', - variationValues: { - color: 'JJI15XX', - size: '9SM' - } - }, - { - orderable: true, - price: 26.23, - productId: '701642873389M', - variationValues: { - color: 'JJI15XX', - size: '9MD' - } - }, - { - orderable: true, - price: 26.23, - productId: '701642873327M', - variationValues: { - color: 'JJ8UTXX', - size: '9XL' - } - }, - { - orderable: true, - price: 26.23, - productId: '701642873341M', - variationValues: { - color: 'JJ908XX', - size: '9MD' - } - }, - { - orderable: true, - price: 26.23, - productId: '701642873358M', - variationValues: { - color: 'JJ908XX', - size: '9SM' - } - }, - { - orderable: true, - price: 26.23, - productId: '701642873365M', - variationValues: { - color: 'JJ908XX', - size: '9XL' - } - }, - { - orderable: true, - price: 26.23, - productId: '701642873242M', - variationValues: { - color: 'JJ169XX', - size: '9XL' - } - } - ], - variationAttributes: [ - { - id: 'color', - name: 'Colour', - values: [ - { - name: 'Black', - orderable: true, - value: 'JJ169XX' - }, - { - name: 'Icy Mint', - orderable: true, - value: 'JJ8UTXX' - }, - { - name: 'Grey Heather', - orderable: true, - value: 'JJ908XX' - }, - { - name: 'White', - orderable: true, - value: 'JJI15XX' - } - ] - }, - { - id: 'size', - name: 'Size', - values: [ - { - name: 'S', - orderable: true, - value: '9SM' - }, - { - name: 'M', - orderable: true, - value: '9MD' - }, - { - name: 'L', - orderable: true, - value: '9LG' - }, - { - name: 'XL', - orderable: true, - value: '9XL' - } - ] - } - ], - c_isNewtest: true, - c_isSale: true - } - } - ], - event: {}, - id: 'eba7a6682031bfa931949708d7', - lastModified: '2021-09-14T00:47:12.612Z', - name: 'PWA wishlist', - public: false, - type: 'wish_list' -} - -jest.mock('../../../hooks/use-wishlist') +import {screen, waitFor} from '@testing-library/react' +import {rest} from 'msw' +import {mockedEmptyWishList, mockedProductLists, mockedWishListProducts} from './index.mock' jest.mock('commerce-sdk-react-preview', () => { const originalModule = jest.requireActual('commerce-sdk-react-preview') @@ -349,61 +19,53 @@ jest.mock('commerce-sdk-react-preview', () => { } }) -beforeEach(() => { - jest.resetModules() +jest.mock('../../../hooks/use-current-customer', () => { + return { + useCurrentCustomer: jest.fn().mockReturnValue({ + data: { + customerId: 'some_id', + isRegistered: true + } + }) + } }) -test('Renders wishlist page', () => { - useWishlist.mockReturnValue({ - isInitialized: true, - isEmpty: false, - hasDetail: true, - items: mockData.customerProductListItems - }) +beforeEach(() => { + jest.resetModules() - renderWithProviders() - expect(screen.getByTestId('account-wishlist-page')).toBeInTheDocument() - expect(screen.getByText(mockData.customerProductListItems[0].product.name)).toBeInTheDocument() + global.server.use( + rest.get('*/products', (req, res, ctx) => { + return res(ctx.delay(0), ctx.status(200), ctx.json(mockedWishListProducts)) + }), + rest.get('*/customers/:customerId/product-lists', (req, res, ctx) => { + return res(ctx.delay(0), ctx.status(200), ctx.json(mockedProductLists)) + }) + ) }) -test('Can remove item from the wishlist', async () => { - const removeItemMock = jest.fn() - useWishlist.mockReturnValue({ - isInitialized: true, - isEmpty: false, - hasDetail: true, - items: mockData.customerProductListItems, - removeListItem: removeItemMock - }) - +test('Renders wishlist page', async () => { renderWithProviders() + await waitFor(() => { + expect(screen.getByTestId('account-wishlist-page')).toBeInTheDocument() + expect(screen.getByRole('link', {name: /fall look/i})).toBeInTheDocument() + }) +}) - const wishlistRemoveButton = await screen.findByTestId( - 'sf-wishlist-remove-98ca9a3a9c8ee803543dc45cdc' +test('renders no wishlist items for empty wishlist', async () => { + global.server.use( + rest.get('*/customers/:customerId/product-lists', (req, res, ctx) => { + return res(ctx.delay(0), ctx.status(200), ctx.json(mockedEmptyWishList)) + }) ) - userEvent.click(wishlistRemoveButton) - userEvent.click(screen.getByRole('button', {name: /yes, remove item/i})) - expect(removeItemMock).toBeCalled() -}) -test('renders no wishlist items for empty wishlist', () => { - useWishlist.mockReturnValue({ - isInitialized: true, - isEmpty: true, - hasDetail: true - }) renderWithProviders() - - expect(screen.getByText(/no wishlist items/i)).toBeInTheDocument() - expect(screen.getByRole('button', {name: /continue shopping/i})).toBeInTheDocument() + await waitFor(() => { + expect(screen.getByText(/no wishlist items/i)).toBeInTheDocument() + expect(screen.getByRole('button', {name: /continue shopping/i})).toBeInTheDocument() + }) }) test('renders skeleton when product list is loading', () => { - useWishlist.mockReturnValue({ - isInitialized: false, - isEmpty: true - }) - renderWithProviders() expect(screen.getByTestId('sf-wishlist-skeleton')).toBeInTheDocument() }) diff --git a/packages/template-retail-react-app/app/pages/account/wishlist/partials/wishlist-primary-action.jsx b/packages/template-retail-react-app/app/pages/account/wishlist/partials/wishlist-primary-action.jsx index 9049790046..e0f9067ae1 100644 --- a/packages/template-retail-react-app/app/pages/account/wishlist/partials/wishlist-primary-action.jsx +++ b/packages/template-retail-react-app/app/pages/account/wishlist/partials/wishlist-primary-action.jsx @@ -6,12 +6,13 @@ */ import React, {useState} from 'react' import {Button, useDisclosure} from '@chakra-ui/react' -import useBasket from '../../../../commerce-api/hooks/useBasket' +import {useShopperBasketsMutation} from 'commerce-sdk-react-preview' import {FormattedMessage, useIntl} from 'react-intl' import {useItemVariant} from '../../../../components/item-variant' import ProductViewModal from '../../../../components/product-view-modal' import {useToast} from '../../../../hooks/use-toast' import {API_ERROR_MESSAGE} from '../../../../constants' +import {useCurrentBasket} from '../../../../hooks/use-current-basket' import Link from '../../../../components/link' /** @@ -21,7 +22,7 @@ import Link from '../../../../components/link' */ const WishlistPrimaryAction = () => { const variant = useItemVariant() - const basket = useBasket() + const {data: basket} = useCurrentBasket() const {formatMessage} = useIntl() const isMasterProduct = variant?.type?.master || false const isProductASet = variant?.type?.set @@ -29,11 +30,12 @@ const WishlistPrimaryAction = () => { const [isLoading, setIsLoading] = useState(false) const {isOpen, onOpen, onClose} = useDisclosure() + const addItemToBasket = useShopperBasketsMutation('addItemToBasket') + const handleAddToCart = async (item, quantity) => { setIsLoading(true) const isAddingASet = Boolean(item.setProducts) - const productItems = isAddingASet ? item.setProducts.map((child) => ({ productId: child.id || child.productId, @@ -48,28 +50,34 @@ const WishlistPrimaryAction = () => { } ] - try { - await basket.addItemToBasket(productItems) - showToast({ - title: formatMessage( - { - defaultMessage: - '{quantity} {quantity, plural, one {item} other {items}} added to cart', - id: 'wishlist_primary_action.info.added_to_cart' - }, - {quantity: isAddingASet ? quantity * item.setProducts.length : quantity} - ), - status: 'success' - }) - onClose() - } catch (error) { - showToast({ - title: formatMessage(API_ERROR_MESSAGE), - status: 'error' - }) - } - - setIsLoading(false) + addItemToBasket.mutate( + {body: productItems, parameters: {basketId: basket?.basketId}}, + { + onSuccess: () => { + showToast({ + title: formatMessage( + { + defaultMessage: + '{quantity} {quantity, plural, one {item} other {items}} added to cart', + id: 'wishlist_primary_action.info.added_to_cart' + }, + {quantity: isAddingASet ? quantity * item.setProducts.length : quantity} + ), + status: 'success' + }) + onClose() + }, + onError: () => { + showToast({ + title: formatMessage(API_ERROR_MESSAGE), + status: 'error' + }) + }, + onSettled: () => { + setIsLoading(false) + } + } + ) } const buttonText = { diff --git a/packages/template-retail-react-app/app/pages/account/wishlist/partials/wishlist-primary-action.test.js b/packages/template-retail-react-app/app/pages/account/wishlist/partials/wishlist-primary-action.test.js index a082e80cd6..ec4e65c372 100644 --- a/packages/template-retail-react-app/app/pages/account/wishlist/partials/wishlist-primary-action.test.js +++ b/packages/template-retail-react-app/app/pages/account/wishlist/partials/wishlist-primary-action.test.js @@ -12,6 +12,8 @@ import WishlistPrimaryAction from './wishlist-primary-action' import userEvent from '@testing-library/user-event' import {screen, waitFor} from '@testing-library/react' import PropTypes from 'prop-types' +import {rest} from 'msw' +import {basketWithProductSet} from '../../../product-detail/index.mock' const MockedComponent = ({variant}) => { return ( @@ -24,26 +26,35 @@ MockedComponent.propTypes = { variant: PropTypes.object } -jest.mock('../../../../commerce-api/hooks/useBasket', () => { - return () => { - return { - addItemToBasket: jest.fn() +jest.mock('../../../../hooks/use-current-basket', () => { + return { + useCurrentBasket: () => { + return { + data: {basketId: 'basket_id'}, + derivedData: {totalItems: 5} + } } } }) beforeEach(() => { jest.resetModules() + + global.server.use( + // For adding items to basket + rest.post('*/baskets/:basketId/items', (req, res, ctx) => { + return res(ctx.json(basketWithProductSet)) + }) + ) }) test('the Add To Cart button', async () => { const variant = mockWishListDetails.data[3] - const {getByRole} = renderWithProviders() + renderWithProviders() - const addToCartButton = getByRole('button', { + const addToCartButton = await screen.findByRole('button', { name: /add to cart/i }) - expect(addToCartButton).toBeInTheDocument() userEvent.click(addToCartButton) await waitFor(() => { @@ -57,7 +68,7 @@ test('the Add Set To Cart button', async () => { const productSetWithoutVariants = mockWishListDetails.data[1] renderWithProviders() - const button = screen.getByRole('button', {name: /add set to cart/i}) + const button = await screen.findByRole('button', {name: /add set to cart/i}) userEvent.click(button) await waitFor(() => { @@ -65,11 +76,11 @@ test('the Add Set To Cart button', async () => { }) }) -test('the View Full Details button', () => { +test('the View Full Details button', async () => { const productSetWithVariants = mockWishListDetails.data[0] renderWithProviders() - const link = screen.getByRole('link', {name: /view full details/i}) + const link = await screen.findByRole('link', {name: /view full details/i}) expect(link).toBeInTheDocument() }) @@ -77,7 +88,7 @@ test('the View Options button', async () => { const masterProduct = mockWishListDetails.data[2] renderWithProviders() - const button = screen.getByRole('button', {name: /view options/i}) + const button = await screen.findByRole('button', {name: /view options/i}) userEvent.click(button) await waitFor( @@ -88,4 +99,4 @@ test('the View Options button', async () => { // Seems like rendering the modal takes a bit more time {timeout: 5000} ) -}) +}, 30000) diff --git a/packages/template-retail-react-app/app/pages/account/wishlist/partials/wishlist-secondary-button-group.jsx b/packages/template-retail-react-app/app/pages/account/wishlist/partials/wishlist-secondary-button-group.jsx index 73b2eaf37d..8e9b3d3749 100644 --- a/packages/template-retail-react-app/app/pages/account/wishlist/partials/wishlist-secondary-button-group.jsx +++ b/packages/template-retail-react-app/app/pages/account/wishlist/partials/wishlist-secondary-button-group.jsx @@ -8,9 +8,11 @@ import React from 'react' import PropTypes from 'prop-types' import {Button, ButtonGroup, useDisclosure} from '@chakra-ui/react' import {useIntl, defineMessage, FormattedMessage} from 'react-intl' +import {useShopperCustomersMutation} from 'commerce-sdk-react-preview' -import useWishlist from '../../../../hooks/use-wishlist' import {useToast} from '../../../../hooks/use-toast' +import {useCurrentCustomer} from '../../../../hooks/use-current-customer' +import {useWishList} from '../../../../hooks/use-wish-list' import ConfirmationModal from '../../../../components/confirmation-modal/index' import {useItemVariant} from '../../../../components/item-variant' @@ -43,7 +45,8 @@ export const REMOVE_WISHLIST_ITEM_CONFIRMATION_DIALOG_CONFIG = { */ const WishlistSecondaryButtonGroup = ({productListItemId, onClick = noop}) => { const variant = useItemVariant() - const wishlist = useWishlist() + const {data: customer} = useCurrentCustomer() + const {data: wishList} = useWishList() const modalProps = useDisclosure() const toast = useToast() const {formatMessage} = useIntl() @@ -52,10 +55,23 @@ const WishlistSecondaryButtonGroup = ({productListItemId, onClick = noop}) => { modalProps.onOpen() } + const deleteCustomerProductListItem = useShopperCustomersMutation( + 'deleteCustomerProductListItem' + ) + const handleItemRemove = async () => { - onClick(variant.id) try { - await wishlist.removeListItem(productListItemId) + const promise = deleteCustomerProductListItem.mutateAsync({ + parameters: { + customerId: customer.customerId, + listId: wishList?.id, + itemId: productListItemId + } + }) + onClick(variant.id, promise) + + await promise + toast({ title: formatMessage({ defaultMessage: 'Item removed from wishlist', @@ -64,12 +80,8 @@ const WishlistSecondaryButtonGroup = ({productListItemId, onClick = noop}) => { status: 'success' }) } catch { - toast({ - title: formatMessage(API_ERROR_MESSAGE), - status: 'error' - }) + toast({title: formatMessage(API_ERROR_MESSAGE), status: 'error'}) } - onClick('') } return ( diff --git a/packages/template-retail-react-app/app/pages/account/wishlist/partials/wishlist-secondary-button-group.test.js b/packages/template-retail-react-app/app/pages/account/wishlist/partials/wishlist-secondary-button-group.test.js index df76954b54..591b78884b 100644 --- a/packages/template-retail-react-app/app/pages/account/wishlist/partials/wishlist-secondary-button-group.test.js +++ b/packages/template-retail-react-app/app/pages/account/wishlist/partials/wishlist-secondary-button-group.test.js @@ -10,7 +10,8 @@ import {renderWithProviders} from '../../../../utils/test-utils' import WishlistSecondaryButtonGroup from './wishlist-secondary-button-group' import {screen, waitFor} from '@testing-library/react' import user from '@testing-library/user-event' -import useWishlist from '../../../../hooks/use-wishlist' +import {rest} from 'msw' +import {mockedProductLists, mockedWishListProducts} from '../index.mock' const mockData = { creationDate: '2021-09-13T23:29:23.396Z', @@ -340,44 +341,48 @@ const mockData = { type: 'wish_list' } -jest.mock('../../../../hooks/use-wishlist') - -const MockedComponent = () => { +const MockedComponent = (props) => { const product = mockData.customerProductListItems[0].product return ( - + ) } beforeEach(() => { jest.resetModules() + + global.server.use( + // For `useWishList` + rest.get('*/products', (req, res, ctx) => { + return res(ctx.delay(0), ctx.status(200), ctx.json(mockedWishListProducts)) + }), + rest.get('*/customers/:customerId/product-lists', (req, res, ctx) => { + return res(ctx.delay(0), ctx.status(200), ctx.json(mockedProductLists)) + }), + rest.delete( + '*/customers/:customerId/product-lists/:listId/items/:itemId', + (req, res, ctx) => { + return res(ctx.delay(0), ctx.status(204)) + } + ) + ) }) test('can remove item', async () => { - const removeItemMock = jest.fn().mockResolvedValue(true) - useWishlist.mockReturnValue({ - isInitialized: true, - isEmpty: false, - hasDetail: true, - data: mockData, - removeListItem: removeItemMock - }) - renderWithProviders() - const removeButton = screen.getByRole('button', { + const mockedHandler = jest.fn() + renderWithProviders() + + const removeButton = await screen.findByRole('button', { name: /remove/i }) - expect(removeButton).toBeInTheDocument() user.click(removeButton) - const confirmButton = screen.getByRole('button', {name: /yes, remove item/i}) + const confirmButton = await screen.findByRole('button', {name: /yes, remove item/i}) + user.click(confirmButton) + await waitFor(() => { - // Chakra UI renders multiple elements with toast title in DOM for accessibility. - // We need to assert the actual text within the alert - expect(confirmButton).toBeInTheDocument() + expect(mockedHandler).toHaveBeenCalled() }) - - user.click(confirmButton) - expect(removeItemMock).toHaveBeenCalled() }) diff --git a/packages/template-retail-react-app/app/pages/product-detail/index.jsx b/packages/template-retail-react-app/app/pages/product-detail/index.jsx index 6c796e1253..6ecd6ed4f5 100644 --- a/packages/template-retail-react-app/app/pages/product-detail/index.jsx +++ b/packages/template-retail-react-app/app/pages/product-detail/index.jsx @@ -119,7 +119,7 @@ const ProductDetail = () => { ) // TODO: DRY this handler when intl provider is available globally - const handleAddToWishlist = (quantity) => { + const handleAddToWishlist = (product, variant, quantity) => { createCustomerProductListItem.mutate( { parameters: { @@ -129,7 +129,7 @@ const ProductDetail = () => { body: { // NOTE: APi does not respect quantity, it always adds 1 quantity, - productId: urlParams.get('pid') || productId, + productId: variant?.productId || product?.id, public: false, priority: 1, type: 'product' @@ -262,9 +262,7 @@ const ProductDetail = () => { product={product} category={primaryCategory?.parentCategoryTree || []} addToCart={handleProductSetAddToCart} - addToWishlist={(product, variant, quantity) => - handleAddToWishlist(product, variant, quantity) - } + addToWishlist={handleAddToWishlist} isProductLoading={isProductLoading} isWishlistLoading={isWishlistLoading} validateOrderability={handleProductSetValidation} @@ -294,9 +292,7 @@ const ProductDetail = () => { {product: childProduct, variant, quantity} ]) } - addToWishlist={(product, variant, quantity) => - handleAddToWishlist(product, variant, quantity) - } + addToWishlist={handleAddToWishlist} onVariantSelected={(product, variant, quantity) => { if (quantity) { setProductSetSelection((previousState) => ({ @@ -333,9 +329,7 @@ const ProductDetail = () => { addToCart={(variant, quantity) => handleAddToCart([{product, variant, quantity}]) } - addToWishlist={(product, variant, quantity) => - handleAddToWishlist(product, variant, quantity) - } + addToWishlist={handleAddToWishlist} isProductLoading={isProductLoading} isWishlistLoading={isWishlistLoading} />
Top\r\nJeans\r\nNecklace\r\nShoes