Skip to content

Commit

Permalink
Add to Wishlist from PDP (#3048)
Browse files Browse the repository at this point in the history
* initial work to add CE wishlist functionality on pdp

* Use 0 as wishlist id for CE

* Adding configurableproduct via selected_options array

* Fixing logic for temporary item added

* migrate logic to wishlist talons with compatible extension

* Added to favorites ux is favorable over a technically challenging remove from favorites ux

Signed-off-by: sirugh <rugh@adobe.com>

* bool name

* EE workflow

* Feedback

Signed-off-by: sirugh <rugh@adobe.com>

* styles

Signed-off-by: sirugh <rugh@adobe.com>

* fix gql warnings

Signed-off-by: sirugh <rugh@adobe.com>

* Add messages and some cleanup

Signed-off-by: sirugh <rugh@adobe.com>

* capitalization

Signed-off-by: sirugh <rugh@adobe.com>

* id for i18n

Signed-off-by: sirugh <rugh@adobe.com>

* Only close new list form when cancel is clicked

Signed-off-by: sirugh <rugh@adobe.com>

* Update icon styles for wishlist button

Signed-off-by: sirugh <rugh@adobe.com>

* Add underline to mimic link

Signed-off-by: sirugh <rugh@adobe.com>

* wrap button in suspense to lazy load

Signed-off-by: sirugh <rugh@adobe.com>

* Address feedback.

Signed-off-by: sirugh <rugh@adobe.com>

* id is required on customer

Signed-off-by: sirugh <rugh@adobe.com>

* Remove unnecessary fragment

Signed-off-by: sirugh <rugh@adobe.com>

* Rename to createWishlistForm

Signed-off-by: sirugh <rugh@adobe.com>

* Fix a bug for simple items where configurable_options was null

* Minimal fixes to existing tests to get passing.

Signed-off-by: sirugh <rugh@adobe.com>

* Remove unused prop

Signed-off-by: sirugh <rugh@adobe.com>

* Test changes to pfd talon

* Move and test wishlistLineItem

* Test useWishlistButton.ce.js

Signed-off-by: sirugh <rugh@adobe.com>

* Test useWishlistButton.ee.js

Signed-off-by: sirugh <rugh@adobe.com>

* Test useCreateWishlistForm

Signed-off-by: sirugh <rugh@adobe.com>

* test useWishlistDialog

Signed-off-by: sirugh <rugh@adobe.com>

* Test productFullDetail

Signed-off-by: sirugh <rugh@adobe.com>

* Test wishlistButton.ce

Signed-off-by: sirugh <rugh@adobe.com>

* Test wishlistButton.ee

Signed-off-by: sirugh <rugh@adobe.com>

* Test CreateWishlistForm

Signed-off-by: sirugh <rugh@adobe.com>

* Test WishlistDialog

Signed-off-by: sirugh <rugh@adobe.com>

* Add enclosing root div to button components and pack content around center.

Signed-off-by: sirugh <rugh@adobe.com>

* fix snaps

Signed-off-by: sirugh <rugh@adobe.com>

* Use a single child of button to contain items

Signed-off-by: sirugh <rugh@adobe.com>

Co-authored-by: Devagouda <40405790+dpatil-magento@users.noreply.github.com>
  • Loading branch information
sirugh and dpatil-magento authored Mar 29, 2021
1 parent 9927070 commit 94696d3
Show file tree
Hide file tree
Showing 42 changed files with 3,086 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -1,18 +1,37 @@
import React from 'react';
import { useMutation } from '@apollo/client';
import { act } from 'react-test-renderer';
import { useMutation, useQuery } from '@apollo/client';

import createTestInstance from '../../../util/createTestInstance';
import { useProductFullDetail } from '../useProductFullDetail';
import { useUserContext } from '../../../context/user';

jest.mock('@apollo/client', () => ({
useMutation: jest.fn().mockImplementation(() => [
jest.fn(),
{
error: null
}
])
]),
useQuery: jest.fn().mockImplementation(() => ({
data: {
storeConfig: {
magento_wishlist_general_is_enabled: true
}
},
loading: false,
error: false
}))
}));

jest.mock('@magento/peregrine/lib/context/user', () => {
const userState = { isSignedIn: false };
const userApi = {};
const useUserContext = jest.fn(() => [userState, userApi]);

return { useUserContext };
});

jest.mock('@magento/peregrine/lib/context/cart', () => {
const cartState = { cartId: 'ThisIsMyCart' };
const cartApi = {};
Expand All @@ -38,10 +57,123 @@ const defaultProps = {
value: 99
}
}
}
},
sku: 'MySimpleProductSku'
}
};

describe('shouldShowWishlistButton', () => {
test('is false if not signed in', () => {
useUserContext.mockReturnValueOnce([{ isSignedIn: false }]);
const tree = createTestInstance(<Component {...defaultProps} />);

const { root } = tree;
const { talonProps } = root.findByType('i').props;

expect(talonProps.shouldShowWishlistButton).toBeFalsy();
});

test('is false if wishlist is disabled in config', () => {
useUserContext.mockReturnValueOnce([{ isSignedIn: true }]);
useQuery.mockReturnValueOnce({
data: {
storeConfig: {
magento_wishlist_general_is_enabled: false
}
},
loading: false,
error: false
});
const tree = createTestInstance(<Component {...defaultProps} />);

const { root } = tree;
const { talonProps } = root.findByType('i').props;

expect(talonProps.shouldShowWishlistButton).toBeFalsy();
});

test('is true if signed in and wishlist is enabled', () => {
useUserContext.mockReturnValueOnce([{ isSignedIn: true }]);
useQuery.mockReturnValueOnce({
data: {
storeConfig: {
magento_wishlist_general_is_enabled: true
}
},
loading: false,
error: false
});
const tree = createTestInstance(<Component {...defaultProps} />);

const { root } = tree;
const { talonProps } = root.findByType('i').props;

expect(talonProps.shouldShowWishlistButton).toBeTruthy();
});
});

describe('wishlistItemOptions', () => {
test('returns quantity and sku for all products', () => {
const tree = createTestInstance(<Component {...defaultProps} />);

const { root } = tree;
const { talonProps } = root.findByType('i').props;

expect(talonProps.wishlistItemOptions).toMatchObject({
quantity: 1,
sku: defaultProps.product.sku
});
});

test('returns selected_options for ConfigurableProducts', () => {
const optionId = 1;
const selectionId = 2;
const uid = 'foo';

const props = {
...defaultProps,
product: {
...defaultProps.product,
sku: 'MyConfigurableProductSku',
__typename: 'ConfigurableProduct',
configurable_options: [
{
attribute_id: optionId,
values: [{ uid, value_index: selectionId }]
}
],
variants: []
}
};
const tree = createTestInstance(<Component {...props} />);

const { root } = tree;

expect(
root.findByType('i').props.talonProps.wishlistItemOptions
).toMatchObject({
quantity: 1,
sku: props.product.sku,
selected_options: []
});

act(() => {
root.findByType('i').props.talonProps.handleSelectionChange(
optionId,
selectionId
);
});

expect(
root.findByType('i').props.talonProps.wishlistItemOptions
).toMatchObject({
quantity: 1,
sku: props.product.sku,
selected_options: [uid]
});
});
});

test('returns undefined category if there are no categories for the product', () => {
const props = {
...defaultProps,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useCallback, useState, useMemo } from 'react';
import { useMutation } from '@apollo/client';
import { useMutation, useQuery } from '@apollo/client';
import { useCartContext } from '@magento/peregrine/lib/context/cart';
import { useUserContext } from '@magento/peregrine/lib/context/user';

import { appendOptionsToPayload } from '@magento/peregrine/lib/util/appendOptionsToPayload';
import { findMatchingVariant } from '@magento/peregrine/lib/util/findMatchingProductVariant';
Expand Down Expand Up @@ -150,6 +151,7 @@ const SUPPORTED_PRODUCT_TYPES = ['SimpleProduct', 'ConfigurableProduct'];
/**
* @param {GraphQLQuery} props.addConfigurableProductToCartMutation - configurable product mutation
* @param {GraphQLQuery} props.addSimpleProductToCartMutation - configurable product mutation
* @param {GraphQLQuery} props.getWishlistConfig - queries for whether wishlists are enabled.
* @param {Object} props.product - the product, see RootComponents/Product
*
* @returns {{
Expand All @@ -169,6 +171,7 @@ export const useProductFullDetail = props => {
const {
addConfigurableProductToCartMutation,
addSimpleProductToCartMutation,
getWishlistConfig,
product
} = props;

Expand All @@ -179,6 +182,11 @@ export const useProductFullDetail = props => {
);

const [{ cartId }] = useCartContext();
const [{ isSignedIn }] = useUserContext();

const { data: storeConfigData } = useQuery(getWishlistConfig, {
fetchPolicy: 'cache-and-network'
});

const [
addConfigurableProductToCart,
Expand Down Expand Up @@ -222,6 +230,39 @@ export const useProductFullDetail = props => {
[product, optionCodes, optionSelections]
);

// The map of ids to values (and their uids)
// For example:
// { "179" => [{ uid: "abc", value_index: 1 }, { uid: "def", value_index: 2 }]}
const attributeIdToValuesMap = useMemo(() => {
const map = new Map();
// For simple items, this will be an empty map.
const options = product.configurable_options || [];
for (const { attribute_id, values } of options) {
map.set(attribute_id, values);
}
return map;
}, [product.configurable_options]);

// An array of selected option uids. Useful for passing to mutations.
// For example:
// ["abc", "def"]
const selectedOptionsArray = useMemo(() => {
const selectedOptions = [];

optionSelections.forEach((value, key) => {
const values = attributeIdToValuesMap.get(key);

const selectedValue = values.find(
item => item.value_index === value
);

if (selectedValue) {
selectedOptions.push(selectedValue.uid);
}
});
return selectedOptions;
}, [attributeIdToValuesMap, optionSelections]);

const handleAddToCart = useCallback(
async formValues => {
const { quantity } = formValues;
Expand Down Expand Up @@ -310,6 +351,19 @@ export const useProductFullDetail = props => {
[errorAddingConfigurableProduct, errorAddingSimpleProduct]
);

const wishlistItemOptions = useMemo(() => {
const options = {
quantity: 1,
sku: product.sku
};

if (productType === 'ConfigurableProduct') {
options.selected_options = selectedOptionsArray;
}

return options;
}, [product, productType, selectedOptionsArray]);

return {
breadcrumbCategoryId,
errorMessage: derivedErrorMessage,
Expand All @@ -319,6 +373,11 @@ export const useProductFullDetail = props => {
isMissingOptions || isAddConfigurableLoading || isAddSimpleLoading,
isSupportedProductType,
mediaGalleryEntries,
productDetails
shouldShowWishlistButton:
isSignedIn &&
storeConfigData &&
!!storeConfigData.storeConfig.magento_wishlist_general_is_enabled,
productDetails,
wishlistItemOptions
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const ProductDetailsFragment = gql`
id
label
values {
uid
default_label
label
store_label
Expand Down
Loading

0 comments on commit 94696d3

Please sign in to comment.