From b8e5e03314520c4f1062b5a4e6a00d24a6e5a0de Mon Sep 17 00:00:00 2001 From: Revanth Kumar Date: Fri, 23 Apr 2021 16:30:23 -0500 Subject: [PATCH 1/9] Using useWishlist as a helper. --- .../CartPage/ProductListing/product.gql.js | 34 ----------------- .../CartPage/ProductListing/useProduct.js | 16 ++++++-- .../lib/talons/CartPage/useCartPage.js | 4 +- .../talons/Wishlist/Wishlist/product.gql.js | 38 +++++++++++++++++++ .../Wishlist}/useWishlist.ce.js | 21 ++++------ .../Wishlist}/useWishlist.ee.js | 21 ++++------ .../Wishlist}/wishlistFragment.ce.js | 0 .../Wishlist}/wishlistFragment.ee.js | 0 .../CartPage/ProductListing/product.js | 4 +- .../CartPage/ProductListing/productListing.js | 4 +- .../lib/components/CartPage/cartPage.js | 4 +- 11 files changed, 75 insertions(+), 71 deletions(-) create mode 100644 packages/peregrine/lib/talons/Wishlist/Wishlist/product.gql.js rename packages/peregrine/lib/talons/{CartPage/ProductListing => Wishlist/Wishlist}/useWishlist.ce.js (84%) rename packages/peregrine/lib/talons/{CartPage/ProductListing => Wishlist/Wishlist}/useWishlist.ee.js (89%) rename packages/peregrine/lib/talons/{CartPage/ProductListing => Wishlist/Wishlist}/wishlistFragment.ce.js (100%) rename packages/peregrine/lib/talons/{CartPage/ProductListing => Wishlist/Wishlist}/wishlistFragment.ee.js (100%) diff --git a/packages/peregrine/lib/talons/CartPage/ProductListing/product.gql.js b/packages/peregrine/lib/talons/CartPage/ProductListing/product.gql.js index 8321774591..0dd7a6756b 100644 --- a/packages/peregrine/lib/talons/CartPage/ProductListing/product.gql.js +++ b/packages/peregrine/lib/talons/CartPage/ProductListing/product.gql.js @@ -1,7 +1,5 @@ import { gql } from '@apollo/client'; -import { WishlistFragment } from './wishlistFragment'; - export const GET_CONFIGURABLE_THUMBNAIL_SOURCE = gql` query getConfigurableThumbnailSource { storeConfig { @@ -11,38 +9,6 @@ export const GET_CONFIGURABLE_THUMBNAIL_SOURCE = gql` } `; -export const GET_MULTIPLE_WISHLISTS_ENABLED = gql` - query getMultipleWishlistsEnabled { - storeConfig { - id - enable_multiple_wishlists - } - } -`; - -export const ADD_TO_WISHLIST = gql` - mutation addProductToWishlist( - $wishlistId: ID! - $itemOptions: WishlistItemInput! - ) { - addProductsToWishlist( - wishlistId: $wishlistId - wishlistItems: [$itemOptions] - ) { - user_errors { - code - message - } - wishlist { - ...WishlistFragment - } - } - } - ${WishlistFragment} -`; - export default { - addProductToWishlistMutation: ADD_TO_WISHLIST, - getMultipleWishlistsEnabledQuery: GET_MULTIPLE_WISHLISTS_ENABLED, getConfigurableThumbnailSource: GET_CONFIGURABLE_THUMBNAIL_SOURCE }; diff --git a/packages/peregrine/lib/talons/CartPage/ProductListing/useProduct.js b/packages/peregrine/lib/talons/CartPage/ProductListing/useProduct.js index 16144c27d4..49d63b2abf 100644 --- a/packages/peregrine/lib/talons/CartPage/ProductListing/useProduct.js +++ b/packages/peregrine/lib/talons/CartPage/ProductListing/useProduct.js @@ -5,7 +5,7 @@ import { useCartContext } from '@magento/peregrine/lib/context/cart'; import { useUserContext } from '@magento/peregrine/lib/context/user'; import configuredVariant from '@magento/peregrine/lib/util/configuredVariant'; -import { useWishlist } from './useWishlist'; +import { useWishlist } from '../../Wishlist/Wishlist/useWishlist'; import { deriveErrorMessage } from '../../../util/deriveErrorMessage'; import mergeOperations from '../../../util/shallowMerge'; @@ -36,7 +36,7 @@ import DEFAULT_OPERATIONS from './product.gql'; export const useProduct = props => { const { item, - onAddToWishlistSuccess, + updateWishlistToastProps, setActiveEditItem, setIsCartUpdating } = props; @@ -109,12 +109,22 @@ export const useProduct = props => { return null; }, [formatMessage, showLoginToast]); + const onWishlistUpdate = useCallback(async () => { + await removeItemFromCart({ + variables: { + cartId, + itemId: item.id + } + }); + }, [removeItemFromCart, item, cartId]); + const wishlistTalonProps = useWishlist({ removeItemFromCart, cartId, item, setDisplayError, - onAddToWishlistSuccess, + updateWishlistToastProps, + onWishlistUpdate, operations: props.operations }); const { diff --git a/packages/peregrine/lib/talons/CartPage/useCartPage.js b/packages/peregrine/lib/talons/CartPage/useCartPage.js index d0bc64ccdc..64faf2e819 100644 --- a/packages/peregrine/lib/talons/CartPage/useCartPage.js +++ b/packages/peregrine/lib/talons/CartPage/useCartPage.js @@ -50,7 +50,7 @@ export const useCartPage = props => { return (data && data.cart.items) || []; }, [data]); - const onAddToWishlistSuccess = useCallback(successToastProps => { + const updateWishlistToastProps = useCallback(successToastProps => { setWishlistSuccessProps(successToastProps); }, []); @@ -58,7 +58,7 @@ export const useCartPage = props => { cartItems, hasItems, isCartUpdating, - onAddToWishlistSuccess, + updateWishlistToastProps, setIsCartUpdating, shouldShowLoadingIndicator, wishlistSuccessProps diff --git a/packages/peregrine/lib/talons/Wishlist/Wishlist/product.gql.js b/packages/peregrine/lib/talons/Wishlist/Wishlist/product.gql.js new file mode 100644 index 0000000000..5c5d555d84 --- /dev/null +++ b/packages/peregrine/lib/talons/Wishlist/Wishlist/product.gql.js @@ -0,0 +1,38 @@ +import { gql } from '@apollo/client'; + +import { WishlistFragment } from './wishlistFragment'; + +export const GET_MULTIPLE_WISHLISTS_ENABLED = gql` + query getMultipleWishlistsEnabled { + storeConfig { + id + enable_multiple_wishlists + } + } +`; + +export const ADD_TO_WISHLIST = gql` + mutation addProductToWishlist( + $wishlistId: ID! + $itemOptions: WishlistItemInput! + ) { + addProductsToWishlist( + wishlistId: $wishlistId + wishlistItems: [$itemOptions] + ) { + user_errors { + code + message + } + wishlist { + ...WishlistFragment + } + } + } + ${WishlistFragment} +`; + +export default { + addProductToWishlistMutation: ADD_TO_WISHLIST, + getMultipleWishlistsEnabledQuery: GET_MULTIPLE_WISHLISTS_ENABLED +}; diff --git a/packages/peregrine/lib/talons/CartPage/ProductListing/useWishlist.ce.js b/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ce.js similarity index 84% rename from packages/peregrine/lib/talons/CartPage/ProductListing/useWishlist.ce.js rename to packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ce.js index 6c466ffd8b..bd7154cc12 100644 --- a/packages/peregrine/lib/talons/CartPage/ProductListing/useWishlist.ce.js +++ b/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ce.js @@ -7,10 +7,9 @@ import DEFAULT_OPERATIONS from './product.gql'; export const useWishlist = props => { const { - removeItemFromCart, - cartId, + onWishlistUpdate, item, - onAddToWishlistSuccess, + updateWishlistToastProps, setDisplayError } = props; @@ -43,7 +42,7 @@ export const useWishlist = props => { }); if (wishlistData) { - onAddToWishlistSuccess({ + updateWishlistToastProps({ type: 'info', message: formatMessage({ id: 'cartPage.wishlist.ce.successMessage', @@ -54,12 +53,9 @@ export const useWishlist = props => { }); } - await removeItemFromCart({ - variables: { - cartId, - itemId: item.id - } - }); + if (onWishlistUpdate) { + await onWishlistUpdate(); + } } catch (err) { console.error(err); @@ -68,11 +64,10 @@ export const useWishlist = props => { } }, [ addProductToWishlist, - removeItemFromCart, formatMessage, - cartId, + onWishlistUpdate, item, - onAddToWishlistSuccess, + updateWishlistToastProps, setDisplayError ]); diff --git a/packages/peregrine/lib/talons/CartPage/ProductListing/useWishlist.ee.js b/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ee.js similarity index 89% rename from packages/peregrine/lib/talons/CartPage/ProductListing/useWishlist.ee.js rename to packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ee.js index c2f18545bf..28969f17b6 100644 --- a/packages/peregrine/lib/talons/CartPage/ProductListing/useWishlist.ee.js +++ b/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ee.js @@ -7,10 +7,9 @@ import DEFAULT_OPERATIONS from './product.gql'; export const useWishlist = props => { const { - removeItemFromCart, - cartId, + onWishlistUpdate, item, - onAddToWishlistSuccess, + updateWishlistToastProps, setDisplayError } = props; @@ -69,7 +68,7 @@ export const useWishlist = props => { if (wishlistData) { const { name } = wishlistData.addProductsToWishlist.wishlist; - onAddToWishlistSuccess({ + updateWishlistToastProps({ type: 'info', message: formatMessage( { @@ -82,12 +81,9 @@ export const useWishlist = props => { }); } - await removeItemFromCart({ - variables: { - cartId, - itemId: item.id - } - }); + if (onWishlistUpdate) { + await onWishlistUpdate(); + } } catch (err) { console.error(err); @@ -98,10 +94,9 @@ export const useWishlist = props => { addProductToWishlist, isMultipleWishlistsEnabled, formatMessage, - removeItemFromCart, - cartId, + onWishlistUpdate, item, - onAddToWishlistSuccess, + updateWishlistToastProps, setDisplayError ]); diff --git a/packages/peregrine/lib/talons/CartPage/ProductListing/wishlistFragment.ce.js b/packages/peregrine/lib/talons/Wishlist/Wishlist/wishlistFragment.ce.js similarity index 100% rename from packages/peregrine/lib/talons/CartPage/ProductListing/wishlistFragment.ce.js rename to packages/peregrine/lib/talons/Wishlist/Wishlist/wishlistFragment.ce.js diff --git a/packages/peregrine/lib/talons/CartPage/ProductListing/wishlistFragment.ee.js b/packages/peregrine/lib/talons/Wishlist/Wishlist/wishlistFragment.ee.js similarity index 100% rename from packages/peregrine/lib/talons/CartPage/ProductListing/wishlistFragment.ee.js rename to packages/peregrine/lib/talons/Wishlist/Wishlist/wishlistFragment.ee.js diff --git a/packages/venia-ui/lib/components/CartPage/ProductListing/product.js b/packages/venia-ui/lib/components/CartPage/ProductListing/product.js index a6a4d373a3..c3fb9262f7 100644 --- a/packages/venia-ui/lib/components/CartPage/ProductListing/product.js +++ b/packages/venia-ui/lib/components/CartPage/ProductListing/product.js @@ -25,7 +25,7 @@ const InfoIcon = ; const Product = props => { const { item, - onAddToWishlistSuccess, + updateWishlistToastProps, setActiveEditItem, setIsCartUpdating } = props; @@ -38,7 +38,7 @@ const Product = props => { removeItemMutation: REMOVE_ITEM_MUTATION, updateItemQuantityMutation: UPDATE_QUANTITY_MUTATION }, - onAddToWishlistSuccess, + updateWishlistToastProps, setActiveEditItem, setIsCartUpdating }); diff --git a/packages/venia-ui/lib/components/CartPage/ProductListing/productListing.js b/packages/venia-ui/lib/components/CartPage/ProductListing/productListing.js index 58b6af271b..7a28d64a39 100644 --- a/packages/venia-ui/lib/components/CartPage/ProductListing/productListing.js +++ b/packages/venia-ui/lib/components/CartPage/ProductListing/productListing.js @@ -26,7 +26,7 @@ const EditModal = React.lazy(() => import('./EditModal')); * import ProductListing from "@magento/venia-ui/lib/components/CartPage/ProductListing"; */ const ProductListing = props => { - const { onAddToWishlistSuccess, setIsCartUpdating } = props; + const { updateWishlistToastProps, setIsCartUpdating } = props; const talonProps = useProductListing({ queries: { getProductListing: GET_PRODUCT_LISTING @@ -54,7 +54,7 @@ const ProductListing = props => { key={product.id} setActiveEditItem={setActiveEditItem} setIsCartUpdating={setIsCartUpdating} - onAddToWishlistSuccess={onAddToWishlistSuccess} + updateWishlistToastProps={updateWishlistToastProps} /> )); diff --git a/packages/venia-ui/lib/components/CartPage/cartPage.js b/packages/venia-ui/lib/components/CartPage/cartPage.js index 11b283f17d..cb899eb7d4 100644 --- a/packages/venia-ui/lib/components/CartPage/cartPage.js +++ b/packages/venia-ui/lib/components/CartPage/cartPage.js @@ -45,7 +45,7 @@ const CartPage = props => { cartItems, hasItems, isCartUpdating, - onAddToWishlistSuccess, + updateWishlistToastProps, setIsCartUpdating, shouldShowLoadingIndicator, wishlistSuccessProps @@ -68,7 +68,7 @@ const CartPage = props => { const productListing = hasItems ? ( ) : ( From 1aa9114203fa81c7122fe727292990102426bf51 Mon Sep 17 00:00:00 2001 From: Revanth Kumar Date: Fri, 23 Apr 2021 17:51:35 -0500 Subject: [PATCH 2/9] CE wishlist refactor. --- .../CartPage/ProductListing/useProduct.js | 10 ++++++- .../ProductFullDetail/useProductFullDetail.js | 6 ++--- .../Wishlist/Wishlist/useWishlist.ce.js | 27 ++++++++++++------- .../ProductFullDetail/productFullDetail.js | 5 ++-- .../WishlistButton/wishlistButton.ce.js | 12 ++++----- 5 files changed, 38 insertions(+), 22 deletions(-) diff --git a/packages/peregrine/lib/talons/CartPage/ProductListing/useProduct.js b/packages/peregrine/lib/talons/CartPage/ProductListing/useProduct.js index 49d63b2abf..269b40fae8 100644 --- a/packages/peregrine/lib/talons/CartPage/ProductListing/useProduct.js +++ b/packages/peregrine/lib/talons/CartPage/ProductListing/useProduct.js @@ -118,10 +118,18 @@ export const useProduct = props => { }); }, [removeItemFromCart, item, cartId]); + const wishlistItemOptions = { + ...item, + sku: item.product.sku, + selected_options: item.configurable_options.map( + option => option.configurable_product_option_value_uid + ) + }; + const wishlistTalonProps = useWishlist({ removeItemFromCart, cartId, - item, + item: wishlistItemOptions, setDisplayError, updateWishlistToastProps, onWishlistUpdate, diff --git a/packages/peregrine/lib/talons/ProductFullDetail/useProductFullDetail.js b/packages/peregrine/lib/talons/ProductFullDetail/useProductFullDetail.js index d136d6fb36..db2bc0acaf 100644 --- a/packages/peregrine/lib/talons/ProductFullDetail/useProductFullDetail.js +++ b/packages/peregrine/lib/talons/ProductFullDetail/useProductFullDetail.js @@ -390,10 +390,10 @@ export const useProductFullDetail = props => { // Normalization object for product details we need for rendering. const productDetails = { - description: product.description, - name: product.name, + ...product, + quantity: 1, price: productPrice, - sku: product.sku + selected_options: selectedOptionsArray }; const derivedErrorMessage = useMemo( diff --git a/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ce.js b/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ce.js index bd7154cc12..add2e7fa08 100644 --- a/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ce.js +++ b/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ce.js @@ -1,4 +1,4 @@ -import { useCallback } from 'react'; +import { useState, useEffect, useCallback } from 'react'; import { useMutation } from '@apollo/client'; import { useIntl } from 'react-intl'; @@ -16,6 +16,8 @@ export const useWishlist = props => { const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations); const { addProductToWishlistMutation } = operations; + const [isItemAdded, setIsItemAdded] = useState(false); + const [addProductToWishlist, { loading, called, error }] = useMutation( addProductToWishlistMutation ); @@ -23,11 +25,7 @@ export const useWishlist = props => { const { formatMessage } = useIntl(); const handleAddToWishlist = useCallback(async () => { - const sku = item.product.sku; - const quantity = item.quantity; - const selected_options = item.configurable_options.map( - option => option.configurable_product_option_value_uid - ); + const { sku, quantity, selected_options } = item; try { const { data: wishlistData } = await addProductToWishlist({ @@ -41,7 +39,9 @@ export const useWishlist = props => { } }); - if (wishlistData) { + setIsItemAdded(true); + + if (wishlistData && updateWishlistToastProps) { updateWishlistToastProps({ type: 'info', message: formatMessage({ @@ -60,7 +60,9 @@ export const useWishlist = props => { console.error(err); // Make sure any errors from the mutation are displayed. - setDisplayError(true); + if (setDisplayError) { + setDisplayError(true); + } } }, [ addProductToWishlist, @@ -71,10 +73,17 @@ export const useWishlist = props => { setDisplayError ]); + useEffect(() => { + // If a user changes selections, let them add that combination to a list. + if (item.selected_options) setIsItemAdded(false); + }, [item.selected_options]); + return { handleAddToWishlist, loading, called, - error + error, + isDisabled: isItemAdded || loading, + isItemAdded }; }; diff --git a/packages/venia-ui/lib/components/ProductFullDetail/productFullDetail.js b/packages/venia-ui/lib/components/ProductFullDetail/productFullDetail.js index 4278e5e294..865281c7c6 100644 --- a/packages/venia-ui/lib/components/ProductFullDetail/productFullDetail.js +++ b/packages/venia-ui/lib/components/ProductFullDetail/productFullDetail.js @@ -49,8 +49,7 @@ const ProductFullDetail = props => { isSupportedProductType, mediaGalleryEntries, productDetails, - shouldShowWishlistButton, - wishlistItemOptions + shouldShowWishlistButton } = talonProps; const { formatMessage } = useIntl(); @@ -128,7 +127,7 @@ const ProductFullDetail = props => { const maybeWishlistButton = shouldShowWishlistButton ? ( - + ) : null; diff --git a/packages/venia-ui/lib/components/Wishlist/WishlistButton/wishlistButton.ce.js b/packages/venia-ui/lib/components/Wishlist/WishlistButton/wishlistButton.ce.js index 29063d48a5..ad856a5a34 100644 --- a/packages/venia-ui/lib/components/Wishlist/WishlistButton/wishlistButton.ce.js +++ b/packages/venia-ui/lib/components/Wishlist/WishlistButton/wishlistButton.ce.js @@ -3,7 +3,7 @@ import { AlertCircle, Heart, Check } from 'react-feather'; import { useIntl } from 'react-intl'; import { useToasts } from '@magento/peregrine'; -import { useWishlistButton } from '@magento/peregrine/lib/talons/Wishlist/WishlistButton/useWishlistButton'; +import { useWishlist } from '@magento/peregrine/lib/talons/Wishlist/Wishlist/useWishlist'; import { mergeClasses } from '@magento/venia-ui/lib/classify'; import Icon from '@magento/venia-ui/lib/components/Icon'; @@ -16,13 +16,13 @@ const ErrorIcon = ; const WishlistButton = props => { const classes = mergeClasses(defaultClasses, props.classes); - const talonProps = useWishlistButton({ itemOptions: props.itemOptions }); + const talonProps = useWishlist({ item: props.item }); const { - addProductError, - handleClick, + handleAddToWishlist, isDisabled, - isItemAdded + isItemAdded, + error: addProductError } = talonProps; const { formatMessage } = useIntl(); @@ -77,7 +77,7 @@ const WishlistButton = props => { disabled={isDisabled} type="button" className={classes.button} - onClick={handleClick} + onClick={handleAddToWishlist} > {iconElement} From 28a70d06fad24cd766f420c0827c9ba4d53a3865 Mon Sep 17 00:00:00 2001 From: Revanth Kumar Date: Fri, 23 Apr 2021 18:46:32 -0500 Subject: [PATCH 3/9] EE wishlist refactor. --- .../talons/Wishlist/Wishlist/product.gql.js | 26 ++- .../Wishlist/Wishlist/useWishlist.ee.js | 157 +++++++++++------- .../WishlistDialog/useWishlistDialog.js | 32 +--- .../WishlistDialog/wishlistDialog.gql.js | 19 +-- .../WishlistButton/wishlistButton.ee.js | 17 +- .../Wishlist/WishlistDialog/wishlistDialog.js | 22 +-- 6 files changed, 134 insertions(+), 139 deletions(-) diff --git a/packages/peregrine/lib/talons/Wishlist/Wishlist/product.gql.js b/packages/peregrine/lib/talons/Wishlist/Wishlist/product.gql.js index 5c5d555d84..023724840a 100644 --- a/packages/peregrine/lib/talons/Wishlist/Wishlist/product.gql.js +++ b/packages/peregrine/lib/talons/Wishlist/Wishlist/product.gql.js @@ -2,15 +2,6 @@ import { gql } from '@apollo/client'; import { WishlistFragment } from './wishlistFragment'; -export const GET_MULTIPLE_WISHLISTS_ENABLED = gql` - query getMultipleWishlistsEnabled { - storeConfig { - id - enable_multiple_wishlists - } - } -`; - export const ADD_TO_WISHLIST = gql` mutation addProductToWishlist( $wishlistId: ID! @@ -32,6 +23,23 @@ export const ADD_TO_WISHLIST = gql` ${WishlistFragment} `; +export const GET_MULTIPLE_WISHLISTS_ENABLED = gql` + query getWishlistsDialogData { + storeConfig { + id + enable_multiple_wishlists + maximum_number_of_wishlists + } + customer { + id + wishlists { + id + name + } + } + } +`; + export default { addProductToWishlistMutation: ADD_TO_WISHLIST, getMultipleWishlistsEnabledQuery: GET_MULTIPLE_WISHLISTS_ENABLED diff --git a/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ee.js b/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ee.js index 28969f17b6..444adf4016 100644 --- a/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ee.js +++ b/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ee.js @@ -1,4 +1,4 @@ -import { useCallback, useMemo } from 'react'; +import { useState, useEffect, useCallback, useMemo } from 'react'; import { useQuery, useMutation } from '@apollo/client'; import { useIntl } from 'react-intl'; @@ -19,91 +19,126 @@ export const useWishlist = props => { addProductToWishlistMutation } = operations; + const [isItemAdded, setIsItemAdded] = useState(false); + const [isModalOpen, setIsModalOpen] = useState(false); + const { formatMessage } = useIntl(); - const { data: storeConfigData } = useQuery( - getMultipleWishlistsEnabledQuery, + const [addProductToWishlist, { loading, called, error }] = useMutation( + addProductToWishlistMutation, { - fetchPolicy: 'cache-and-network', - nextFetchPolicy: 'cache-first' + refetchQueries: [{ query: getMultipleWishlistsEnabledQuery }] } ); - const [addProductToWishlist, { loading, called, error }] = useMutation( - addProductToWishlistMutation + const { data: storeConfigData } = useQuery( + getMultipleWishlistsEnabledQuery, + { + fetchPolicy: 'cache-and-network' + } ); + // enable_multiple_wishlists is a string "1" or "0". See documentation here: + // https://devdocs.magento.com/guides/v2.4/graphql/mutations/create-wishlist.html const isMultipleWishlistsEnabled = useMemo(() => { return ( storeConfigData && - storeConfigData.storeConfig.enable_multiple_wishlists === '1' + !!storeConfigData.storeConfig.enable_multiple_wishlists && + storeConfigData.storeConfig.maximum_number_of_wishlists > + storeConfigData.customer.wishlists.length ); }, [storeConfigData]); - const handleAddToWishlist = useCallback(async () => { - const sku = item.product.sku; - const quantity = item.quantity; - const selected_options = item.configurable_options.map( - option => option.configurable_product_option_value_uid - ); + const handleAddToWishlist = useCallback( + async (wishlistId = '0') => { + const { sku, quantity, selected_options } = item; - /** - * When we work on when we work on https://jira.corp.magento.com/browse/PWA-1599 - * this logic will change to the wishlist the user would like to add the item to. - */ - const wishlistId = isMultipleWishlistsEnabled ? '0' : '0'; - - try { - const { data: wishlistData } = await addProductToWishlist({ - variables: { - wishlistId, - itemOptions: { - sku, - quantity, - selected_options + try { + const { data: wishlistData } = await addProductToWishlist({ + variables: { + wishlistId, + itemOptions: { + sku, + quantity, + selected_options + } } - } - }); - - if (wishlistData) { - const { name } = wishlistData.addProductsToWishlist.wishlist; - - updateWishlistToastProps({ - type: 'info', - message: formatMessage( - { - id: 'cartPage.wishlist.ee.successMessage', - defaultMessage: `Item successfully added to ${name}.` - }, - { wishlistName: name } - ), - timeout: 5000 }); - } - if (onWishlistUpdate) { - await onWishlistUpdate(); + setIsItemAdded(true); + + if (wishlistData && updateWishlistToastProps) { + const { + name + } = wishlistData.addProductsToWishlist.wishlist; + + updateWishlistToastProps({ + type: 'info', + message: formatMessage( + { + id: 'cartPage.wishlist.ee.successMessage', + defaultMessage: `Item successfully added to ${name}.` + }, + { wishlistName: name } + ), + timeout: 5000 + }); + } + + if (isMultipleWishlistsEnabled) { + setIsModalOpen(false); + } + + if (onWishlistUpdate) { + await onWishlistUpdate(); + } + } catch (err) { + console.error(err); + + // Make sure any errors from the mutation are displayed. + if (setDisplayError) { + setDisplayError(true); + } } - } catch (err) { - console.error(err); + }, + [ + addProductToWishlist, + formatMessage, + onWishlistUpdate, + item, + updateWishlistToastProps, + setDisplayError, + isMultipleWishlistsEnabled + ] + ); - // Make sure any errors from the mutation are displayed. - setDisplayError(true); + const handleModalOpen = useCallback(() => { + setIsModalOpen(true); + }, []); + + const handleModalClose = useCallback(success => { + setIsModalOpen(false); + + // only set item added true if someone calls handleModalClose(true) + if (success === true) { + setIsItemAdded(true); } - }, [ - addProductToWishlist, - isMultipleWishlistsEnabled, - formatMessage, - onWishlistUpdate, - item, - updateWishlistToastProps, - setDisplayError - ]); + }, []); + + useEffect(() => { + // If a user changes selections, let them add that combination to a list. + if (item.selected_options) setIsItemAdded(false); + }, [item.selected_options]); return { handleAddToWishlist, loading, called, - error + error, + isDisabled: isItemAdded || loading, + isItemAdded, + isModalOpen, + handleModalOpen, + handleModalClose }; }; diff --git a/packages/peregrine/lib/talons/Wishlist/WishlistDialog/useWishlistDialog.js b/packages/peregrine/lib/talons/Wishlist/WishlistDialog/useWishlistDialog.js index 722c938d45..a982f3aa97 100644 --- a/packages/peregrine/lib/talons/Wishlist/WishlistDialog/useWishlistDialog.js +++ b/packages/peregrine/lib/talons/Wishlist/WishlistDialog/useWishlistDialog.js @@ -1,11 +1,11 @@ import { useCallback, useMemo, useState } from 'react'; -import { useQuery, useMutation } from '@apollo/client'; +import { useQuery } from '@apollo/client'; import mergeOperations from '@magento/peregrine/lib/util/shallowMerge'; import DEFAULT_OPERATIONS from './wishlistDialog.gql'; export const useWishlistDialog = props => { - const { itemOptions, onClose } = props; + const { onClose } = props; const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations); const [isFormOpen, setIsFormOpen] = useState(false); @@ -14,13 +14,6 @@ export const useWishlistDialog = props => { fetchPolicy: 'cache-and-network' }); - const [ - addProductToWishlist, - { loading: isAddLoading, error: addProductError } - ] = useMutation(operations.addProductToWishlistMutation, { - refetchQueries: [{ query: operations.getWishlistsQuery }] - }); - // enable_multiple_wishlists is a string "1" or "0". See documentation here: // https://devdocs.magento.com/guides/v2.4/graphql/mutations/create-wishlist.html const canCreateWishlist = useMemo(() => { @@ -32,24 +25,6 @@ export const useWishlistDialog = props => { ); }, [wishlistsData]); - const handleAddToWishlist = useCallback( - async wishlistId => { - try { - await addProductToWishlist({ - variables: { - wishlistId, - itemOptions - } - }); - onClose(true); - setIsFormOpen(false); - } catch (err) { - console.log(err); - } - }, - [addProductToWishlist, itemOptions, onClose] - ); - const handleNewListClick = useCallback(() => { setIsFormOpen(true); }, []); @@ -64,13 +39,10 @@ export const useWishlistDialog = props => { }, [onClose]); return { - formErrors: [addProductError], canCreateWishlist, - handleAddToWishlist, handleCancel, handleCancelNewList, handleNewListClick, - isAddLoading, isFormOpen, wishlistsData }; diff --git a/packages/peregrine/lib/talons/Wishlist/WishlistDialog/wishlistDialog.gql.js b/packages/peregrine/lib/talons/Wishlist/WishlistDialog/wishlistDialog.gql.js index df05e94146..f80c27554e 100644 --- a/packages/peregrine/lib/talons/Wishlist/WishlistDialog/wishlistDialog.gql.js +++ b/packages/peregrine/lib/talons/Wishlist/WishlistDialog/wishlistDialog.gql.js @@ -1,22 +1,5 @@ import { gql } from '@apollo/client'; -export const ADD_TO_WISHLIST = gql` - mutation addProductToWishlist( - $wishlistId: ID! - $itemOptions: WishlistItemInput! - ) { - addProductsToWishlist( - wishlistId: $wishlistId - wishlistItems: [$itemOptions] - ) { - user_errors { - code - message - } - } - } -`; - export const GET_WISHLISTS = gql` query getWishlistsDialogData { storeConfig { @@ -33,7 +16,7 @@ export const GET_WISHLISTS = gql` } } `; + export default { - addProductToWishlistMutation: ADD_TO_WISHLIST, getWishlistsQuery: GET_WISHLISTS }; diff --git a/packages/venia-ui/lib/components/Wishlist/WishlistButton/wishlistButton.ee.js b/packages/venia-ui/lib/components/Wishlist/WishlistButton/wishlistButton.ee.js index a6854b9321..a4dceb8c41 100644 --- a/packages/venia-ui/lib/components/Wishlist/WishlistButton/wishlistButton.ee.js +++ b/packages/venia-ui/lib/components/Wishlist/WishlistButton/wishlistButton.ee.js @@ -3,7 +3,7 @@ import { AlertCircle, Heart, Check } from 'react-feather'; import { useIntl } from 'react-intl'; import { useToasts } from '@magento/peregrine'; -import { useWishlistButton } from '@magento/peregrine/lib/talons/Wishlist/WishlistButton/useWishlistButton'; +import { useWishlist } from '@magento/peregrine/lib/talons/Wishlist/Wishlist/useWishlist'; import { mergeClasses } from '@magento/venia-ui/lib/classify'; import Icon from '@magento/venia-ui/lib/components/Icon'; @@ -17,15 +17,17 @@ const ErrorIcon = ; const WishlistButton = props => { const classes = mergeClasses(defaultClasses, props.classes); - const talonProps = useWishlistButton({ itemOptions: props.itemOptions }); + const talonProps = useWishlist({ item: props.item }); const { - addProductError, - handleButtonClick, + handleModalOpen, handleModalClose, isDisabled, isItemAdded, - isModalOpen + isModalOpen, + handleAddToWishlist, + error: addProductError, + loading: isLoading } = talonProps; const { formatMessage } = useIntl(); @@ -81,7 +83,7 @@ const WishlistButton = props => { disabled={isDisabled} type="button" className={classes.button} - onClick={handleButtonClick} + onClick={handleModalOpen} > {iconElement} @@ -92,6 +94,9 @@ const WishlistButton = props => { isOpen={isModalOpen} onClose={handleModalClose} itemOptions={props.itemOptions} + onSubmit={handleAddToWishlist} + errors={[addProductError]} + isLoading={isLoading} /> diff --git a/packages/venia-ui/lib/components/Wishlist/WishlistDialog/wishlistDialog.js b/packages/venia-ui/lib/components/Wishlist/WishlistDialog/wishlistDialog.js index 4287f69f80..6b5175d5d2 100644 --- a/packages/venia-ui/lib/components/Wishlist/WishlistDialog/wishlistDialog.js +++ b/packages/venia-ui/lib/components/Wishlist/WishlistDialog/wishlistDialog.js @@ -15,7 +15,7 @@ import defaultClasses from './wishlistDialog.css'; import { arrayOf, bool, func, number, shape, string } from 'prop-types'; const WishlistDialog = props => { - const { isOpen, itemOptions, onClose } = props; + const { isOpen, itemOptions, onClose, onSubmit, errors, isLoading } = props; const classes = mergeClasses(defaultClasses, props.classes); const talonProps = useWishlistDialog({ @@ -25,12 +25,9 @@ const WishlistDialog = props => { const { canCreateWishlist, - formErrors, - handleAddToWishlist, handleCancel, handleNewListClick, handleCancelNewList, - isAddLoading, isFormOpen, wishlistsData } = talonProps; @@ -50,8 +47,8 @@ const WishlistDialog = props => {
  • {name} @@ -62,12 +59,7 @@ const WishlistDialog = props => { } else { return null; } - }, [ - classes.existingWishlists, - handleAddToWishlist, - isAddLoading, - wishlistsData - ]); + }, [classes.existingWishlists, onSubmit, isLoading, wishlistsData]); const shouldRenderForm = useCallback(() => !!isFormOpen, [isFormOpen]); @@ -82,8 +74,8 @@ const WishlistDialog = props => { @@ -105,7 +97,7 @@ const WishlistDialog = props => { classes={{ root: classes.formErrors }} - errors={formErrors} + errors={errors} /> {maybeListsElement} {maybeNewListElement} From e36a09df196de969c81ae8009cd7b08f94afa346 Mon Sep 17 00:00:00 2001 From: Revanth Kumar Date: Tue, 27 Apr 2021 10:08:32 -0500 Subject: [PATCH 4/9] onWishlistUpdateError -> setDisplayError --- .../lib/talons/CartPage/ProductListing/useProduct.js | 6 +++++- .../lib/talons/Wishlist/Wishlist/useWishlist.ce.js | 8 ++++---- .../lib/talons/Wishlist/Wishlist/useWishlist.ee.js | 9 ++++----- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/peregrine/lib/talons/CartPage/ProductListing/useProduct.js b/packages/peregrine/lib/talons/CartPage/ProductListing/useProduct.js index 269b40fae8..5af832a96e 100644 --- a/packages/peregrine/lib/talons/CartPage/ProductListing/useProduct.js +++ b/packages/peregrine/lib/talons/CartPage/ProductListing/useProduct.js @@ -126,11 +126,15 @@ export const useProduct = props => { ) }; + const onWishlistUpdateError = useCallback(() => { + setDisplayError(true); + }, []); + const wishlistTalonProps = useWishlist({ removeItemFromCart, cartId, item: wishlistItemOptions, - setDisplayError, + onWishlistUpdateError, updateWishlistToastProps, onWishlistUpdate, operations: props.operations diff --git a/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ce.js b/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ce.js index add2e7fa08..cc01eb85a4 100644 --- a/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ce.js +++ b/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ce.js @@ -10,7 +10,7 @@ export const useWishlist = props => { onWishlistUpdate, item, updateWishlistToastProps, - setDisplayError + onWishlistUpdateError } = props; const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations); @@ -60,8 +60,8 @@ export const useWishlist = props => { console.error(err); // Make sure any errors from the mutation are displayed. - if (setDisplayError) { - setDisplayError(true); + if (onWishlistUpdateError) { + onWishlistUpdateError(true); } } }, [ @@ -70,7 +70,7 @@ export const useWishlist = props => { onWishlistUpdate, item, updateWishlistToastProps, - setDisplayError + onWishlistUpdateError ]); useEffect(() => { diff --git a/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ee.js b/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ee.js index 444adf4016..f2eb5c6cdf 100644 --- a/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ee.js +++ b/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ee.js @@ -10,7 +10,7 @@ export const useWishlist = props => { onWishlistUpdate, item, updateWishlistToastProps, - setDisplayError + onWishlistUpdateError } = props; const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations); @@ -95,9 +95,8 @@ export const useWishlist = props => { } catch (err) { console.error(err); - // Make sure any errors from the mutation are displayed. - if (setDisplayError) { - setDisplayError(true); + if (onWishlistUpdateError) { + onWishlistUpdateError(err); } } }, @@ -107,7 +106,7 @@ export const useWishlist = props => { onWishlistUpdate, item, updateWishlistToastProps, - setDisplayError, + onWishlistUpdateError, isMultipleWishlistsEnabled ] ); From b922a3d44fd0619950005e9bc1fe3f6b97c438d3 Mon Sep 17 00:00:00 2001 From: Revanth Kumar Date: Tue, 27 Apr 2021 10:23:54 -0500 Subject: [PATCH 5/9] Minor. --- .../CartPage/ProductListing/useProduct.js | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/peregrine/lib/talons/CartPage/ProductListing/useProduct.js b/packages/peregrine/lib/talons/CartPage/ProductListing/useProduct.js index 5af832a96e..d12a3a1aae 100644 --- a/packages/peregrine/lib/talons/CartPage/ProductListing/useProduct.js +++ b/packages/peregrine/lib/talons/CartPage/ProductListing/useProduct.js @@ -165,16 +165,15 @@ export const useProduct = props => { updateItemLoading ]); - const handleSaveForLater = useCallback( - async (...args) => { - if (!isSignedIn) { - setShowLoginToast(current => ++current); - } else { - await handleAddToWishlist(args); - } - }, - [isSignedIn, handleAddToWishlist] - ); + const handleSaveForLater = useCallback(async () => { + if (!isSignedIn) { + setShowLoginToast(current => ++current); + } else { + // should update this to the wishlist selected by the user in + // https://jira.corp.magento.com/browse/PWA-1599 + await handleAddToWishlist('0'); + } + }, [isSignedIn, handleAddToWishlist]); const derivedErrorMessage = useMemo(() => { return ( From f2a53b5d9dfc1e3d06267369dc9a143084a8047b Mon Sep 17 00:00:00 2001 From: Revanth Kumar Date: Tue, 27 Apr 2021 10:25:26 -0500 Subject: [PATCH 6/9] Minor. --- .../peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ce.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ce.js b/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ce.js index cc01eb85a4..fe977eb7fb 100644 --- a/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ce.js +++ b/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ce.js @@ -59,9 +59,8 @@ export const useWishlist = props => { } catch (err) { console.error(err); - // Make sure any errors from the mutation are displayed. if (onWishlistUpdateError) { - onWishlistUpdateError(true); + onWishlistUpdateError(err); } } }, [ From 9de4e64d0e88d5f941e078fd09d5256672844590 Mon Sep 17 00:00:00 2001 From: Revanth Kumar Date: Tue, 27 Apr 2021 16:11:53 -0500 Subject: [PATCH 7/9] Added initial test. --- .../Wishlist/__tests__/useWishlist.ce.spec.js | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 packages/peregrine/lib/talons/Wishlist/Wishlist/__tests__/useWishlist.ce.spec.js diff --git a/packages/peregrine/lib/talons/Wishlist/Wishlist/__tests__/useWishlist.ce.spec.js b/packages/peregrine/lib/talons/Wishlist/Wishlist/__tests__/useWishlist.ce.spec.js new file mode 100644 index 0000000000..b4af8c84a0 --- /dev/null +++ b/packages/peregrine/lib/talons/Wishlist/Wishlist/__tests__/useWishlist.ce.spec.js @@ -0,0 +1,59 @@ +import React from 'react'; +// import { useMutation } from '@apollo/client'; + +import { useWishlist } from '../useWishlist.ce'; +import createTestInstance from '../../../../util/createTestInstance'; + +jest.mock('@apollo/client', () => + jest.fn().mockReturnValue([ + jest.fn(), + { + loading: false, + called: false, + error: null + } + ]) +); + +const defaultProps = { + item: { + sku: 'sku', + quantity: '1', + selected_options: 'selected options' + }, + onWishlistUpdate: jest.fn(), + onWishlistUpdateError: jest.fn(), + updateWishlistToastProps: jest.fn() +}; + +const Component = props => { + const talonProps = useWishlist(props); + + return ; +}; + +const getTalonProps = props => { + const tree = createTestInstance(); + const { root } = tree; + const { talonProps } = root.findByType('i').props; + + const update = newProps => { + tree.update(); + + return root.findByType('i').props.talonProps; + }; + + return { talonProps, tree, update }; +}; + +test('should return correct shape', () => { + const { talonProps } = getTalonProps(defaultProps); + + expect(talonProps).toMatchSnapshot(); +}); + +// test('handleAddToWishlist') + +// test('should use necessary translations', () => { + +// }) From 1c9aed8e64cbf727c9387a7d01ced8507426f987 Mon Sep 17 00:00:00 2001 From: Revanth Kumar Date: Wed, 28 Apr 2021 16:12:08 -0500 Subject: [PATCH 8/9] Added useWishlist.ce tests. --- .../__snapshots__/useWishlist.ce.spec.js.snap | 54 ++++++++ .../Wishlist/__tests__/useWishlist.ce.spec.js | 129 ++++++++++++++++-- 2 files changed, 172 insertions(+), 11 deletions(-) create mode 100644 packages/peregrine/lib/talons/Wishlist/Wishlist/__tests__/__snapshots__/useWishlist.ce.spec.js.snap diff --git a/packages/peregrine/lib/talons/Wishlist/Wishlist/__tests__/__snapshots__/useWishlist.ce.spec.js.snap b/packages/peregrine/lib/talons/Wishlist/Wishlist/__tests__/__snapshots__/useWishlist.ce.spec.js.snap new file mode 100644 index 0000000000..5fde065133 --- /dev/null +++ b/packages/peregrine/lib/talons/Wishlist/Wishlist/__tests__/__snapshots__/useWishlist.ce.spec.js.snap @@ -0,0 +1,54 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should return correct shape 1`] = ` +Object { + "called": true, + "error": null, + "handleAddToWishlist": [Function], + "isDisabled": true, + "isItemAdded": false, + "loading": true, +} +`; + +exports[`testing handleAddToWishlist should add the product to list 1`] = ` +Array [ + Object { + "variables": Object { + "itemOptions": Object { + "quantity": "1", + "selected_options": "selected options", + "sku": "sku", + }, + "wishlistId": "0", + }, + }, +] +`; + +exports[`testing handleAddToWishlist should call onWishlistUpdateError if there was an error calling the mutation 1`] = ` +Array [ + "Something went wrong", +] +`; + +exports[`testing handleAddToWishlist should call updateWishlistToastProps 1`] = ` +Array [ + Object { + "message": "Item successfully added to your favorites list.", + "timeout": 5000, + "type": "info", + }, +] +`; + +exports[`testing handleAddToWishlist should use necessary translations 1`] = ` +Array [ + Array [ + Object { + "defaultMessage": "Item successfully added to your favorites list.", + "id": "cartPage.wishlist.ce.successMessage", + }, + ], +] +`; diff --git a/packages/peregrine/lib/talons/Wishlist/Wishlist/__tests__/useWishlist.ce.spec.js b/packages/peregrine/lib/talons/Wishlist/Wishlist/__tests__/useWishlist.ce.spec.js index b4af8c84a0..da7e2b1aa0 100644 --- a/packages/peregrine/lib/talons/Wishlist/Wishlist/__tests__/useWishlist.ce.spec.js +++ b/packages/peregrine/lib/talons/Wishlist/Wishlist/__tests__/useWishlist.ce.spec.js @@ -1,19 +1,34 @@ import React from 'react'; -// import { useMutation } from '@apollo/client'; +import { useMutation } from '@apollo/client'; import { useWishlist } from '../useWishlist.ce'; import createTestInstance from '../../../../util/createTestInstance'; +import { useIntl } from 'react-intl'; -jest.mock('@apollo/client', () => - jest.fn().mockReturnValue([ - jest.fn(), +jest.mock('@apollo/client', () => ({ + useMutation: jest.fn().mockReturnValue([ + jest.fn().mockResolvedValue({ data: 'data' }), { loading: false, called: false, error: null } ]) -); +})); + +jest.mock('react-intl', () => ({ + useIntl: jest.fn().mockReturnValue({ formatMessage: jest.fn() }) +})); + +jest.mock('../../../../util/shallowMerge', () => () => ({ + addProductToWishlistMutation: 'addProductToWishlistMutation' +})); + +jest.mock('../product.gql', () => () => ({})); + +const onWishlistUpdate = jest.fn(); +const onWishlistUpdateError = jest.fn(); +const updateWishlistToastProps = jest.fn(); const defaultProps = { item: { @@ -21,9 +36,9 @@ const defaultProps = { quantity: '1', selected_options: 'selected options' }, - onWishlistUpdate: jest.fn(), - onWishlistUpdateError: jest.fn(), - updateWishlistToastProps: jest.fn() + onWishlistUpdate, + onWishlistUpdateError, + updateWishlistToastProps }; const Component = props => { @@ -46,14 +61,106 @@ const getTalonProps = props => { return { talonProps, tree, update }; }; +const addProductToWishlist = jest.fn().mockResolvedValue({ data: 'data' }); +const formatMessage = jest + .fn() + .mockImplementation(({ defaultMessage }) => defaultMessage); + +beforeAll(() => { + useMutation.mockReturnValue([ + addProductToWishlist, + { called: true, loading: true, error: null } + ]); + + useIntl.mockReturnValue({ + formatMessage + }); +}); + +beforeEach(() => { + onWishlistUpdate.mockClear(); + onWishlistUpdateError.mockClear(); + updateWishlistToastProps.mockClear(); +}); + test('should return correct shape', () => { const { talonProps } = getTalonProps(defaultProps); expect(talonProps).toMatchSnapshot(); }); -// test('handleAddToWishlist') +describe('testing handleAddToWishlist', () => { + test('should add the product to list', async () => { + const { talonProps } = getTalonProps(defaultProps); + + await talonProps.handleAddToWishlist(); + + expect(addProductToWishlist.mock.calls[0]).toMatchSnapshot(); + }); + + test('should set isItemAdded to true if mutation is successful', async () => { + const { talonProps, update } = getTalonProps(defaultProps); + + await talonProps.handleAddToWishlist(); + + const { isItemAdded } = update(); + + expect(isItemAdded).toBeTruthy(); + }); + + test('should call updateWishlistToastProps', async () => { + const { talonProps } = getTalonProps(defaultProps); + + await talonProps.handleAddToWishlist(); + + expect(updateWishlistToastProps.mock.calls[0]).toMatchSnapshot(); + }); + + test('should call onWishlistUpdate', async () => { + const { talonProps } = getTalonProps(defaultProps); + + await talonProps.handleAddToWishlist(); + + expect(onWishlistUpdate).toHaveBeenCalled(); + }); + + test('should call onWishlistUpdateError if there was an error calling the mutation', async () => { + const error = 'Something went wrong'; + addProductToWishlist.mockRejectedValueOnce(error); + const { talonProps } = getTalonProps(defaultProps); + + await talonProps.handleAddToWishlist(); + + expect(onWishlistUpdateError.mock.calls[0]).toMatchSnapshot(); + }); + + test('should use necessary translations', async () => { + const { talonProps } = getTalonProps(defaultProps); + + await talonProps.handleAddToWishlist(); + + expect(formatMessage.mock.calls).toMatchSnapshot(); + }); +}); + +test('should reset isItemAdded if selected_options change', async () => { + const { talonProps, update } = getTalonProps(defaultProps); + + await talonProps.handleAddToWishlist(); + + const { isItemAdded } = update(); -// test('should use necessary translations', () => { + expect(isItemAdded).toBeTruthy(); -// }) + update({ + ...defaultProps, + item: { + ...defaultProps.item, + selected_options: 'new selected options' + } + }); + + const { isItemAdded: newIsItemAdded } = update(); + + expect(newIsItemAdded).toBeFalsy(); +}); From b6a24ecd4729309a924355d40776f2ab64efd466 Mon Sep 17 00:00:00 2001 From: Revanth Kumar Date: Wed, 28 Apr 2021 17:25:12 -0500 Subject: [PATCH 9/9] Added useWishlist.ee tests. --- .../__snapshots__/useWishlist.ee.spec.js.snap | 60 +++++ .../Wishlist/__tests__/useWishlist.ee.spec.js | 230 ++++++++++++++++++ .../Wishlist/Wishlist/useWishlist.ee.js | 4 +- 3 files changed, 292 insertions(+), 2 deletions(-) create mode 100644 packages/peregrine/lib/talons/Wishlist/Wishlist/__tests__/__snapshots__/useWishlist.ee.spec.js.snap create mode 100644 packages/peregrine/lib/talons/Wishlist/Wishlist/__tests__/useWishlist.ee.spec.js diff --git a/packages/peregrine/lib/talons/Wishlist/Wishlist/__tests__/__snapshots__/useWishlist.ee.spec.js.snap b/packages/peregrine/lib/talons/Wishlist/Wishlist/__tests__/__snapshots__/useWishlist.ee.spec.js.snap new file mode 100644 index 0000000000..1e1e1a4d0c --- /dev/null +++ b/packages/peregrine/lib/talons/Wishlist/Wishlist/__tests__/__snapshots__/useWishlist.ee.spec.js.snap @@ -0,0 +1,60 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should return correct shape 1`] = ` +Object { + "called": true, + "error": null, + "handleAddToWishlist": [Function], + "handleModalClose": [Function], + "handleModalOpen": [Function], + "isDisabled": true, + "isItemAdded": false, + "isModalOpen": false, + "loading": true, +} +`; + +exports[`testing handleAddToWishlist should add the product to list 1`] = ` +Array [ + Object { + "variables": Object { + "itemOptions": Object { + "quantity": "1", + "selected_options": "selected options", + "sku": "sku", + }, + "wishlistId": "0", + }, + }, +] +`; + +exports[`testing handleAddToWishlist should call onWishlistUpdateError if there was an error calling the mutation 1`] = ` +Array [ + "Something went wrong", +] +`; + +exports[`testing handleAddToWishlist should call updateWishlistToastProps 1`] = ` +Array [ + Object { + "message": "Item successfully added to Bday List.", + "timeout": 5000, + "type": "info", + }, +] +`; + +exports[`testing handleAddToWishlist should use necessary translations 1`] = ` +Array [ + Array [ + Object { + "defaultMessage": "Item successfully added to Bday List.", + "id": "cartPage.wishlist.ee.successMessage", + }, + Object { + "wishlistName": "Bday List", + }, + ], +] +`; diff --git a/packages/peregrine/lib/talons/Wishlist/Wishlist/__tests__/useWishlist.ee.spec.js b/packages/peregrine/lib/talons/Wishlist/Wishlist/__tests__/useWishlist.ee.spec.js new file mode 100644 index 0000000000..f58b8a1a73 --- /dev/null +++ b/packages/peregrine/lib/talons/Wishlist/Wishlist/__tests__/useWishlist.ee.spec.js @@ -0,0 +1,230 @@ +import React from 'react'; +import { useMutation, useQuery } from '@apollo/client'; + +import { useWishlist } from '../useWishlist.ee'; +import createTestInstance from '../../../../util/createTestInstance'; +import { useIntl } from 'react-intl'; + +jest.mock('@apollo/client', () => ({ + useMutation: jest.fn().mockReturnValue([ + jest.fn().mockResolvedValue({ data: 'data' }), + { + loading: false, + called: false, + error: null + } + ]), + useQuery: jest.fn().mockReturnValue({}) +})); + +jest.mock('react-intl', () => ({ + useIntl: jest.fn().mockReturnValue({ formatMessage: jest.fn() }) +})); + +jest.mock('../../../../util/shallowMerge', () => () => ({ + addProductToWishlistMutation: 'addProductToWishlistMutation' +})); + +jest.mock('../product.gql', () => () => ({})); + +const onWishlistUpdate = jest.fn(); +const onWishlistUpdateError = jest.fn(); +const updateWishlistToastProps = jest.fn(); + +const defaultProps = { + item: { + sku: 'sku', + quantity: '1', + selected_options: 'selected options' + }, + onWishlistUpdate, + onWishlistUpdateError, + updateWishlistToastProps +}; + +const Component = props => { + const talonProps = useWishlist(props); + + return ; +}; + +const getTalonProps = props => { + const tree = createTestInstance(); + const { root } = tree; + const { talonProps } = root.findByType('i').props; + + const update = newProps => { + tree.update(); + + return root.findByType('i').props.talonProps; + }; + + return { talonProps, tree, update }; +}; + +const addProductToWishlist = jest.fn().mockResolvedValue({ + data: { + addProductsToWishlist: { + wishlist: { + name: 'Bday List' + } + } + } +}); + +const formatMessage = jest + .fn() + .mockImplementation(({ defaultMessage }) => defaultMessage); + +const wishlistData = { + storeConfig: { + enable_multiple_wishlists: '1', + maximum_number_of_wishlists: 3 + }, + customer: { wishlists: ['Bday List', 'New Year List'] } +}; + +beforeAll(() => { + useMutation.mockReturnValue([ + addProductToWishlist, + { called: true, loading: true, error: null } + ]); + + useQuery.mockReturnValue({ + data: wishlistData + }); + + useIntl.mockReturnValue({ + formatMessage + }); +}); + +beforeEach(() => { + onWishlistUpdate.mockClear(); + onWishlistUpdateError.mockClear(); + updateWishlistToastProps.mockClear(); +}); + +test('should return correct shape', () => { + const { talonProps } = getTalonProps(defaultProps); + + expect(talonProps).toMatchSnapshot(); +}); + +describe('testing handleAddToWishlist', () => { + test('should add the product to list', async () => { + const { talonProps } = getTalonProps(defaultProps); + + await talonProps.handleAddToWishlist(); + + expect(addProductToWishlist.mock.calls[0]).toMatchSnapshot(); + }); + + test('should set isItemAdded to true if mutation is successful', async () => { + const { talonProps, update } = getTalonProps(defaultProps); + + await talonProps.handleAddToWishlist(); + + const { isItemAdded } = update(); + + expect(isItemAdded).toBeTruthy(); + }); + + test('should call updateWishlistToastProps', async () => { + const { talonProps } = getTalonProps(defaultProps); + + await talonProps.handleAddToWishlist(); + + expect(updateWishlistToastProps.mock.calls[0]).toMatchSnapshot(); + }); + + test('should call onWishlistUpdate', async () => { + const { talonProps } = getTalonProps(defaultProps); + + await talonProps.handleAddToWishlist(); + + expect(onWishlistUpdate).toHaveBeenCalled(); + }); + + test('should call onWishlistUpdateError if there was an error calling the mutation', async () => { + const error = 'Something went wrong'; + addProductToWishlist.mockRejectedValueOnce(error); + const { talonProps } = getTalonProps(defaultProps); + + await talonProps.handleAddToWishlist(); + + expect(onWishlistUpdateError.mock.calls[0]).toMatchSnapshot(); + }); + + test('should use necessary translations', async () => { + const { talonProps } = getTalonProps(defaultProps); + + await talonProps.handleAddToWishlist(); + + expect(formatMessage.mock.calls).toMatchSnapshot(); + }); + + test('should close modal if store config has multiple wishlists enabled and max num of wishlists is less than customer wishlists', async () => { + const { talonProps, update } = getTalonProps(defaultProps); + + talonProps.handleModalOpen(); + await talonProps.handleAddToWishlist(); + + const { isModalOpen } = update(); + + expect(isModalOpen).toBeFalsy(); + }); +}); + +test('should reset isItemAdded if selected_options change', async () => { + const { talonProps, update } = getTalonProps(defaultProps); + + await talonProps.handleAddToWishlist(); + + const { isItemAdded } = update(); + + expect(isItemAdded).toBeTruthy(); + + update({ + ...defaultProps, + item: { + ...defaultProps.item, + selected_options: 'new selected options' + } + }); + + const { isItemAdded: newIsItemAdded } = update(); + + expect(newIsItemAdded).toBeFalsy(); +}); + +test('handleModalOpen should set isModalOpen to true', () => { + const { talonProps, update } = getTalonProps(defaultProps); + + talonProps.handleModalOpen(); + + const { isModalOpen } = update(); + + expect(isModalOpen).toBeTruthy(); +}); + +test('handleModalClose should set isModalOpen to false', () => { + const { talonProps, update } = getTalonProps(defaultProps); + + talonProps.handleModalClose(); + + const { isModalOpen } = update(); + + expect(isModalOpen).toBeFalsy(); +}); + +test('handleModalClose should set isModalOpen to false and set isItemAdded to true', () => { + const { talonProps, update } = getTalonProps(defaultProps); + + talonProps.handleModalClose(true); + + const { isModalOpen, isItemAdded } = update(); + + expect(isModalOpen).toBeFalsy(); + expect(isItemAdded).toBeTruthy(); +}); diff --git a/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ee.js b/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ee.js index f2eb5c6cdf..677e04339f 100644 --- a/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ee.js +++ b/packages/peregrine/lib/talons/Wishlist/Wishlist/useWishlist.ee.js @@ -115,11 +115,11 @@ export const useWishlist = props => { setIsModalOpen(true); }, []); - const handleModalClose = useCallback(success => { + const handleModalClose = useCallback(isSuccess => { setIsModalOpen(false); // only set item added true if someone calls handleModalClose(true) - if (success === true) { + if (isSuccess) { setIsItemAdded(true); } }, []);