@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
@@ -0,0 +1,59 @@
1
+ import { ApolloLink } from '@apollo/client';
2
+ import { BrowserPersistence } from '@magento/peregrine/lib/util';
3
+
4
+ // The name of the header to exchange with the server.
5
+ const CACHE_ID_HEADER = 'x-magento-cache-id';
6
+ // The key in local storage where we save the cache id
7
+ const LOCAL_STORAGE_KEY = 'magento_cache_id';
8
+ const storage = new BrowserPersistence();
9
+
10
+ /**
11
+ * The Magento GraphQL Cache Link class is an ApolloLink that is responsible for
12
+ * Venia sending the proper `x-magento-cache-id` header with each of its GraphQL requests.
13
+ */
14
+ export default class MagentoGQLCacheLink extends ApolloLink {
15
+ // The links get reinstantiated on refresh.
16
+ // If we have an existing cache id value from a previous browsing session, use it.
17
+ #cacheId = storage.getItem(LOCAL_STORAGE_KEY) || null;
18
+
19
+ // Any time the cache id needs to be set, update both our internal variable and
20
+ // the value in local storage.
21
+ setCacheId(value) {
22
+ this.#cacheId = value;
23
+ storage.setItem(LOCAL_STORAGE_KEY, value);
24
+ }
25
+
26
+ request(operation, forward) {
27
+ // Attach the cache header to each outgoing request.
28
+ operation.setContext(previousContext => {
29
+ const { headers } = previousContext;
30
+ const withCacheHeader = {
31
+ ...headers,
32
+ [CACHE_ID_HEADER]: this.#cacheId
33
+ };
34
+
35
+ return {
36
+ ...previousContext,
37
+ headers: withCacheHeader
38
+ };
39
+ });
40
+
41
+ // Update the cache id from each response.
42
+ const updateCacheId = data => {
43
+ const context = operation.getContext();
44
+ const { response } = context;
45
+
46
+ const responseCacheId = response.headers.get(CACHE_ID_HEADER);
47
+
48
+ if (responseCacheId) {
49
+ this.setCacheId(responseCacheId);
50
+ }
51
+
52
+ // Purposefully don't modify the result,
53
+ // no other link needs to know about the cache id.
54
+ return data;
55
+ };
56
+
57
+ return forward(operation).map(updateCacheId);
58
+ }
59
+ }
package/lib/List/item.js CHANGED
@@ -35,15 +35,18 @@ const Item = props => {
35
35
  ]);
36
36
  const onFocus = useCallback(() => setFocus(key), [key, setFocus]);
37
37
 
38
- const customProps = {
39
- classes,
40
- hasFocus,
41
- isSelected,
42
- item,
43
- itemIndex,
44
- onClick,
45
- onFocus
46
- };
38
+ const customProps = useMemo(
39
+ () => ({
40
+ classes,
41
+ hasFocus,
42
+ isSelected,
43
+ item,
44
+ itemIndex,
45
+ onClick,
46
+ onFocus
47
+ }),
48
+ [classes, hasFocus, isSelected, item, itemIndex, onClick, onFocus]
49
+ );
47
50
 
48
51
  const Root = useMemo(
49
52
  () => fromRenderProp(render, Object.keys(customProps)),
package/lib/List/list.js CHANGED
@@ -37,13 +37,16 @@ const List = props => {
37
37
  ...restProps
38
38
  } = props;
39
39
 
40
- const customProps = {
41
- classes,
42
- getItemKey,
43
- items,
44
- onSelectionChange,
45
- selectionModel
46
- };
40
+ const customProps = useMemo(
41
+ () => ({
42
+ classes,
43
+ getItemKey,
44
+ items,
45
+ onSelectionChange,
46
+ selectionModel
47
+ }),
48
+ [classes, getItemKey, items, onSelectionChange, selectionModel]
49
+ );
47
50
 
48
51
  const handleSelectionChange = useCallback(
49
52
  selection => {
@@ -15,7 +15,6 @@ import { useIntl } from 'react-intl';
15
15
  * [polyfill]: https://www.npmjs.com/package/intl
16
16
  * [Intl.NumberFormat.prototype.formatToParts]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat/formatToParts
17
17
  */
18
-
19
18
  const Price = props => {
20
19
  const { locale } = useIntl();
21
20
  const { value, currencyCode, classes } = props;
@@ -36,12 +36,6 @@ const CartContextProvider = props => {
36
36
  }
37
37
  }, [cartState]);
38
38
 
39
- const derivedCartState = {
40
- ...cartState,
41
- isEmpty: isCartEmpty(cartState),
42
- derivedDetails
43
- };
44
-
45
39
  const cartApi = useMemo(
46
40
  () => ({
47
41
  actions,
@@ -50,10 +44,15 @@ const CartContextProvider = props => {
50
44
  [actions, asyncActions]
51
45
  );
52
46
 
53
- const contextValue = useMemo(() => [derivedCartState, cartApi], [
54
- cartApi,
55
- derivedCartState
56
- ]);
47
+ const contextValue = useMemo(() => {
48
+ const derivedCartState = {
49
+ ...cartState,
50
+ isEmpty: isCartEmpty(cartState),
51
+ derivedDetails
52
+ };
53
+
54
+ return [derivedCartState, cartApi];
55
+ }, [cartApi, cartState, derivedDetails]);
57
56
 
58
57
  const [fetchCartId] = useMutation(CREATE_CART_MUTATION);
59
58
  const fetchCartDetails = useAwaitQuery(CART_DETAILS_QUERY);
@@ -0,0 +1,27 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { useFieldState } from 'informed';
3
+
4
+ const useInformedFieldStateWrapper = field => {
5
+ const [isInitialRender, setIsInitialRender] = useState(true);
6
+ const warn = console.warn;
7
+ // Temporarily override console warn to remove false positive warning from useFieldState
8
+ const regex = /^Attempting to get field (.*) but it does not exist$/g;
9
+ if (isInitialRender) {
10
+ console.warn = err => {
11
+ if (!err.match(regex)) {
12
+ warn(err);
13
+ }
14
+ };
15
+ }
16
+ useEffect(() => {
17
+ setIsInitialRender(false);
18
+ }, []);
19
+ const fieldState = useFieldState(field);
20
+ if (isInitialRender) {
21
+ console.warn = warn;
22
+ }
23
+
24
+ return fieldState;
25
+ };
26
+
27
+ export default useInformedFieldStateWrapper;
@@ -0,0 +1,127 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import { matchPath } from 'react-router';
3
+ import { useHistory, useLocation } from 'react-router-dom';
4
+ import { useApolloClient } from '@apollo/client';
5
+
6
+ import { availableRoutes } from '@magento/venia-ui/lib/components/Routes/routes';
7
+
8
+ import { useAppContext } from '../context/app';
9
+ import { useRootComponents } from '../context/rootComponents';
10
+ import mergeOperations from '../util/shallowMerge';
11
+ import { getComponentData } from '../util/magentoRouteData';
12
+ import DEFAULT_OPERATIONS from '../talons/MagentoRoute/magentoRoute.gql';
13
+ import { getRootComponent } from '../talons/MagentoRoute/helpers';
14
+
15
+ const DELAY_MESSAGE_PREFIX = 'DELAY:';
16
+
17
+ const useDelayedTransition = () => {
18
+ const { pathname } = useLocation();
19
+ const history = useHistory();
20
+ const client = useApolloClient();
21
+ const operations = mergeOperations(DEFAULT_OPERATIONS);
22
+ const { resolveUrlQuery } = operations;
23
+ const [, setComponentMap] = useRootComponents();
24
+ const [, appApi] = useAppContext();
25
+ const { actions: appActions } = appApi;
26
+ const { setPageLoading } = appActions;
27
+ const unblock = useRef();
28
+
29
+ useEffect(() => {
30
+ // Override globalThis.addEventListener to prevent binding beforeunload while we add our blocker
31
+ const originalWindowAddEventListener = globalThis.addEventListener;
32
+ globalThis.addEventListener = (type, listener, options) => {
33
+ if (type === 'beforeunload') {
34
+ return;
35
+ }
36
+
37
+ if (typeof originalWindowAddEventListener === 'function') {
38
+ return originalWindowAddEventListener(type, listener, options);
39
+ }
40
+ };
41
+
42
+ unblock.current = history.block(location => {
43
+ let currentPath = pathname;
44
+
45
+ if (process.env.USE_STORE_CODE_IN_URL === 'true') {
46
+ const storeCodes = AVAILABLE_STORE_VIEWS.map(
47
+ store => `\/?${store.code}`
48
+ ).join('|');
49
+ const regex = new RegExp(`^${storeCodes}`);
50
+ currentPath = currentPath.replace(regex, '');
51
+ }
52
+
53
+ // Ignore query string changes
54
+ if (location.pathname === currentPath) {
55
+ return true;
56
+ }
57
+
58
+ // Ignore hardcoded routes
59
+ const isInternalRoute = availableRoutes.some(
60
+ ({ pattern: path, exact }) => {
61
+ return !!matchPath(location.pathname, {
62
+ path,
63
+ exact
64
+ });
65
+ }
66
+ );
67
+ if (isInternalRoute) {
68
+ return true;
69
+ }
70
+
71
+ return `${DELAY_MESSAGE_PREFIX}${location.pathname}`;
72
+ });
73
+
74
+ globalThis.addEventListener = originalWindowAddEventListener;
75
+
76
+ return () => {
77
+ if (typeof unblock.current === 'function') {
78
+ unblock.current();
79
+ }
80
+ };
81
+ }, [pathname, history]);
82
+
83
+ useEffect(() => {
84
+ globalThis.handleRouteChangeConfirmation = async (message, proceed) => {
85
+ if (globalThis.avoidDelayedTransition) {
86
+ globalThis.avoidDelayedTransition = false;
87
+ if (typeof unblock.current === 'function') {
88
+ unblock.current();
89
+ }
90
+ return proceed(true);
91
+ }
92
+
93
+ setPageLoading(true);
94
+ const currentPathname = message.replace(DELAY_MESSAGE_PREFIX, '');
95
+
96
+ const queryResult = await client.query({
97
+ query: resolveUrlQuery,
98
+ fetchPolicy: 'cache-first',
99
+ nextFetchPolicy: 'cache-first',
100
+ variables: { url: currentPathname }
101
+ });
102
+
103
+ const { data } = queryResult;
104
+ const { route } = data || {};
105
+ const { type, ...routeData } = route || {};
106
+
107
+ if (type) {
108
+ const rootComponent = await getRootComponent(type);
109
+ setComponentMap(prevMap =>
110
+ new Map(prevMap).set(currentPathname, {
111
+ component: rootComponent,
112
+ ...getComponentData(routeData),
113
+ type
114
+ })
115
+ );
116
+ }
117
+
118
+ setPageLoading(false);
119
+ if (typeof unblock.current === 'function') {
120
+ unblock.current();
121
+ }
122
+ proceed(true);
123
+ };
124
+ }, [client, resolveUrlQuery, setComponentMap, setPageLoading]);
125
+ };
126
+
127
+ export default useDelayedTransition;
@@ -0,0 +1,77 @@
1
+ import { useLayoutEffect } from 'react';
2
+ import { makeVar } from '@apollo/client';
3
+
4
+ /**
5
+ * A hook that will detect if there is a scrollbar will be added to the viewport and
6
+ * will add its width value as a `--global-scrollbar-width` custom property to the body element,
7
+ * which can be used to calculate the full width of the viewport.
8
+ */
9
+
10
+ // Check if the browser will add a scrollbar to the viewport
11
+ const getScrollBarWidth = () => {
12
+ const innerElement = document.createElement('div');
13
+ innerElement.style.cssText = 'width:100%;height:200px';
14
+
15
+ const outerElement = document.createElement('div');
16
+ outerElement.style.cssText =
17
+ 'position:absolute;top:-50px;left:0px;visibility:hidden;width:50px;height:50px;overflow:hidden';
18
+ outerElement.appendChild(innerElement);
19
+ document.body.appendChild(outerElement);
20
+ const widthWithoutScroll = innerElement.offsetWidth;
21
+ outerElement.style.overflow = 'scroll';
22
+ let widthWithScroll = innerElement.offsetWidth;
23
+ if (widthWithoutScroll === widthWithScroll)
24
+ widthWithScroll = innerElement.clientWidth;
25
+
26
+ document.body.removeChild(outerElement);
27
+
28
+ return widthWithoutScroll - widthWithScroll;
29
+ };
30
+
31
+ // Watching for body height changes, and setting custom property with scrollbar width, when scrollbar is present.
32
+ const createObserver = scrollBarWidth => {
33
+ return new globalThis.ResizeObserver(entries => {
34
+ for (const entry of entries) {
35
+ if (entry.target.scrollHeight > globalThis.innerHeight) {
36
+ entry.target.style.setProperty(
37
+ '--global-scrollbar-width',
38
+ `${scrollBarWidth}px`
39
+ );
40
+ } else {
41
+ entry.target.style.setProperty(
42
+ '--global-scrollbar-width',
43
+ '0px'
44
+ );
45
+ }
46
+ }
47
+ });
48
+ };
49
+ // Using Apollo reactive var to store observer status, in order to add observer only once.
50
+ const isObserverActive = makeVar(false);
51
+
52
+ export const useDetectScrollWidth = () => {
53
+ useLayoutEffect(() => {
54
+ if (isObserverActive()) {
55
+ return;
56
+ }
57
+ const scrollBarWidth = getScrollBarWidth();
58
+ const observer =
59
+ typeof globalThis.ResizeObserver !== 'undefined' &&
60
+ scrollBarWidth !== 0
61
+ ? createObserver(scrollBarWidth)
62
+ : null;
63
+
64
+ if (observer) {
65
+ observer.observe(document.body);
66
+ isObserverActive(true);
67
+ }
68
+
69
+ return () => {
70
+ if (observer) {
71
+ observer.disconnect();
72
+ document.body.style.removeProperty('--global-scrollbar-width');
73
+ isObserverActive(false);
74
+ }
75
+ };
76
+ }, []);
77
+ };
@@ -0,0 +1,21 @@
1
+ import { useCallback } from 'react';
2
+ import { useAppContext } from '../context/app';
3
+
4
+ export const SHIMMER_TYPE_SUFFIX = '_SHIMMER';
5
+
6
+ export default rootType => {
7
+ const [, appApi] = useAppContext();
8
+ const { actions: appActions } = appApi;
9
+ const { setNextRootComponent } = appActions;
10
+
11
+ const type = `${rootType.toUpperCase()}${SHIMMER_TYPE_SUFFIX}`;
12
+
13
+ const setShimmerType = useCallback(() => {
14
+ globalThis.avoidDelayedTransition = true;
15
+ setNextRootComponent(type);
16
+ }, [setNextRootComponent, type]);
17
+
18
+ return {
19
+ setShimmerType
20
+ };
21
+ };
@@ -0,0 +1,7 @@
1
+ export default () => {
2
+ if (typeof IntersectionObserver === 'undefined') {
3
+ return;
4
+ }
5
+
6
+ return IntersectionObserver;
7
+ };
@@ -0,0 +1,44 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ import useIntersectionObserver from '@magento/peregrine/lib/hooks/useIntersectionObserver';
4
+
5
+ export const useIsInViewport = props => {
6
+ const { elementRef, renderOnce = true } = props;
7
+ const intersectionObserver = useIntersectionObserver();
8
+ const [isInViewport, setIsInViewport] = useState(false);
9
+
10
+ useEffect(() => {
11
+ if (!elementRef || !elementRef.current || !intersectionObserver) {
12
+ setIsInViewport(true);
13
+
14
+ return null;
15
+ }
16
+
17
+ // Prevent init if already rendered once
18
+ if (isInViewport && renderOnce) {
19
+ return null;
20
+ }
21
+
22
+ const htmlElement = elementRef.current;
23
+ const elementObserver = new IntersectionObserver(
24
+ (entries, observer) => {
25
+ const isIntersecting =
26
+ entries.some(entry => entry.isIntersecting) === true;
27
+ setIsInViewport(isIntersecting);
28
+
29
+ // Stop observing if already rendered once
30
+ if (isIntersecting && renderOnce) {
31
+ observer.unobserve(htmlElement);
32
+ }
33
+ }
34
+ );
35
+
36
+ elementObserver.observe(htmlElement);
37
+
38
+ return () => {
39
+ elementObserver.unobserve(htmlElement);
40
+ };
41
+ }, [elementRef, intersectionObserver, isInViewport, renderOnce]);
42
+
43
+ return isInViewport;
44
+ };
@@ -8,7 +8,8 @@ const actionTypes = [
8
8
  'TOGGLE_SEARCH',
9
9
  'EXECUTE_SEARCH',
10
10
  'MARK_ERROR_HANDLED',
11
- 'SET_PAGE_LOADING'
11
+ 'SET_PAGE_LOADING',
12
+ 'SET_NEXT_ROOT_COMPONENT'
12
13
  ];
13
14
 
14
15
  export default createActions(...actionTypes, { prefix });
@@ -16,7 +16,8 @@ const initialState = {
16
16
  isPageLoading: false,
17
17
  overlay: false,
18
18
  pending: {},
19
- searchOpen: false
19
+ searchOpen: false,
20
+ nextRootComponent: null
20
21
  };
21
22
 
22
23
  const reducerMap = {
@@ -51,6 +52,12 @@ const reducerMap = {
51
52
  ...state,
52
53
  isPageLoading: !!payload
53
54
  };
55
+ },
56
+ [actions.setNextRootComponent]: (state, { payload }) => {
57
+ return {
58
+ ...state,
59
+ nextRootComponent: payload
60
+ };
54
61
  }
55
62
  };
56
63
 
@@ -136,7 +136,6 @@ export const useAccountInformationPage = props => {
136
136
  initialValues,
137
137
  isDisabled: isUpdatingCustomerInformation || isChangingCustomerPassword,
138
138
  isUpdateMode,
139
- isSignedIn,
140
139
  loadDataError,
141
140
  shouldShowNewPassword,
142
141
  showUpdateMode
@@ -7,12 +7,13 @@ import { RetryLink } from '@apollo/client/link/retry';
7
7
  import { CachePersistor } from 'apollo-cache-persist';
8
8
  import getWithPath from 'lodash.get';
9
9
  import setWithPath from 'lodash.set';
10
- import { useEffect, useMemo, useState } from 'react';
10
+ import { useEffect, useMemo, useState, useCallback } from 'react';
11
11
 
12
12
  import MutationQueueLink from '@adobe/apollo-link-mutation-queue';
13
13
  import attachClient from '@magento/peregrine/lib/Apollo/attachClientToStore';
14
14
  import { CACHE_PERSIST_PREFIX } from '@magento/peregrine/lib/Apollo/constants';
15
15
  import typePolicies from '@magento/peregrine/lib/Apollo/policies';
16
+ import MagentoGQLCacheLink from '@magento/peregrine/lib/Apollo/magentoGqlCacheLink';
16
17
  import { BrowserPersistence } from '@magento/peregrine/lib/util';
17
18
  import shrinkQuery from '@magento/peregrine/lib/util/shrinkQuery';
18
19
 
@@ -160,6 +161,8 @@ export const useAdapter = props => {
160
161
  []
161
162
  );
162
163
 
164
+ const magentoGqlCacheLink = useMemo(() => new MagentoGQLCacheLink(), []);
165
+
163
166
  const apolloLink = useMemo(
164
167
  () =>
165
168
  ApolloLink.from([
@@ -168,11 +171,20 @@ export const useAdapter = props => {
168
171
  mutationQueueLink,
169
172
  retryLink,
170
173
  authLink,
174
+ magentoGqlCacheLink,
171
175
  storeLink,
172
176
  errorLink,
173
177
  httpLink
174
178
  ]),
175
- [authLink, errorLink, httpLink, mutationQueueLink, retryLink, storeLink]
179
+ [
180
+ authLink,
181
+ errorLink,
182
+ httpLink,
183
+ magentoGqlCacheLink,
184
+ mutationQueueLink,
185
+ retryLink,
186
+ storeLink
187
+ ]
176
188
  );
177
189
 
178
190
  const apolloClient = useMemo(() => {
@@ -199,9 +211,17 @@ export const useAdapter = props => {
199
211
  return client;
200
212
  }, [apiBase, apolloLink]);
201
213
 
214
+ const getUserConfirmation = useCallback(async (message, callback) => {
215
+ if (typeof globalThis.handleRouteChangeConfirmation === 'function') {
216
+ return globalThis.handleRouteChangeConfirmation(message, callback);
217
+ }
218
+
219
+ return callback(globalThis.confirm(message));
220
+ }, []);
221
+
202
222
  const apolloProps = { client: apolloClient };
203
223
  const reduxProps = { store };
204
- const routerProps = { basename };
224
+ const routerProps = { basename, getUserConfirmation };
205
225
  const styleProps = { initialState: styles };
206
226
 
207
227
  // perform blocking async work here
@@ -1,5 +1,4 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from 'react';
2
- import { useHistory } from 'react-router-dom';
3
2
  import { useMutation, useQuery } from '@apollo/client';
4
3
 
5
4
  import { useAppContext } from '@magento/peregrine/lib/context/app';
@@ -38,8 +37,6 @@ export const useAddressBookPage = (props = {}) => {
38
37
  ] = useAppContext();
39
38
  const [{ isSignedIn }] = useUserContext();
40
39
 
41
- const history = useHistory();
42
-
43
40
  const { data: customerAddressesData, loading } = useQuery(
44
41
  getCustomerAddressesQuery,
45
42
  {
@@ -85,13 +82,6 @@ export const useAddressBookPage = (props = {}) => {
85
82
  // https://github.com/apollographql/apollo-feature-requests/issues/170
86
83
  const [displayError, setDisplayError] = useState(false);
87
84
 
88
- // If the user is no longer signed in, redirect to the home page.
89
- useEffect(() => {
90
- if (!isSignedIn) {
91
- history.push('/');
92
- }
93
- }, [history, isSignedIn]);
94
-
95
85
  // Update the page indicator if the GraphQL query is in flight.
96
86
  useEffect(() => {
97
87
  setPageLoading(isRefetching);
@@ -1,5 +1,14 @@
1
1
  import { gql } from '@apollo/client';
2
2
 
3
+ export const GET_STORE_CONFIG_DATA = gql`
4
+ query GetStoreConfigForBreadcrumbs {
5
+ storeConfig {
6
+ id
7
+ category_url_suffix
8
+ }
9
+ }
10
+ `;
11
+
3
12
  export const GET_BREADCRUMBS = gql`
4
13
  query GetBreadcrumbs($category_id: Int!) {
5
14
  category(id: $category_id) {
@@ -13,11 +22,11 @@ export const GET_BREADCRUMBS = gql`
13
22
  id
14
23
  name
15
24
  url_path
16
- url_suffix
17
25
  }
18
26
  }
19
27
  `;
20
28
 
21
29
  export default {
22
- getBreadcrumbsQuery: GET_BREADCRUMBS
30
+ getBreadcrumbsQuery: GET_BREADCRUMBS,
31
+ getStoreConfigQuery: GET_STORE_CONFIG_DATA
23
32
  };