@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.
- package/lib/Apollo/magentoGqlCacheLink.js +59 -0
- package/lib/List/item.js +12 -9
- package/lib/List/list.js +10 -7
- package/lib/Price/price.js +0 -1
- package/lib/context/cart.js +9 -10
- package/lib/hooks/hook-wrappers/useInformedFieldStateWrapper.js +27 -0
- package/lib/hooks/useDelayedTransition.js +127 -0
- package/lib/hooks/useDetectScrollWidth.js +77 -0
- package/lib/hooks/useInternalLink.js +21 -0
- package/lib/hooks/useIntersectionObserver.js +7 -0
- package/lib/hooks/useIsInViewport.js +44 -0
- package/lib/store/actions/app/actions.js +2 -1
- package/lib/store/reducers/app.js +8 -1
- package/lib/talons/AccountInformationPage/useAccountInformationPage.js +0 -1
- package/lib/talons/Adapter/useAdapter.js +23 -3
- package/lib/talons/AddressBookPage/useAddressBookPage.js +0 -10
- package/lib/talons/Breadcrumbs/breadcrumbs.gql.js +11 -2
- package/lib/talons/Breadcrumbs/useBreadcrumbs.js +19 -5
- package/lib/talons/CartPage/GiftCards/giftCardFragments.gql.ee.js +14 -0
- package/lib/talons/CartPage/GiftCards/giftCardFragments.gql.js +8 -0
- package/lib/talons/CartPage/GiftCards/giftCardQueries.gql.ee.js +73 -0
- package/lib/talons/CartPage/GiftCards/useGiftCards.js +25 -16
- package/lib/talons/CartPage/PriceAdjustments/CouponCode/couponCode.gql.js +56 -0
- package/lib/talons/CartPage/PriceAdjustments/CouponCode/couponCodeFragments.gql.js +10 -0
- package/lib/talons/CartPage/PriceAdjustments/{useCouponCode.js → CouponCode/useCouponCode.js} +10 -6
- package/lib/talons/CartPage/{GiftOptions → PriceAdjustments/GiftOptions}/client-schema.graphql +0 -0
- package/lib/talons/CartPage/PriceAdjustments/GiftOptions/giftOptions.gql.js +21 -0
- package/lib/talons/CartPage/{GiftOptions → PriceAdjustments/GiftOptions}/useGiftOptions.js +5 -3
- package/lib/talons/CartPage/PriceAdjustments/ShippingMethods/shippingMethods.gql.js +43 -0
- package/lib/talons/CartPage/PriceAdjustments/ShippingMethods/shippingMethodsFragments.gql.js +60 -0
- package/lib/talons/CartPage/PriceAdjustments/ShippingMethods/useShippingForm.js +8 -9
- package/lib/talons/CartPage/PriceAdjustments/ShippingMethods/useShippingMethods.js +7 -5
- package/lib/talons/CartPage/PriceAdjustments/ShippingMethods/useShippingRadios.js +1 -1
- package/lib/talons/CartPage/PriceSummary/priceSummary.gql.js +16 -0
- package/lib/talons/CartPage/PriceSummary/usePriceSummary.js +6 -5
- package/lib/talons/CartPage/ProductListing/EditModal/productForm.gql.js +78 -0
- package/lib/talons/CartPage/ProductListing/EditModal/productFormFragment.gql.js +47 -0
- package/lib/talons/CartPage/ProductListing/EditModal/useProductForm.js +18 -8
- package/lib/talons/CartPage/ProductListing/product.gql.js +4 -3
- package/lib/talons/CartPage/ProductListing/productListing.gql.ce.js +13 -1
- package/lib/talons/CartPage/ProductListing/productListing.gql.ee.js +13 -1
- package/lib/talons/CartPage/ProductListing/productListingFragments.gql.js +52 -0
- package/lib/talons/CartPage/ProductListing/useProduct.js +22 -18
- package/lib/talons/CartPage/ProductListing/useProductListing.js +8 -13
- package/lib/talons/CartPage/ProductListing/useQuantity.js +2 -1
- package/lib/talons/CartPage/cartPage.gql.js +16 -0
- package/lib/talons/CartPage/cartPageFragments.gql.js +21 -0
- package/lib/talons/CartPage/useCartPage.js +7 -6
- package/lib/talons/CategoryList/categoryList.gql.js +11 -2
- package/lib/talons/CategoryList/useCategoryList.js +8 -1
- package/lib/talons/CategoryList/useCategoryTile.js +10 -4
- package/lib/talons/CategoryTree/categoryTree.gql.js +11 -2
- package/lib/talons/CategoryTree/useCategoryTree.js +12 -3
- package/lib/talons/CheckoutPage/AddressBook/addressBook.gql.js +34 -0
- package/lib/talons/CheckoutPage/AddressBook/addressBookFragments.gql.js +31 -0
- package/lib/talons/CheckoutPage/AddressBook/useAddressBook.js +16 -8
- package/lib/talons/CheckoutPage/PaymentInformation/braintreeSummary.gql.js +1 -1
- package/lib/talons/CheckoutPage/PaymentInformation/editModal.gql.js +16 -0
- package/lib/talons/CheckoutPage/PaymentInformation/paymentInformation.gql.js +4 -8
- package/lib/talons/CheckoutPage/PaymentInformation/paymentMethods.gql.js +17 -0
- package/lib/talons/CheckoutPage/PaymentInformation/useBraintreeSummary.js +1 -1
- package/lib/talons/CheckoutPage/PaymentInformation/useEditModal.js +6 -5
- package/lib/talons/CheckoutPage/PaymentInformation/usePaymentInformation.js +22 -16
- package/lib/talons/CheckoutPage/PaymentInformation/usePaymentMethods.js +6 -3
- package/lib/talons/CheckoutPage/ShippingInformation/AddressForm/customerForm.gql.js +55 -0
- package/lib/talons/CheckoutPage/ShippingInformation/AddressForm/guestForm.gql.js +44 -0
- package/lib/talons/CheckoutPage/ShippingInformation/AddressForm/useCustomerForm.js +11 -14
- package/lib/talons/CheckoutPage/ShippingInformation/AddressForm/useGuestForm.js +6 -7
- package/lib/talons/CheckoutPage/ShippingInformation/shippingInformation.gql.js +54 -0
- package/lib/talons/CheckoutPage/ShippingInformation/shippingInformationFragments.gql.js +25 -0
- package/lib/talons/CheckoutPage/ShippingInformation/useShippingInformation.js +11 -6
- package/lib/talons/CheckoutPage/ShippingMethod/shippingMethod.gql.js +64 -0
- package/lib/talons/CheckoutPage/ShippingMethod/shippingMethodFragments.gql.js +64 -0
- package/lib/talons/CheckoutPage/{useShippingMethod.js → ShippingMethod/useShippingMethod.js} +11 -8
- package/lib/talons/Cms/cmsPage.gql.js +2 -2
- package/lib/talons/Cms/useCmsPage.js +2 -2
- package/lib/talons/CommunicationsPage/useCommunicationsPage.js +1 -2
- package/lib/talons/CreateAccount/createAccount.gql.js +3 -0
- package/lib/talons/CreateAccount/useCreateAccount.js +2 -4
- package/lib/talons/CreateAccountPage/useCreateAccountPage.js +38 -9
- package/lib/talons/FilterModal/useFilterList.js +17 -3
- package/lib/talons/FilterModal/useFilterModal.js +4 -5
- package/lib/talons/FilterSidebar/useFilterSidebar.js +4 -5
- package/lib/talons/ForgotPasswordPage/useForgotPasswordPage.js +46 -0
- package/lib/talons/FormError/useFormError.js +10 -3
- package/lib/talons/Gallery/__fixtures__/apolloMocks.js +8 -6
- package/lib/talons/Gallery/addToCart.gql.js +17 -0
- package/lib/talons/Gallery/gallery.gql.ce.js +4 -3
- package/lib/talons/Gallery/gallery.gql.ee.js +4 -3
- package/lib/talons/Gallery/useAddToCartButton.js +81 -0
- package/lib/talons/Gallery/useGallery.js +4 -7
- package/lib/talons/Gallery/useGalleryItem.js +7 -1
- package/lib/talons/Header/useCartTrigger.js +14 -7
- package/lib/talons/Link/useLink.js +68 -0
- package/lib/talons/MagentoRoute/magentoRoute.gql.js +12 -3
- package/lib/talons/MagentoRoute/useMagentoRoute.js +115 -30
- package/lib/talons/MegaMenu/megaMenu.gql.js +10 -4
- package/lib/talons/MegaMenu/useMegaMenu.js +61 -11
- package/lib/talons/MegaMenu/useMegaMenuItem.js +61 -0
- package/lib/talons/MegaMenu/useSubMenu.js +20 -0
- package/lib/talons/MiniCart/ProductList/productListFragments.gql.js +0 -1
- package/lib/talons/MiniCart/miniCart.gql.js +4 -3
- package/lib/talons/MiniCart/useMiniCart.js +15 -12
- package/lib/talons/Navigation/useNavigation.js +6 -1
- package/lib/talons/OrderHistoryPage/orderRow.gql.js +0 -1
- package/lib/talons/OrderHistoryPage/useOrderHistoryPage.js +2 -14
- package/lib/talons/PageLoadingIndicator/usePageLoadingIndicator.js +52 -0
- package/lib/talons/Postcode/usePostcode.js +2 -1
- package/lib/talons/ProductFullDetail/useProductFullDetail.js +30 -5
- package/lib/talons/Region/useRegion.js +12 -10
- package/lib/talons/RootComponents/Category/categoryFragments.gql.js +5 -4
- package/lib/talons/RootComponents/Product/productDetailFragment.gql.js +7 -0
- package/lib/talons/SavedPaymentsPage/useSavedPaymentsPage.js +1 -11
- package/lib/talons/SearchBar/useAutocomplete.js +1 -1
- package/lib/talons/SearchBar/useSearchField.js +2 -1
- package/lib/talons/SearchBar/useSuggestedCategory.js +4 -6
- package/lib/talons/SearchPage/searchPage.gql.js +10 -9
- package/lib/talons/SignIn/signIn.gql.js +3 -0
- package/lib/talons/SignInPage/useSignInPage.js +63 -0
- package/lib/talons/Wishlist/AddToListButton/helpers/useSingleWishlist.js +2 -2
- package/lib/talons/Wishlist/AddToListButton/useAddToListButton.ee.js +2 -1
- package/lib/talons/WishlistPage/useActionMenu.js +39 -5
- package/lib/talons/WishlistPage/useWishlist.js +4 -4
- package/lib/talons/WishlistPage/useWishlistPage.js +3 -11
- package/lib/talons/WishlistPage/wishlist.gql.js +1 -1
- package/lib/talons/WishlistPage/wishlistConfig.gql.ce.js +14 -0
- package/lib/talons/WishlistPage/wishlistConfig.gql.ee.js +15 -0
- package/lib/targets/peregrine-declare.js +0 -4
- package/lib/util/deriveErrorMessage.js +12 -6
- package/lib/util/images.js +3 -2
- package/lib/util/isSupportedProductType.js +5 -0
- package/lib/util/magentoRouteData.js +9 -0
- package/package.json +8 -8
|
@@ -10,14 +10,11 @@ export const useGallery = (props = {}) => {
|
|
|
10
10
|
|
|
11
11
|
useCustomerWishlistSkus();
|
|
12
12
|
|
|
13
|
-
const { data:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
);
|
|
13
|
+
const { data: storeConfigData } = useQuery(operations.getStoreConfigQuery, {
|
|
14
|
+
fetchPolicy: 'cache-and-network'
|
|
15
|
+
});
|
|
17
16
|
|
|
18
|
-
const storeConfig =
|
|
19
|
-
? wishlistConfigData.storeConfig
|
|
20
|
-
: null;
|
|
17
|
+
const storeConfig = storeConfigData ? storeConfigData.storeConfig : null;
|
|
21
18
|
|
|
22
19
|
return {
|
|
23
20
|
storeConfig
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
import { isSupportedProductType as isSupported } from '@magento/peregrine/lib/util/isSupportedProductType';
|
|
2
|
+
|
|
1
3
|
export const useGalleryItem = (props = {}) => {
|
|
2
4
|
const { item, storeConfig } = props;
|
|
3
5
|
|
|
6
|
+
const productType = item ? item.__typename : null;
|
|
7
|
+
|
|
8
|
+
const isSupportedProductType = isSupported(productType);
|
|
9
|
+
|
|
4
10
|
const wishlistButtonProps =
|
|
5
11
|
storeConfig && storeConfig.magento_wishlist_general_is_enabled === '1'
|
|
6
12
|
? {
|
|
@@ -12,5 +18,5 @@ export const useGalleryItem = (props = {}) => {
|
|
|
12
18
|
}
|
|
13
19
|
: null;
|
|
14
20
|
|
|
15
|
-
return { ...props, wishlistButtonProps };
|
|
21
|
+
return { ...props, wishlistButtonProps, isSupportedProductType };
|
|
16
22
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useCallback } from 'react';
|
|
1
|
+
import { useCallback, useState, useEffect } from 'react';
|
|
2
2
|
import { useQuery } from '@apollo/client';
|
|
3
|
-
import { useHistory } from 'react-router-dom';
|
|
3
|
+
import { useHistory, useLocation } from 'react-router-dom';
|
|
4
4
|
|
|
5
5
|
import { useCartContext } from '@magento/peregrine/lib/context/cart';
|
|
6
6
|
import { useDropdown } from '@magento/peregrine/lib/hooks/useDropdown';
|
|
@@ -30,13 +30,18 @@ export const useCartTrigger = props => {
|
|
|
30
30
|
} = props;
|
|
31
31
|
|
|
32
32
|
const [{ cartId }] = useCartContext();
|
|
33
|
+
const history = useHistory();
|
|
34
|
+
const location = useLocation();
|
|
35
|
+
const [isHidden, setIsHidden] = useState(() =>
|
|
36
|
+
DENIED_MINI_CART_ROUTES.includes(location.pathname)
|
|
37
|
+
);
|
|
38
|
+
|
|
33
39
|
const {
|
|
34
40
|
elementRef: miniCartRef,
|
|
35
41
|
expanded: miniCartIsOpen,
|
|
36
42
|
setExpanded: setMiniCartIsOpen,
|
|
37
43
|
triggerRef: miniCartTriggerRef
|
|
38
44
|
} = useDropdown();
|
|
39
|
-
const history = useHistory();
|
|
40
45
|
|
|
41
46
|
const { data } = useQuery(getItemCountQuery, {
|
|
42
47
|
fetchPolicy: 'cache-and-network',
|
|
@@ -46,10 +51,8 @@ export const useCartTrigger = props => {
|
|
|
46
51
|
},
|
|
47
52
|
skip: !cartId
|
|
48
53
|
});
|
|
54
|
+
|
|
49
55
|
const itemCount = data ? data.cart.total_quantity : 0;
|
|
50
|
-
const hideCartTrigger = DENIED_MINI_CART_ROUTES.includes(
|
|
51
|
-
history.location.pathname
|
|
52
|
-
);
|
|
53
56
|
|
|
54
57
|
const handleTriggerClick = useCallback(() => {
|
|
55
58
|
// Open the mini cart.
|
|
@@ -61,13 +64,17 @@ export const useCartTrigger = props => {
|
|
|
61
64
|
history.push('/cart');
|
|
62
65
|
}, [history]);
|
|
63
66
|
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
setIsHidden(DENIED_MINI_CART_ROUTES.includes(location.pathname));
|
|
69
|
+
}, [location]);
|
|
70
|
+
|
|
64
71
|
return {
|
|
65
72
|
handleLinkClick,
|
|
66
73
|
handleTriggerClick,
|
|
67
74
|
itemCount,
|
|
68
75
|
miniCartIsOpen,
|
|
69
76
|
miniCartRef,
|
|
70
|
-
hideCartTrigger,
|
|
77
|
+
hideCartTrigger: isHidden,
|
|
71
78
|
setMiniCartIsOpen,
|
|
72
79
|
miniCartTriggerRef
|
|
73
80
|
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
import { useLazyQuery } from '@apollo/client';
|
|
3
|
+
import useIntersectionObserver from '../../hooks/useIntersectionObserver';
|
|
4
|
+
import resourceUrl from '../../util/makeUrl';
|
|
5
|
+
import mergeOperations from '../../util/shallowMerge';
|
|
6
|
+
import DEFAULT_OPERATIONS from '../MagentoRoute/magentoRoute.gql';
|
|
7
|
+
|
|
8
|
+
const useLink = (props, passedOperations = {}) => {
|
|
9
|
+
const { prefetchType: shouldPrefetch, innerRef: originalRef, to } = props;
|
|
10
|
+
const operations = shouldPrefetch
|
|
11
|
+
? mergeOperations(DEFAULT_OPERATIONS, passedOperations)
|
|
12
|
+
: {};
|
|
13
|
+
|
|
14
|
+
const intersectionObserver = useIntersectionObserver();
|
|
15
|
+
const { resolveUrlQuery } = operations;
|
|
16
|
+
const generatedRef = useRef();
|
|
17
|
+
const elementRef =
|
|
18
|
+
originalRef || !shouldPrefetch ? originalRef : generatedRef;
|
|
19
|
+
const [runQuery, { called: pageTypeCalled }] = useLazyQuery(
|
|
20
|
+
resolveUrlQuery
|
|
21
|
+
);
|
|
22
|
+
const linkPath = shouldPrefetch ? resourceUrl(to) : null;
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (
|
|
26
|
+
!shouldPrefetch ||
|
|
27
|
+
pageTypeCalled ||
|
|
28
|
+
!runQuery ||
|
|
29
|
+
!elementRef.current ||
|
|
30
|
+
!intersectionObserver
|
|
31
|
+
) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const htmlElement = elementRef.current;
|
|
36
|
+
|
|
37
|
+
const onIntersection = entries => {
|
|
38
|
+
if (entries.some(entry => entry.isIntersecting)) {
|
|
39
|
+
observer.unobserve(htmlElement);
|
|
40
|
+
|
|
41
|
+
runQuery({
|
|
42
|
+
variables: { url: linkPath }
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const observer = new intersectionObserver(onIntersection);
|
|
47
|
+
observer.observe(htmlElement);
|
|
48
|
+
|
|
49
|
+
return () => {
|
|
50
|
+
if (htmlElement) {
|
|
51
|
+
observer.unobserve(htmlElement);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}, [
|
|
55
|
+
shouldPrefetch,
|
|
56
|
+
elementRef,
|
|
57
|
+
pageTypeCalled,
|
|
58
|
+
linkPath,
|
|
59
|
+
intersectionObserver,
|
|
60
|
+
runQuery
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
ref: elementRef
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export default useLink;
|
|
@@ -2,11 +2,20 @@ import { gql } from '@apollo/client';
|
|
|
2
2
|
|
|
3
3
|
export const RESOLVE_URL = gql`
|
|
4
4
|
query ResolveURL($url: String!) {
|
|
5
|
-
|
|
6
|
-
id
|
|
5
|
+
route(url: $url) {
|
|
7
6
|
relative_url
|
|
8
|
-
|
|
7
|
+
redirect_code
|
|
9
8
|
type
|
|
9
|
+
... on CmsPage {
|
|
10
|
+
identifier
|
|
11
|
+
}
|
|
12
|
+
... on ProductInterface {
|
|
13
|
+
id
|
|
14
|
+
__typename
|
|
15
|
+
}
|
|
16
|
+
... on CategoryInterface {
|
|
17
|
+
id
|
|
18
|
+
}
|
|
10
19
|
}
|
|
11
20
|
}
|
|
12
21
|
`;
|
|
@@ -1,12 +1,24 @@
|
|
|
1
|
-
import { useCallback, useEffect } from 'react';
|
|
1
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
2
2
|
import { useHistory, useLocation } from 'react-router-dom';
|
|
3
|
-
import {
|
|
4
|
-
import { useRootComponents } from '
|
|
5
|
-
import mergeOperations from '
|
|
3
|
+
import { useLazyQuery } from '@apollo/client';
|
|
4
|
+
import { useRootComponents } from '../../context/rootComponents';
|
|
5
|
+
import mergeOperations from '../../util/shallowMerge';
|
|
6
|
+
import { getComponentData } from '../../util/magentoRouteData';
|
|
7
|
+
import { useAppContext } from '../../context/app';
|
|
6
8
|
|
|
7
9
|
import { getRootComponent, isRedirect } from './helpers';
|
|
8
10
|
import DEFAULT_OPERATIONS from './magentoRoute.gql';
|
|
9
11
|
|
|
12
|
+
const getInlinedPageData = () => {
|
|
13
|
+
return globalThis.INLINED_PAGE_TYPE && globalThis.INLINED_PAGE_TYPE.type
|
|
14
|
+
? globalThis.INLINED_PAGE_TYPE
|
|
15
|
+
: null;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const resetInlinedPageData = () => {
|
|
19
|
+
globalThis.INLINED_PAGE_TYPE = false;
|
|
20
|
+
};
|
|
21
|
+
|
|
10
22
|
export const useMagentoRoute = (props = {}) => {
|
|
11
23
|
const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations);
|
|
12
24
|
const { resolveUrlQuery } = operations;
|
|
@@ -14,6 +26,15 @@ export const useMagentoRoute = (props = {}) => {
|
|
|
14
26
|
const { pathname } = useLocation();
|
|
15
27
|
const [componentMap, setComponentMap] = useRootComponents();
|
|
16
28
|
|
|
29
|
+
const initialized = useRef(false);
|
|
30
|
+
const fetchedPathname = useRef(null);
|
|
31
|
+
const fetching = useRef(false);
|
|
32
|
+
|
|
33
|
+
const [appState, appApi] = useAppContext();
|
|
34
|
+
const { actions: appActions } = appApi;
|
|
35
|
+
const { nextRootComponent } = appState;
|
|
36
|
+
const { setNextRootComponent, setPageLoading } = appActions;
|
|
37
|
+
|
|
17
38
|
const setComponent = useCallback(
|
|
18
39
|
(key, value) => {
|
|
19
40
|
setComponentMap(prevMap => new Map(prevMap).set(key, value));
|
|
@@ -21,23 +42,55 @@ export const useMagentoRoute = (props = {}) => {
|
|
|
21
42
|
[setComponentMap]
|
|
22
43
|
);
|
|
23
44
|
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
45
|
+
const component = componentMap.get(pathname);
|
|
46
|
+
|
|
47
|
+
const [runQuery, queryResult] = useLazyQuery(resolveUrlQuery, {
|
|
48
|
+
onCompleted: async ({ route }) => {
|
|
49
|
+
fetching.current = false;
|
|
50
|
+
if (!component) {
|
|
51
|
+
const { type, ...routeData } = route || {};
|
|
52
|
+
try {
|
|
53
|
+
const rootComponent = await getRootComponent(type);
|
|
54
|
+
setComponent(pathname, {
|
|
55
|
+
component: rootComponent,
|
|
56
|
+
...getComponentData(routeData),
|
|
57
|
+
type
|
|
58
|
+
});
|
|
59
|
+
} catch (error) {
|
|
60
|
+
setComponent(pathname, error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
onError: () => {
|
|
65
|
+
fetching.current = false;
|
|
66
|
+
}
|
|
28
67
|
});
|
|
29
68
|
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (initialized.current || !getInlinedPageData()) {
|
|
71
|
+
fetching.current = true;
|
|
72
|
+
runQuery({
|
|
73
|
+
fetchPolicy: 'cache-and-network',
|
|
74
|
+
nextFetchPolicy: 'cache-first',
|
|
75
|
+
variables: { url: pathname }
|
|
76
|
+
});
|
|
77
|
+
fetchedPathname.current = pathname;
|
|
78
|
+
}
|
|
79
|
+
}, [initialized, pathname]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
80
|
+
|
|
30
81
|
// destructure the query result
|
|
31
|
-
const { data, error
|
|
32
|
-
const {
|
|
33
|
-
const { id,
|
|
82
|
+
const { data, error } = queryResult;
|
|
83
|
+
const { route } = data || {};
|
|
84
|
+
const { id, redirect_code, relative_url, type } = route || {};
|
|
34
85
|
|
|
35
86
|
// evaluate both results and determine the response type
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
const redirect = isRedirect(redirectCode);
|
|
87
|
+
const empty = !route || !type || id < 1;
|
|
88
|
+
const redirect = isRedirect(redirect_code);
|
|
39
89
|
const fetchError = component instanceof Error && component;
|
|
40
90
|
const routeError = fetchError || error;
|
|
91
|
+
const isInitialized = initialized.current || !getInlinedPageData();
|
|
92
|
+
|
|
93
|
+
let showPageLoader = false;
|
|
41
94
|
let routeData;
|
|
42
95
|
|
|
43
96
|
if (component && !fetchError) {
|
|
@@ -48,32 +101,53 @@ export const useMagentoRoute = (props = {}) => {
|
|
|
48
101
|
routeData = { hasError: true, routeError };
|
|
49
102
|
} else if (redirect) {
|
|
50
103
|
// REDIRECT
|
|
51
|
-
routeData = {
|
|
52
|
-
|
|
104
|
+
routeData = {
|
|
105
|
+
isRedirect: true,
|
|
106
|
+
relativeUrl: relative_url.startsWith('/')
|
|
107
|
+
? relative_url
|
|
108
|
+
: '/' + relative_url
|
|
109
|
+
};
|
|
110
|
+
} else if (
|
|
111
|
+
empty &&
|
|
112
|
+
fetchedPathname.current === pathname &&
|
|
113
|
+
!fetching.current
|
|
114
|
+
) {
|
|
53
115
|
// NOT FOUND
|
|
54
116
|
routeData = { isNotFound: true };
|
|
117
|
+
} else if (nextRootComponent) {
|
|
118
|
+
// LOADING with full page shimmer
|
|
119
|
+
showPageLoader = true;
|
|
120
|
+
routeData = { isLoading: true, shimmer: nextRootComponent };
|
|
55
121
|
} else {
|
|
56
122
|
// LOADING
|
|
57
|
-
|
|
123
|
+
const isInitialLoad = !isInitialized;
|
|
124
|
+
routeData = { isLoading: true, initial: isInitialLoad };
|
|
58
125
|
}
|
|
59
126
|
|
|
60
|
-
// fetch a component if necessary
|
|
61
127
|
useEffect(() => {
|
|
62
128
|
(async () => {
|
|
63
|
-
|
|
64
|
-
if (
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
129
|
+
const inlinedData = getInlinedPageData();
|
|
130
|
+
if (inlinedData) {
|
|
131
|
+
try {
|
|
132
|
+
const componentType = inlinedData.type;
|
|
133
|
+
const rootComponent = await getRootComponent(componentType);
|
|
134
|
+
setComponent(pathname, {
|
|
135
|
+
component: rootComponent,
|
|
136
|
+
type: componentType,
|
|
137
|
+
...getComponentData(inlinedData)
|
|
138
|
+
});
|
|
139
|
+
} catch (error) {
|
|
140
|
+
setComponent(pathname, error);
|
|
141
|
+
}
|
|
74
142
|
}
|
|
143
|
+
initialized.current = true;
|
|
75
144
|
})();
|
|
76
|
-
|
|
145
|
+
|
|
146
|
+
return () => {
|
|
147
|
+
// Unmount
|
|
148
|
+
resetInlinedPageData();
|
|
149
|
+
};
|
|
150
|
+
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
77
151
|
|
|
78
152
|
// perform a redirect if necesssary
|
|
79
153
|
useEffect(() => {
|
|
@@ -82,5 +156,16 @@ export const useMagentoRoute = (props = {}) => {
|
|
|
82
156
|
}
|
|
83
157
|
}, [pathname, replace, routeData]);
|
|
84
158
|
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
if (component) {
|
|
161
|
+
// Reset loading shimmer whenever component resolves
|
|
162
|
+
setNextRootComponent(null);
|
|
163
|
+
}
|
|
164
|
+
}, [component, pathname, setNextRootComponent]);
|
|
165
|
+
|
|
166
|
+
useEffect(() => {
|
|
167
|
+
setPageLoading(showPageLoader);
|
|
168
|
+
}, [showPageLoader, setPageLoading]);
|
|
169
|
+
|
|
85
170
|
return routeData;
|
|
86
171
|
};
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { gql } from '@apollo/client';
|
|
2
|
+
export const GET_STORE_CONFIG_DATA = gql`
|
|
3
|
+
query GetStoreConfigForMegaMenu {
|
|
4
|
+
storeConfig {
|
|
5
|
+
id
|
|
6
|
+
category_url_suffix
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
`;
|
|
2
10
|
|
|
3
11
|
export const GET_MEGA_MENU = gql`
|
|
4
12
|
query getMegaMenu {
|
|
@@ -11,21 +19,18 @@ export const GET_MEGA_MENU = gql`
|
|
|
11
19
|
name
|
|
12
20
|
position
|
|
13
21
|
url_path
|
|
14
|
-
url_suffix
|
|
15
22
|
children {
|
|
16
23
|
id
|
|
17
24
|
include_in_menu
|
|
18
25
|
name
|
|
19
26
|
position
|
|
20
27
|
url_path
|
|
21
|
-
url_suffix
|
|
22
28
|
children {
|
|
23
29
|
id
|
|
24
30
|
include_in_menu
|
|
25
31
|
name
|
|
26
32
|
position
|
|
27
33
|
url_path
|
|
28
|
-
url_suffix
|
|
29
34
|
}
|
|
30
35
|
}
|
|
31
36
|
}
|
|
@@ -34,5 +39,6 @@ export const GET_MEGA_MENU = gql`
|
|
|
34
39
|
`;
|
|
35
40
|
|
|
36
41
|
export default {
|
|
37
|
-
getMegaMenuQuery: GET_MEGA_MENU
|
|
42
|
+
getMegaMenuQuery: GET_MEGA_MENU,
|
|
43
|
+
getStoreConfigQuery: GET_STORE_CONFIG_DATA
|
|
38
44
|
};
|
|
@@ -1,29 +1,46 @@
|
|
|
1
|
-
import mergeOperations from '../../util/shallowMerge';
|
|
2
|
-
import DEFAULT_OPERATIONS from './megaMenu.gql';
|
|
3
|
-
import { useQuery } from '@apollo/client';
|
|
4
1
|
import { useMemo, useState, useCallback, useEffect } from 'react';
|
|
5
2
|
import { useLocation } from 'react-router-dom';
|
|
3
|
+
import useInternalLink from '../../hooks/useInternalLink';
|
|
4
|
+
|
|
5
|
+
import { useQuery } from '@apollo/client';
|
|
6
|
+
import { useEventListener } from '../../hooks/useEventListener';
|
|
7
|
+
|
|
8
|
+
import mergeOperations from '../../util/shallowMerge';
|
|
9
|
+
import DEFAULT_OPERATIONS from './megaMenu.gql';
|
|
6
10
|
|
|
7
11
|
/**
|
|
8
12
|
* The useMegaMenu talon complements the MegaMenu component.
|
|
9
13
|
*
|
|
10
14
|
* @param {Object} props
|
|
11
15
|
* @param {*} props.operations GraphQL operations used by talons
|
|
16
|
+
* @param {React.RefObject} props.mainNavRef Reference to main navigation DOM node
|
|
12
17
|
*
|
|
13
18
|
* @return {MegaMenuTalonProps}
|
|
14
19
|
*/
|
|
15
20
|
export const useMegaMenu = (props = {}) => {
|
|
16
21
|
const operations = mergeOperations(DEFAULT_OPERATIONS, props.operations);
|
|
17
|
-
const { getMegaMenuQuery } = operations;
|
|
22
|
+
const { getMegaMenuQuery, getStoreConfigQuery } = operations;
|
|
18
23
|
|
|
19
24
|
const location = useLocation();
|
|
20
25
|
const [activeCategoryId, setActiveCategoryId] = useState(null);
|
|
26
|
+
const [subMenuState, setSubMenuState] = useState(false);
|
|
27
|
+
const [disableFocus, setDisableFocus] = useState(false);
|
|
28
|
+
|
|
29
|
+
const { data: storeConfigData } = useQuery(getStoreConfigQuery, {
|
|
30
|
+
fetchPolicy: 'cache-and-network'
|
|
31
|
+
});
|
|
21
32
|
|
|
22
33
|
const { data } = useQuery(getMegaMenuQuery, {
|
|
23
34
|
fetchPolicy: 'cache-and-network',
|
|
24
35
|
nextFetchPolicy: 'cache-first'
|
|
25
36
|
});
|
|
26
37
|
|
|
38
|
+
const categoryUrlSuffix = useMemo(() => {
|
|
39
|
+
if (storeConfigData) {
|
|
40
|
+
return storeConfigData.storeConfig.category_url_suffix;
|
|
41
|
+
}
|
|
42
|
+
}, [storeConfigData]);
|
|
43
|
+
|
|
27
44
|
/**
|
|
28
45
|
* Check if category should be visible on the storefront.
|
|
29
46
|
*
|
|
@@ -40,15 +57,16 @@ export const useMegaMenu = (props = {}) => {
|
|
|
40
57
|
* @param {MegaMenuCategory} category
|
|
41
58
|
* @returns {boolean}
|
|
42
59
|
*/
|
|
60
|
+
|
|
43
61
|
const isActive = useCallback(
|
|
44
|
-
({ url_path
|
|
62
|
+
({ url_path }) => {
|
|
45
63
|
if (!url_path) return false;
|
|
46
64
|
|
|
47
|
-
const categoryUrlPath = `/${url_path}${
|
|
65
|
+
const categoryUrlPath = `/${url_path}${categoryUrlSuffix || ''}`;
|
|
48
66
|
|
|
49
67
|
return location.pathname === categoryUrlPath;
|
|
50
68
|
},
|
|
51
|
-
[location.pathname]
|
|
69
|
+
[location.pathname, categoryUrlSuffix]
|
|
52
70
|
);
|
|
53
71
|
|
|
54
72
|
/**
|
|
@@ -106,6 +124,21 @@ export const useMegaMenu = (props = {}) => {
|
|
|
106
124
|
[isActive]
|
|
107
125
|
);
|
|
108
126
|
|
|
127
|
+
const handleClickOutside = e => {
|
|
128
|
+
if (!props.mainNavRef.current.contains(e.target)) {
|
|
129
|
+
setSubMenuState(false);
|
|
130
|
+
setDisableFocus(true);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
useEventListener(globalThis, 'mousedown', handleClickOutside);
|
|
135
|
+
useEventListener(globalThis, 'mouseout', handleClickOutside);
|
|
136
|
+
useEventListener(globalThis, 'keydown', handleClickOutside);
|
|
137
|
+
|
|
138
|
+
const handleSubMenuFocus = useCallback(() => {
|
|
139
|
+
setSubMenuState(true);
|
|
140
|
+
}, [setSubMenuState]);
|
|
141
|
+
|
|
109
142
|
useEffect(() => {
|
|
110
143
|
const activeCategory = findActiveCategory(
|
|
111
144
|
location.pathname,
|
|
@@ -119,9 +152,22 @@ export const useMegaMenu = (props = {}) => {
|
|
|
119
152
|
}
|
|
120
153
|
}, [findActiveCategory, location.pathname, megaMenuData]);
|
|
121
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Sets next root component to show proper loading effect
|
|
157
|
+
*
|
|
158
|
+
* @returns {void}
|
|
159
|
+
*/
|
|
160
|
+
const { setShimmerType } = useInternalLink('category');
|
|
161
|
+
|
|
122
162
|
return {
|
|
123
163
|
megaMenuData,
|
|
124
|
-
activeCategoryId
|
|
164
|
+
activeCategoryId,
|
|
165
|
+
categoryUrlSuffix,
|
|
166
|
+
handleClickOutside,
|
|
167
|
+
subMenuState,
|
|
168
|
+
disableFocus,
|
|
169
|
+
handleSubMenuFocus,
|
|
170
|
+
handleNavigate: setShimmerType
|
|
125
171
|
};
|
|
126
172
|
};
|
|
127
173
|
|
|
@@ -133,8 +179,13 @@ export const useMegaMenu = (props = {}) => {
|
|
|
133
179
|
* @property {MegaMenuCategory} megaMenuData - The Object with categories contains only categories
|
|
134
180
|
* with the include_in_menu = 1 flag. The categories are sorted
|
|
135
181
|
* based on the field position.
|
|
136
|
-
* @property {int}
|
|
137
|
-
*
|
|
182
|
+
* @property {int} activeCategoryId returns the currently selected category id.
|
|
183
|
+
* @property {String} categoryUrlSuffix store's category url suffix to construct category URL
|
|
184
|
+
* @property {Function} handleClickOutside function to handle mouse/key events.
|
|
185
|
+
* @property {Boolean} subMenuState maintaining sub-menu open/close state
|
|
186
|
+
* @property {Boolean} disableFocus state to disable focus
|
|
187
|
+
* @property {Function} handleSubMenuFocus toggle function to handle sub-menu focus
|
|
188
|
+
* @property {function} handleNavigate - callback to fire on link click
|
|
138
189
|
*/
|
|
139
190
|
|
|
140
191
|
/**
|
|
@@ -146,6 +197,5 @@ export const useMegaMenu = (props = {}) => {
|
|
|
146
197
|
* @property {String} name - name of the category
|
|
147
198
|
* @property {int} position - value used for sorting
|
|
148
199
|
* @property {String} url_path - URL path for a category
|
|
149
|
-
* @property {String} url_suffix - URL Suffix for the category
|
|
150
200
|
* @property {MegaMenuCategory} children - child category
|
|
151
201
|
*/
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export const useMegaMenuItem = props => {
|
|
4
|
+
const { category, activeCategoryId, subMenuState, disableFocus } = props;
|
|
5
|
+
|
|
6
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
7
|
+
const isActive = category.id === activeCategoryId;
|
|
8
|
+
|
|
9
|
+
const handleCloseSubMenu = useCallback(() => {
|
|
10
|
+
setIsFocused(false);
|
|
11
|
+
}, [setIsFocused]);
|
|
12
|
+
|
|
13
|
+
const isMenuActive = useMemo(() => {
|
|
14
|
+
if (!isFocused) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
if (subMenuState) {
|
|
18
|
+
return true;
|
|
19
|
+
} else if (disableFocus) {
|
|
20
|
+
setIsFocused(false);
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}, [isFocused, subMenuState, disableFocus]);
|
|
24
|
+
|
|
25
|
+
const handleKeyDown = useCallback(
|
|
26
|
+
event => {
|
|
27
|
+
const { key: pressedKey, shiftKey } = event;
|
|
28
|
+
|
|
29
|
+
// checking down arrow and spacebar
|
|
30
|
+
if (pressedKey === 'ArrowDown' || pressedKey === ' ') {
|
|
31
|
+
event.preventDefault();
|
|
32
|
+
if (category.children.length) {
|
|
33
|
+
setIsFocused(true);
|
|
34
|
+
} else {
|
|
35
|
+
setIsFocused(false);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
//checking up arrow or escape
|
|
42
|
+
if (pressedKey === 'ArrowUp' || pressedKey === 'Escape') {
|
|
43
|
+
setIsFocused(false);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
//checking Tab with Shift
|
|
47
|
+
if (shiftKey && pressedKey === 'Tab') {
|
|
48
|
+
setIsFocused(false);
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
[category.children.length]
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
isFocused,
|
|
56
|
+
isActive,
|
|
57
|
+
handleCloseSubMenu,
|
|
58
|
+
isMenuActive,
|
|
59
|
+
handleKeyDown
|
|
60
|
+
};
|
|
61
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useKeyboard } from 'react-aria';
|
|
2
|
+
|
|
3
|
+
export const useSubMenu = props => {
|
|
4
|
+
const { isFocused, subMenuState, handleCloseSubMenu } = props;
|
|
5
|
+
|
|
6
|
+
const { keyboardProps } = useKeyboard({
|
|
7
|
+
onKeyDown: e => {
|
|
8
|
+
//checking for Tab without Shift
|
|
9
|
+
if (!e.shiftKey && e.key === 'Tab') {
|
|
10
|
+
e.target.addEventListener('blur', handleCloseSubMenu);
|
|
11
|
+
} else {
|
|
12
|
+
e.target.removeEventListener('blur', handleCloseSubMenu);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const isSubMenuActive = isFocused && subMenuState;
|
|
18
|
+
|
|
19
|
+
return { keyboardProps, isSubMenuActive };
|
|
20
|
+
};
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { gql } from '@apollo/client';
|
|
2
2
|
|
|
3
|
-
export const
|
|
4
|
-
query
|
|
3
|
+
export const GET_STORE_CONFIG_DATA = gql`
|
|
4
|
+
query getStoreConfigForMiniCart {
|
|
5
5
|
storeConfig {
|
|
6
6
|
id
|
|
7
|
+
product_url_suffix
|
|
7
8
|
configurable_thumbnail_source
|
|
8
9
|
}
|
|
9
10
|
}
|
|
10
11
|
`;
|
|
11
12
|
|
|
12
13
|
export default {
|
|
13
|
-
|
|
14
|
+
getStoreConfigQuery: GET_STORE_CONFIG_DATA
|
|
14
15
|
};
|