@magento/peregrine 11.0.0-beta.1 → 12.0.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/lib/Apollo/magentoGqlCacheLink.js +59 -0
  2. package/lib/List/item.js +12 -9
  3. package/lib/List/list.js +10 -7
  4. package/lib/Price/price.js +0 -1
  5. package/lib/context/cart.js +9 -10
  6. package/lib/hooks/hook-wrappers/useInformedFieldStateWrapper.js +27 -0
  7. package/lib/hooks/useDelayedTransition.js +127 -0
  8. package/lib/hooks/useDetectScrollWidth.js +77 -0
  9. package/lib/hooks/useInternalLink.js +21 -0
  10. package/lib/hooks/useIntersectionObserver.js +7 -0
  11. package/lib/hooks/useIsInViewport.js +44 -0
  12. package/lib/store/actions/app/actions.js +2 -1
  13. package/lib/store/reducers/app.js +8 -1
  14. package/lib/talons/AccountInformationPage/useAccountInformationPage.js +0 -1
  15. package/lib/talons/Adapter/useAdapter.js +23 -3
  16. package/lib/talons/AddressBookPage/useAddressBookPage.js +0 -10
  17. package/lib/talons/Breadcrumbs/breadcrumbs.gql.js +11 -2
  18. package/lib/talons/Breadcrumbs/useBreadcrumbs.js +19 -5
  19. package/lib/talons/CartPage/GiftCards/giftCardFragments.gql.ee.js +14 -0
  20. package/lib/talons/CartPage/GiftCards/giftCardFragments.gql.js +8 -0
  21. package/lib/talons/CartPage/GiftCards/giftCardQueries.gql.ee.js +73 -0
  22. package/lib/talons/CartPage/GiftCards/useGiftCards.js +25 -16
  23. package/lib/talons/CartPage/PriceAdjustments/CouponCode/couponCode.gql.js +56 -0
  24. package/lib/talons/CartPage/PriceAdjustments/CouponCode/couponCodeFragments.gql.js +10 -0
  25. package/lib/talons/CartPage/PriceAdjustments/{useCouponCode.js → CouponCode/useCouponCode.js} +10 -6
  26. package/lib/talons/CartPage/{GiftOptions → PriceAdjustments/GiftOptions}/client-schema.graphql +0 -0
  27. package/lib/talons/CartPage/PriceAdjustments/GiftOptions/giftOptions.gql.js +21 -0
  28. package/lib/talons/CartPage/{GiftOptions → PriceAdjustments/GiftOptions}/useGiftOptions.js +5 -3
  29. package/lib/talons/CartPage/PriceAdjustments/ShippingMethods/shippingMethods.gql.js +43 -0
  30. package/lib/talons/CartPage/PriceAdjustments/ShippingMethods/shippingMethodsFragments.gql.js +60 -0
  31. package/lib/talons/CartPage/PriceAdjustments/ShippingMethods/useShippingForm.js +8 -9
  32. package/lib/talons/CartPage/PriceAdjustments/ShippingMethods/useShippingMethods.js +7 -5
  33. package/lib/talons/CartPage/PriceAdjustments/ShippingMethods/useShippingRadios.js +1 -1
  34. package/lib/talons/CartPage/PriceSummary/priceSummary.gql.js +16 -0
  35. package/lib/talons/CartPage/PriceSummary/usePriceSummary.js +6 -5
  36. package/lib/talons/CartPage/ProductListing/EditModal/productForm.gql.js +78 -0
  37. package/lib/talons/CartPage/ProductListing/EditModal/productFormFragment.gql.js +47 -0
  38. package/lib/talons/CartPage/ProductListing/EditModal/useProductForm.js +18 -8
  39. package/lib/talons/CartPage/ProductListing/product.gql.js +4 -3
  40. package/lib/talons/CartPage/ProductListing/productListing.gql.ce.js +13 -1
  41. package/lib/talons/CartPage/ProductListing/productListing.gql.ee.js +13 -1
  42. package/lib/talons/CartPage/ProductListing/productListingFragments.gql.js +52 -0
  43. package/lib/talons/CartPage/ProductListing/useProduct.js +22 -18
  44. package/lib/talons/CartPage/ProductListing/useProductListing.js +8 -13
  45. package/lib/talons/CartPage/ProductListing/useQuantity.js +2 -1
  46. package/lib/talons/CartPage/cartPage.gql.js +16 -0
  47. package/lib/talons/CartPage/cartPageFragments.gql.js +21 -0
  48. package/lib/talons/CartPage/useCartPage.js +7 -6
  49. package/lib/talons/CategoryList/categoryList.gql.js +11 -2
  50. package/lib/talons/CategoryList/useCategoryList.js +8 -1
  51. package/lib/talons/CategoryList/useCategoryTile.js +10 -4
  52. package/lib/talons/CategoryTree/categoryTree.gql.js +11 -2
  53. package/lib/talons/CategoryTree/useCategoryTree.js +12 -3
  54. package/lib/talons/CheckoutPage/AddressBook/addressBook.gql.js +34 -0
  55. package/lib/talons/CheckoutPage/AddressBook/addressBookFragments.gql.js +31 -0
  56. package/lib/talons/CheckoutPage/AddressBook/useAddressBook.js +16 -8
  57. package/lib/talons/CheckoutPage/PaymentInformation/braintreeSummary.gql.js +1 -1
  58. package/lib/talons/CheckoutPage/PaymentInformation/editModal.gql.js +16 -0
  59. package/lib/talons/CheckoutPage/PaymentInformation/paymentInformation.gql.js +4 -8
  60. package/lib/talons/CheckoutPage/PaymentInformation/paymentMethods.gql.js +17 -0
  61. package/lib/talons/CheckoutPage/PaymentInformation/useBraintreeSummary.js +1 -1
  62. package/lib/talons/CheckoutPage/PaymentInformation/useEditModal.js +6 -5
  63. package/lib/talons/CheckoutPage/PaymentInformation/usePaymentInformation.js +22 -16
  64. package/lib/talons/CheckoutPage/PaymentInformation/usePaymentMethods.js +6 -3
  65. package/lib/talons/CheckoutPage/ShippingInformation/AddressForm/customerForm.gql.js +55 -0
  66. package/lib/talons/CheckoutPage/ShippingInformation/AddressForm/guestForm.gql.js +44 -0
  67. package/lib/talons/CheckoutPage/ShippingInformation/AddressForm/useCustomerForm.js +11 -14
  68. package/lib/talons/CheckoutPage/ShippingInformation/AddressForm/useGuestForm.js +6 -7
  69. package/lib/talons/CheckoutPage/ShippingInformation/shippingInformation.gql.js +54 -0
  70. package/lib/talons/CheckoutPage/ShippingInformation/shippingInformationFragments.gql.js +25 -0
  71. package/lib/talons/CheckoutPage/ShippingInformation/useShippingInformation.js +11 -6
  72. package/lib/talons/CheckoutPage/ShippingMethod/shippingMethod.gql.js +64 -0
  73. package/lib/talons/CheckoutPage/ShippingMethod/shippingMethodFragments.gql.js +64 -0
  74. package/lib/talons/CheckoutPage/{useShippingMethod.js → ShippingMethod/useShippingMethod.js} +11 -8
  75. package/lib/talons/Cms/cmsPage.gql.js +2 -2
  76. package/lib/talons/Cms/useCmsPage.js +2 -2
  77. package/lib/talons/CommunicationsPage/useCommunicationsPage.js +1 -2
  78. package/lib/talons/CreateAccount/createAccount.gql.js +3 -0
  79. package/lib/talons/CreateAccount/useCreateAccount.js +2 -4
  80. package/lib/talons/CreateAccountPage/useCreateAccountPage.js +38 -9
  81. package/lib/talons/FilterModal/useFilterList.js +17 -3
  82. package/lib/talons/FilterModal/useFilterModal.js +4 -5
  83. package/lib/talons/FilterSidebar/useFilterSidebar.js +4 -5
  84. package/lib/talons/ForgotPasswordPage/useForgotPasswordPage.js +46 -0
  85. package/lib/talons/FormError/useFormError.js +10 -3
  86. package/lib/talons/Gallery/__fixtures__/apolloMocks.js +8 -6
  87. package/lib/talons/Gallery/addToCart.gql.js +17 -0
  88. package/lib/talons/Gallery/gallery.gql.ce.js +4 -3
  89. package/lib/talons/Gallery/gallery.gql.ee.js +4 -3
  90. package/lib/talons/Gallery/useAddToCartButton.js +81 -0
  91. package/lib/talons/Gallery/useGallery.js +4 -7
  92. package/lib/talons/Gallery/useGalleryItem.js +7 -1
  93. package/lib/talons/Header/useCartTrigger.js +14 -7
  94. package/lib/talons/Link/useLink.js +68 -0
  95. package/lib/talons/MagentoRoute/magentoRoute.gql.js +12 -3
  96. package/lib/talons/MagentoRoute/useMagentoRoute.js +115 -30
  97. package/lib/talons/MegaMenu/megaMenu.gql.js +10 -4
  98. package/lib/talons/MegaMenu/useMegaMenu.js +61 -11
  99. package/lib/talons/MegaMenu/useMegaMenuItem.js +61 -0
  100. package/lib/talons/MegaMenu/useSubMenu.js +20 -0
  101. package/lib/talons/MiniCart/ProductList/productListFragments.gql.js +0 -1
  102. package/lib/talons/MiniCart/miniCart.gql.js +4 -3
  103. package/lib/talons/MiniCart/useMiniCart.js +15 -12
  104. package/lib/talons/Navigation/useNavigation.js +6 -1
  105. package/lib/talons/OrderHistoryPage/orderRow.gql.js +0 -1
  106. package/lib/talons/OrderHistoryPage/useOrderHistoryPage.js +2 -14
  107. package/lib/talons/PageLoadingIndicator/usePageLoadingIndicator.js +52 -0
  108. package/lib/talons/Postcode/usePostcode.js +2 -1
  109. package/lib/talons/ProductFullDetail/useProductFullDetail.js +30 -5
  110. package/lib/talons/Region/useRegion.js +12 -10
  111. package/lib/talons/RootComponents/Category/categoryFragments.gql.js +5 -4
  112. package/lib/talons/RootComponents/Product/productDetailFragment.gql.js +7 -0
  113. package/lib/talons/SavedPaymentsPage/useSavedPaymentsPage.js +1 -11
  114. package/lib/talons/SearchBar/useAutocomplete.js +1 -1
  115. package/lib/talons/SearchBar/useSearchField.js +2 -1
  116. package/lib/talons/SearchBar/useSuggestedCategory.js +4 -6
  117. package/lib/talons/SearchPage/searchPage.gql.js +10 -9
  118. package/lib/talons/SignIn/signIn.gql.js +3 -0
  119. package/lib/talons/SignInPage/useSignInPage.js +63 -0
  120. package/lib/talons/Wishlist/AddToListButton/helpers/useSingleWishlist.js +2 -2
  121. package/lib/talons/Wishlist/AddToListButton/useAddToListButton.ee.js +2 -1
  122. package/lib/talons/WishlistPage/useActionMenu.js +39 -5
  123. package/lib/talons/WishlistPage/useWishlist.js +4 -4
  124. package/lib/talons/WishlistPage/useWishlistPage.js +3 -11
  125. package/lib/talons/WishlistPage/wishlist.gql.js +1 -1
  126. package/lib/talons/WishlistPage/wishlistConfig.gql.ce.js +14 -0
  127. package/lib/talons/WishlistPage/wishlistConfig.gql.ee.js +15 -0
  128. package/lib/targets/peregrine-declare.js +0 -4
  129. package/lib/util/deriveErrorMessage.js +12 -6
  130. package/lib/util/images.js +3 -2
  131. package/lib/util/isSupportedProductType.js +5 -0
  132. package/lib/util/magentoRouteData.js +9 -0
  133. package/package.json +8 -8
@@ -33,7 +33,7 @@ export const useMiniCart = props => {
33
33
  const {
34
34
  removeItemMutation,
35
35
  miniCartQuery,
36
- getConfigurableThumbnailSource
36
+ getStoreConfigQuery
37
37
  } = operations;
38
38
 
39
39
  const [{ cartId }] = useCartContext();
@@ -49,19 +49,21 @@ export const useMiniCart = props => {
49
49
  }
50
50
  );
51
51
 
52
- const { data: configurableThumbnailSourceData } = useQuery(
53
- getConfigurableThumbnailSource,
54
- {
55
- fetchPolicy: 'cache-and-network'
56
- }
57
- );
52
+ const { data: storeConfigData } = useQuery(getStoreConfigQuery, {
53
+ fetchPolicy: 'cache-and-network'
54
+ });
58
55
 
59
56
  const configurableThumbnailSource = useMemo(() => {
60
- if (configurableThumbnailSourceData) {
61
- return configurableThumbnailSourceData.storeConfig
62
- .configurable_thumbnail_source;
57
+ if (storeConfigData) {
58
+ return storeConfigData.storeConfig.configurable_thumbnail_source;
59
+ }
60
+ }, [storeConfigData]);
61
+
62
+ const storeUrlSuffix = useMemo(() => {
63
+ if (storeConfigData) {
64
+ return storeConfigData.storeConfig.product_url_suffix;
63
65
  }
64
- }, [configurableThumbnailSourceData]);
66
+ }, [storeConfigData]);
65
67
 
66
68
  const [
67
69
  removeItem,
@@ -135,6 +137,7 @@ export const useMiniCart = props => {
135
137
  productList,
136
138
  subTotal,
137
139
  totalQuantity,
138
- configurableThumbnailSource
140
+ configurableThumbnailSource,
141
+ storeUrlSuffix
139
142
  };
140
143
  };
@@ -6,6 +6,7 @@ import { useAppContext } from '../../context/app';
6
6
  import { useCatalogContext } from '../../context/catalog';
7
7
  import { useUserContext } from '../../context/user';
8
8
  import { useAwaitQuery } from '../../hooks/useAwaitQuery';
9
+ import useInternalLink from '../../hooks/useInternalLink';
9
10
 
10
11
  import DEFAULT_OPERATIONS from './navigation.gql';
11
12
 
@@ -76,9 +77,13 @@ export const useNavigation = (props = {}) => {
76
77
  }
77
78
  }, [category, closeDrawer, isTopLevel, view]);
78
79
 
80
+ const { setShimmerType } = useInternalLink('category');
81
+
79
82
  const handleClose = useCallback(() => {
80
83
  closeDrawer();
81
- }, [closeDrawer]);
84
+ // Sets next root component to show proper loading effect
85
+ setShimmerType();
86
+ }, [closeDrawer, setShimmerType]);
82
87
 
83
88
  // create callbacks for local state
84
89
  const showCreateAccount = useCallback(() => {
@@ -20,7 +20,6 @@ export const GET_PRODUCT_THUMBNAILS_BY_URL_KEY = gql`
20
20
  url
21
21
  }
22
22
  url_key
23
- url_suffix
24
23
  ... on ConfigurableProduct {
25
24
  variants {
26
25
  product {
@@ -1,13 +1,10 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from 'react';
2
- import { useHistory } from 'react-router-dom';
3
2
  import { useQuery } from '@apollo/client';
4
3
 
4
+ import { useAppContext } from '@magento/peregrine/lib/context/app';
5
+ import { deriveErrorMessage } from '@magento/peregrine/lib/util/deriveErrorMessage';
5
6
  import mergeOperations from '@magento/peregrine/lib/util/shallowMerge';
6
7
 
7
- import { useAppContext } from '../../context/app';
8
- import { useUserContext } from '../../context/user';
9
- import { deriveErrorMessage } from '../../util/deriveErrorMessage';
10
-
11
8
  import DEFAULT_OPERATIONS from './orderHistoryPage.gql';
12
9
 
13
10
  const PAGE_SIZE = 10;
@@ -22,8 +19,6 @@ export const useOrderHistoryPage = (props = {}) => {
22
19
  actions: { setPageLoading }
23
20
  }
24
21
  ] = useAppContext();
25
- const history = useHistory();
26
- const [{ isSignedIn }] = useUserContext();
27
22
 
28
23
  const [pageSize, setPageSize] = useState(PAGE_SIZE);
29
24
  const [searchText, setSearchText] = useState('');
@@ -88,13 +83,6 @@ export const useOrderHistoryPage = (props = {}) => {
88
83
  return null;
89
84
  }, [orderData]);
90
85
 
91
- // If the user is no longer signed in, redirect to the home page.
92
- useEffect(() => {
93
- if (!isSignedIn) {
94
- history.push('/');
95
- }
96
- }, [history, isSignedIn]);
97
-
98
86
  // Update the page indicator if the GraphQL query is in flight.
99
87
  useEffect(() => {
100
88
  setPageLoading(isBackgroundLoading);
@@ -0,0 +1,52 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+ import { useAppContext } from '../../context/app';
3
+
4
+ const GROUP_LOADING_DELAY = 750;
5
+
6
+ export default () => {
7
+ const [{ isPageLoading }] = useAppContext();
8
+ const doneTimeoutRef = useRef();
9
+ const [loadingState, setLoadingState] = useState('off');
10
+
11
+ useEffect(() => {
12
+ if (isPageLoading) {
13
+ setLoadingState('loading');
14
+ } else if (
15
+ typeof clearTimeout !== 'undefined' &&
16
+ typeof setTimeout !== 'undefined'
17
+ ) {
18
+ setLoadingState('done');
19
+
20
+ if (doneTimeoutRef && doneTimeoutRef.current !== null) {
21
+ clearTimeout(doneTimeoutRef.current);
22
+ }
23
+
24
+ doneTimeoutRef.current = setTimeout(() => {
25
+ setLoadingState(currentLoadingState => {
26
+ if (currentLoadingState === 'loading') {
27
+ return 'loading';
28
+ }
29
+
30
+ return 'off';
31
+ });
32
+ }, GROUP_LOADING_DELAY);
33
+ } else {
34
+ setLoadingState('off');
35
+ }
36
+
37
+ return () => {
38
+ if (
39
+ typeof clearTimeout !== 'undefined' &&
40
+ doneTimeoutRef &&
41
+ doneTimeoutRef.current !== null
42
+ ) {
43
+ clearTimeout(doneTimeoutRef.current);
44
+ }
45
+ };
46
+ }, [isPageLoading, doneTimeoutRef]);
47
+
48
+ return {
49
+ isPageLoading,
50
+ loadingState
51
+ };
52
+ };
@@ -1,5 +1,6 @@
1
1
  import { useEffect, useRef } from 'react';
2
- import { useFieldApi, useFieldState } from 'informed';
2
+ import { useFieldApi } from 'informed';
3
+ import useFieldState from '@magento/peregrine/lib/hooks/hook-wrappers/useInformedFieldStateWrapper';
3
4
 
4
5
  /**
5
6
  * The usePostcode talon handles logic for resetting the postcode field value when the country changes.
@@ -7,12 +7,14 @@ import { useUserContext } from '@magento/peregrine/lib/context/user';
7
7
  import { appendOptionsToPayload } from '@magento/peregrine/lib/util/appendOptionsToPayload';
8
8
  import { findMatchingVariant } from '@magento/peregrine/lib/util/findMatchingProductVariant';
9
9
  import { isProductConfigurable } from '@magento/peregrine/lib/util/isProductConfigurable';
10
+ import { isSupportedProductType as isSupported } from '@magento/peregrine/lib/util/isSupportedProductType';
10
11
  import { deriveErrorMessage } from '../../util/deriveErrorMessage';
11
12
  import mergeOperations from '../../util/shallowMerge';
12
13
  import defaultOperations from './productFullDetail.gql';
13
14
 
14
15
  const INITIAL_OPTION_CODES = new Map();
15
16
  const INITIAL_OPTION_SELECTIONS = new Map();
17
+ const OUT_OF_STOCK_CODE = 'OUT_OF_STOCK';
16
18
 
17
19
  const deriveOptionCodesFromProduct = product => {
18
20
  // If this is a simple product it has no option codes.
@@ -63,6 +65,25 @@ const getIsMissingOptions = (product, optionSelections) => {
63
65
  return numProductSelections < numProductOptions;
64
66
  };
65
67
 
68
+ const getIsOutOfStock = (product, optionCodes, optionSelections) => {
69
+ const { stock_status, variants } = product;
70
+ const isConfigurable = isProductConfigurable(product);
71
+ const optionsSelected =
72
+ Array.from(optionSelections.values()).filter(value => !!value).length >
73
+ 0;
74
+
75
+ if (isConfigurable && optionsSelected) {
76
+ const item = findMatchingVariant({
77
+ optionCodes,
78
+ optionSelections,
79
+ variants
80
+ });
81
+
82
+ return item.product.stock_status === OUT_OF_STOCK_CODE;
83
+ }
84
+ return stock_status === OUT_OF_STOCK_CODE;
85
+ };
86
+
66
87
  const getMediaGalleryEntries = (product, optionCodes, optionSelections) => {
67
88
  let value = [];
68
89
 
@@ -149,8 +170,6 @@ const getConfigPrice = (product, optionCodes, optionSelections) => {
149
170
  return value;
150
171
  };
151
172
 
152
- const SUPPORTED_PRODUCT_TYPES = ['SimpleProduct', 'ConfigurableProduct'];
153
-
154
173
  /**
155
174
  * @param {GraphQLDocument} props.addConfigurableProductToCartMutation - configurable product mutation
156
175
  * @param {GraphQLDocument} props.addSimpleProductToCartMutation - configurable product mutation
@@ -185,9 +204,7 @@ export const useProductFullDetail = props => {
185
204
 
186
205
  const productType = product.__typename;
187
206
 
188
- const isSupportedProductType = SUPPORTED_PRODUCT_TYPES.includes(
189
- productType
190
- );
207
+ const isSupportedProductType = isSupported(productType);
191
208
 
192
209
  const [{ cartId }] = useCartContext();
193
210
  const [{ isSignedIn }] = useUserContext();
@@ -248,6 +265,12 @@ export const useProductFullDetail = props => {
248
265
  () => getIsMissingOptions(product, optionSelections),
249
266
  [product, optionSelections]
250
267
  );
268
+
269
+ const isOutOfStock = useMemo(
270
+ () => getIsOutOfStock(product, optionCodes, optionSelections),
271
+ [product, optionCodes, optionSelections]
272
+ );
273
+
251
274
  const mediaGalleryEntries = useMemo(
252
275
  () => getMediaGalleryEntries(product, optionCodes, optionSelections),
253
276
  [product, optionCodes, optionSelections]
@@ -445,7 +468,9 @@ export const useProductFullDetail = props => {
445
468
  errorMessage: derivedErrorMessage,
446
469
  handleAddToCart,
447
470
  handleSelectionChange,
471
+ isOutOfStock,
448
472
  isAddToCartDisabled:
473
+ isOutOfStock ||
449
474
  isMissingOptions ||
450
475
  isAddConfigurableLoading ||
451
476
  isAddSimpleLoading ||
@@ -1,6 +1,7 @@
1
1
  import { useEffect, useRef } from 'react';
2
2
  import { useQuery } from '@apollo/client';
3
- import { useFieldApi, useFieldState } from 'informed';
3
+ import { useFieldApi } from 'informed';
4
+ import useFieldState from '@magento/peregrine/lib/hooks/hook-wrappers/useInformedFieldStateWrapper';
4
5
 
5
6
  /**
6
7
  * The useRegion talon handles logic for:
@@ -33,24 +34,25 @@ export const useRegion = props => {
33
34
  const regionInputFieldApi = useFieldApi(fieldInput);
34
35
  const regionSelectFieldApi = useFieldApi(fieldSelect);
35
36
 
37
+ const { data, loading } = useQuery(getRegionsQuery, {
38
+ variables: { countryCode: country },
39
+ skip: !country
40
+ });
41
+
36
42
  // Reset region value when country changes. Because of how Informed sets
37
43
  // initialValues, we want to skip the first state change of the value being
38
44
  // initialized.
39
45
  useEffect(() => {
40
- if (country) {
46
+ if (country && !loading) {
41
47
  if (hasInitialized.current) {
42
- regionInputFieldApi.exists() && regionInputFieldApi.reset();
43
- regionSelectFieldApi.exists() && regionSelectFieldApi.reset();
48
+ regionInputFieldApi.exists() && regionInputFieldApi.setValue();
49
+ regionSelectFieldApi.exists() &&
50
+ regionSelectFieldApi.setValue();
44
51
  } else {
45
52
  hasInitialized.current = true;
46
53
  }
47
54
  }
48
- }, [country, regionInputFieldApi, regionSelectFieldApi]);
49
-
50
- const { data, loading } = useQuery(getRegionsQuery, {
51
- variables: { countryCode: country },
52
- skip: !country
53
- });
55
+ }, [country, regionInputFieldApi, regionSelectFieldApi, loading]);
54
56
 
55
57
  let formattedRegionsData = [{ label: 'Loading Regions...', value: '' }];
56
58
  if (data) {
@@ -14,9 +14,9 @@ export const ProductsFragment = gql`
14
14
  items {
15
15
  id
16
16
  name
17
- price {
18
- regularPrice {
19
- amount {
17
+ price_range {
18
+ maximum_price {
19
+ regular_price {
20
20
  currency
21
21
  value
22
22
  }
@@ -26,8 +26,9 @@ export const ProductsFragment = gql`
26
26
  small_image {
27
27
  url
28
28
  }
29
+ stock_status
30
+ type_id
29
31
  url_key
30
- url_suffix
31
32
  }
32
33
  page_info {
33
34
  total_pages
@@ -14,7 +14,10 @@ export const ProductDetailsFragment = gql`
14
14
  }
15
15
  id
16
16
  media_gallery_entries {
17
+ # id is deprecated and unused in our code, but lint rules require we
18
+ # request it if available
17
19
  id
20
+ uid
18
21
  label
19
22
  position
20
23
  disabled
@@ -34,6 +37,7 @@ export const ProductDetailsFragment = gql`
34
37
  small_image {
35
38
  url
36
39
  }
40
+ stock_status
37
41
  url_key
38
42
  ... on ConfigurableProduct {
39
43
  configurable_options {
@@ -64,7 +68,10 @@ export const ProductDetailsFragment = gql`
64
68
  product {
65
69
  id
66
70
  media_gallery_entries {
71
+ # id is deprecated and unused in our code, but lint rules require we
72
+ # request it if available
67
73
  id
74
+ uid
68
75
  disabled
69
76
  file
70
77
  label
@@ -1,12 +1,10 @@
1
1
  import { useEffect } from 'react';
2
- import { useHistory } from 'react-router-dom';
3
-
4
2
  import { useQuery } from '@apollo/client';
5
3
 
6
4
  import { useAppContext } from '@magento/peregrine/lib/context/app';
7
5
  import { useUserContext } from '@magento/peregrine/lib/context/user';
8
-
9
6
  import mergeOperations from '@magento/peregrine/lib/util/shallowMerge';
7
+
10
8
  import defaultOperations from './savedPaymentsPage.gql';
11
9
 
12
10
  export const normalizeTokens = responseData => {
@@ -46,7 +44,6 @@ export const useSavedPaymentsPage = (props = {}) => {
46
44
  actions: { setPageLoading }
47
45
  }
48
46
  ] = useAppContext();
49
- const history = useHistory();
50
47
  const [{ isSignedIn }] = useUserContext();
51
48
 
52
49
  const { data: savedPaymentsData, loading } = useQuery(
@@ -58,13 +55,6 @@ export const useSavedPaymentsPage = (props = {}) => {
58
55
  }
59
56
  );
60
57
 
61
- // If the user is no longer signed in, redirect to the home page.
62
- useEffect(() => {
63
- if (!isSignedIn) {
64
- history.push('/');
65
- }
66
- }, [history, isSignedIn]);
67
-
68
58
  // Update the page indicator if the GraphQL query is in flight.
69
59
  useEffect(() => {
70
60
  setPageLoading(loading);
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useMemo } from 'react';
2
- import { useFieldState } from 'informed';
2
+ import useFieldState from '@magento/peregrine/lib/hooks/hook-wrappers/useInformedFieldStateWrapper';
3
3
  import { useLazyQuery } from '@apollo/client';
4
4
  import debounce from 'lodash.debounce';
5
5
 
@@ -1,5 +1,6 @@
1
1
  import { useCallback, useEffect, useRef } from 'react';
2
- import { useFieldState, useFormApi } from 'informed';
2
+ import { useFormApi } from 'informed';
3
+ import useFieldState from '@magento/peregrine/lib/hooks/hook-wrappers/useInformedFieldStateWrapper';
3
4
 
4
5
  import { getSearchParam } from '@magento/peregrine/lib/hooks/useSearchParam';
5
6
 
@@ -1,7 +1,8 @@
1
1
  import { useCallback } from 'react';
2
- import { useHistory, useLocation } from 'react-router-dom';
2
+ import { useLocation } from 'react-router-dom';
3
3
 
4
4
  import { DELIMITER } from '../FilterModal/helpers';
5
+
5
6
  // TODO: derive from store config when available
6
7
  const setSearchParams = (existing, options) => {
7
8
  const params = new URLSearchParams(existing);
@@ -23,13 +24,10 @@ const setSearchParams = (existing, options) => {
23
24
  */
24
25
  export const useSuggestedCategory = props => {
25
26
  const { onNavigate, ...restProps } = props;
26
- const { createHref } = useHistory();
27
27
  const { search } = useLocation();
28
+
28
29
  const nextSearchParams = setSearchParams(search, restProps);
29
- const destination = createHref({
30
- pathname: '/search.html',
31
- search: nextSearchParams
32
- });
30
+ const destination = `/search.html?${nextSearchParams}`;
33
31
 
34
32
  const handleClick = useCallback(() => {
35
33
  if (typeof onNavigate === 'function') {
@@ -43,20 +43,21 @@ export const PRODUCT_SEARCH = gql`
43
43
  items {
44
44
  id
45
45
  name
46
- small_image {
47
- url
48
- }
49
- url_key
50
- url_suffix
51
- price {
52
- regularPrice {
53
- amount {
54
- value
46
+ price_range {
47
+ maximum_price {
48
+ regular_price {
55
49
  currency
50
+ value
56
51
  }
57
52
  }
58
53
  }
59
54
  sku
55
+ small_image {
56
+ url
57
+ }
58
+ stock_status
59
+ type_id
60
+ url_key
60
61
  }
61
62
  page_info {
62
63
  total_pages
@@ -1,4 +1,5 @@
1
1
  import { gql } from '@apollo/client';
2
+ import { CheckoutPageFragment } from '../CheckoutPage/checkoutPageFragments.gql';
2
3
 
3
4
  export const GET_CUSTOMER = gql`
4
5
  query GetCustomerAfterSignIn {
@@ -39,8 +40,10 @@ export const MERGE_CARTS = gql`
39
40
  items {
40
41
  id
41
42
  }
43
+ ...CheckoutPageFragment
42
44
  }
43
45
  }
46
+ ${CheckoutPageFragment}
44
47
  `;
45
48
 
46
49
  export default {
@@ -0,0 +1,63 @@
1
+ import { useCallback, useEffect, useMemo } from 'react';
2
+ import { useHistory } from 'react-router-dom';
3
+
4
+ import { useUserContext } from '@magento/peregrine/lib/context/user';
5
+
6
+ /**
7
+ * @typedef {function} useSignInPage
8
+ *
9
+ * @param {String} props.createAccountPageUrl - Create Account Password Page url
10
+ * @param {String} props.forgotPasswordPageUrl - Forgot Password Page url
11
+ * @param {String} props.signedInRedirectUrl - Url to push when user is signed in
12
+ *
13
+ * @returns {{
14
+ * signInProps: object
15
+ * }}
16
+ */
17
+ export const useSignInPage = props => {
18
+ const {
19
+ createAccountPageUrl,
20
+ forgotPasswordPageUrl,
21
+ signedInRedirectUrl
22
+ } = props;
23
+ const history = useHistory();
24
+ const [{ isSignedIn }] = useUserContext();
25
+
26
+ // Keep location state in memory when pushing history and redirect to
27
+ // the `from` url instead when signing in
28
+ const historyState = useMemo(() => {
29
+ return history && history.location.state ? history.location.state : {};
30
+ }, [history]);
31
+ const fromRedirectUrl = historyState.from || null;
32
+
33
+ // Redirect if user is signed in
34
+ useEffect(() => {
35
+ if (isSignedIn) {
36
+ if (fromRedirectUrl || signedInRedirectUrl) {
37
+ history.push(fromRedirectUrl || signedInRedirectUrl);
38
+ }
39
+ }
40
+ }, [history, isSignedIn, fromRedirectUrl, signedInRedirectUrl]);
41
+
42
+ const handleShowCreateAccount = useCallback(() => {
43
+ if (createAccountPageUrl) {
44
+ history.push(createAccountPageUrl, historyState);
45
+ }
46
+ }, [createAccountPageUrl, history, historyState]);
47
+
48
+ const handleShowForgotPassword = useCallback(() => {
49
+ if (forgotPasswordPageUrl) {
50
+ history.push(forgotPasswordPageUrl, historyState);
51
+ }
52
+ }, [forgotPasswordPageUrl, history, historyState]);
53
+
54
+ const signInProps = {
55
+ classes: { modal_active: undefined },
56
+ showCreateAccount: handleShowCreateAccount,
57
+ showForgotPassword: handleShowForgotPassword
58
+ };
59
+
60
+ return {
61
+ signInProps
62
+ };
63
+ };
@@ -132,8 +132,8 @@ export const useSingleWishlist = props => {
132
132
  id: 'wishlistButton.addText',
133
133
  defaultMessage: 'Add to Favorites'
134
134
  }),
135
- disabled: isSelected,
136
- onClick: handleClick,
135
+ isDisabled: isSelected,
136
+ onPress: handleClick,
137
137
  type: 'button'
138
138
  }),
139
139
  [formatMessage, handleClick, isSelected]
@@ -23,7 +23,8 @@ export const useAddToListButton = props => {
23
23
  if (storeConfig.enable_multiple_wishlists === '1' && isSignedIn) {
24
24
  return {
25
25
  ...singleButtonProps,
26
- onClick: () => {
26
+ 'aria-haspopup': 'dialog',
27
+ onPress: () => {
27
28
  setIsModalOpen(true);
28
29
 
29
30
  if (beforeAdd) {
@@ -1,6 +1,7 @@
1
- import { useCallback, useState } from 'react';
2
- import { useMutation } from '@apollo/client';
1
+ import { useCallback, useState, useMemo } from 'react';
2
+ import { useMutation, useQuery } from '@apollo/client';
3
3
  import DEFAULT_OPERATIONS from './wishlist.gql';
4
+ import getWishlistConfigQuery from './wishlistConfig.gql';
4
5
  import mergeOperations from '../../util/shallowMerge';
5
6
 
6
7
  const dialogs = {
@@ -20,15 +21,21 @@ const dialogs = {
20
21
  */
21
22
  export const useActionMenu = (props = {}) => {
22
23
  const { id } = props;
23
- const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations);
24
+ const operations = mergeOperations(
25
+ DEFAULT_OPERATIONS,
26
+ getWishlistConfigQuery,
27
+ props.operations
28
+ );
24
29
  const { getCustomerWishlistQuery, updateWishlistMutation } = operations;
25
30
  const [currentDialog, setCurrentDialog] = useState(dialogs.NONE);
31
+ const [displayError, setDisplayError] = useState(false);
26
32
 
27
33
  const handleActionMenuClick = useCallback(() => {
28
34
  setCurrentDialog(dialogs.LIST_ACTIONS);
29
35
  }, []);
30
36
 
31
37
  const handleHideDialogs = useCallback(() => {
38
+ setDisplayError(false);
32
39
  setCurrentDialog(dialogs.NONE);
33
40
  }, []);
34
41
 
@@ -44,6 +51,26 @@ export const useActionMenu = (props = {}) => {
44
51
  { error: updateWishlistErrors, loading: isEditInProgress }
45
52
  ] = useMutation(updateWishlistMutation);
46
53
 
54
+ const { data: storeConfigData } = useQuery(
55
+ operations.getWishlistConfigQuery,
56
+ {
57
+ fetchPolicy: 'cache-and-network',
58
+ nextFetchPolicy: 'cache-first'
59
+ }
60
+ );
61
+
62
+ const shouldRender = useMemo(() => {
63
+ let multipleWishlistEnabled = false;
64
+ try {
65
+ if (storeConfigData.storeConfig.enable_multiple_wishlists === '1') {
66
+ multipleWishlistEnabled = true;
67
+ }
68
+ } catch (e) {
69
+ return false;
70
+ }
71
+ return storeConfigData && multipleWishlistEnabled;
72
+ }, [storeConfigData]);
73
+
47
74
  const handleEditWishlist = useCallback(
48
75
  async data => {
49
76
  // add private visibility because is required field for ee
@@ -63,6 +90,7 @@ export const useActionMenu = (props = {}) => {
63
90
  });
64
91
  setCurrentDialog(dialogs.NONE);
65
92
  } catch (error) {
93
+ setDisplayError(true);
66
94
  if (process.env.NODE_ENV !== 'production') {
67
95
  console.error(error);
68
96
  }
@@ -71,15 +99,21 @@ export const useActionMenu = (props = {}) => {
71
99
  [getCustomerWishlistQuery, id, updateWishlist]
72
100
  );
73
101
 
102
+ const errors = useMemo(
103
+ () => (displayError ? [updateWishlistErrors] : [false]),
104
+ [updateWishlistErrors, displayError]
105
+ );
106
+
74
107
  return {
75
108
  editFavoritesListIsOpen,
76
- formErrors: [updateWishlistErrors],
109
+ formErrors: errors,
77
110
  handleActionMenuClick,
78
111
  handleEditWishlist,
79
112
  handleHideDialogs,
80
113
  handleShowEditFavorites,
81
114
  isEditInProgress,
82
- listActionsIsOpen
115
+ listActionsIsOpen,
116
+ shouldRender
83
117
  };
84
118
  };
85
119