@evenicanpm/storefront-core 2.4.2 → 2.5.1
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/CHANGELOG.md +14 -0
- package/package.json +90 -90
- package/src/api-manager/datasources/d365/d365-cart.datasource.ts +4 -3
- package/src/api-manager/datasources/d365/d365-user.datasource.ts +3 -3
- package/src/api-manager/datasources/d365/utils/get-context-cookie.ts +1 -0
- package/src/api-manager/datasources/e4/address/e4-address.datasource.ts +5 -14
- package/src/api-manager/datasources/e4/address/e4-address.translator.ts +4 -3
- package/src/api-manager/datasources/e4/categories/e4-categories.translator.ts +1 -1
- package/src/api-manager/datasources/e4/graphqlRequestSdk.ts +29 -3
- package/src/api-manager/datasources/e4/product/e4-product.translator.ts +1 -1
- package/src/api-manager/index.ts +1 -1
- package/src/api-manager/lib/get-graphql-client.ts +1 -1
- package/src/api-manager/services/cart/mutations/add-discount-code.ts +0 -11
- package/src/api-manager/services/create-query.ts +13 -7
- package/src/api-manager/services/user/mutations/add-to-wishlist.ts +1 -1
- package/src/api-manager/services/user/mutations/remove-from-wishlist.ts +1 -1
- package/src/auth/AUTH GUIDE.md +880 -0
- package/src/cms/blocks/components/featured-categories.tsx +18 -15
- package/src/cms/blocks/components/featured-products.tsx +3 -2
- package/src/cms/blocks/components/footer/data/index.ts +0 -0
- package/src/cms/blocks/components/footer/index.tsx +0 -0
- package/src/cms/blocks/components/footer/sections/footer-app-store.tsx +0 -0
- package/src/cms/blocks/components/footer/sections/footer-contact.tsx +0 -2
- package/src/cms/blocks/components/footer/sections/footer-links.tsx +0 -0
- package/src/cms/blocks/components/footer/sections/footer-logo.tsx +2 -1
- package/src/cms/blocks/components/footer/sections/footer-social-links.tsx +0 -0
- package/src/cms/blocks/components/footer/styles/index.ts +0 -0
- package/src/cms/blocks/components/hero-carousel/index.tsx +0 -0
- package/src/cms/blocks/components/product-carousel/index.tsx +0 -0
- package/src/cms/blocks/components/shared/featured-product-card.tsx +0 -0
- package/src/cms/blocks/components/shared/product-category-item.tsx +0 -0
- package/src/cms/blocks/components/shared/product-grid.tsx +0 -0
- package/src/cms/blocks/components/shared/top-categories-card.tsx +0 -0
- package/src/cms/blocks/components/shared/top-rating-product-card.tsx +0 -0
- package/src/cms/blocks/icons/components/category.tsx +0 -0
- package/src/cms/blocks/icons/components/dotted-star.tsx +0 -0
- package/src/cms/blocks/icons/components/gift-box.tsx +0 -0
- package/src/cms/blocks/icons/components/light.tsx +0 -0
- package/src/cms/blocks/icons/components/new-arrival.tsx +0 -0
- package/src/cms/blocks/icons/components/rank-badge.tsx +0 -0
- package/src/components/SearchInput.stories.tsx +12 -10
- package/src/components/base-layout.tsx +42 -0
- package/src/components/categories/category-list/category-list.tsx +32 -12
- package/src/components/header/__tests__/header.test.tsx +0 -24
- package/src/components/header/components/mobile-header.tsx +16 -8
- package/src/components/header/header.tsx +6 -3
- package/src/components/mini-cart/components/bottom-actions.tsx +7 -2
- package/src/components/mini-cart/components/cart-item.tsx +16 -13
- package/src/components/mini-cart/components/empty-view.tsx +12 -6
- package/src/components/mini-cart/components/top-header.tsx +9 -8
- package/src/components/mini-cart/mini-cart-trigger.tsx +11 -5
- package/src/components/mini-cart/mini-cart.tsx +30 -15
- package/src/components/mobile-navigation/mobile-navigation-bar.tsx +6 -2
- package/src/components/navbar/categories.tsx +8 -2
- package/src/components/navbar/category-based-menu/category-based-menu.tsx +6 -2
- package/src/components/navbar/mega-menu/mega-menu.tsx +22 -14
- package/src/components/navbar/mobile-menu/mobile-menu.test.tsx +8 -11
- package/src/components/navbar/mobile-menu/mobile-menu.tsx +13 -11
- package/src/components/navbar/nav-list/nav-list.tsx +3 -2
- package/src/components/navbar/navbar.tsx +4 -2
- package/src/components/product-cards/product-card/product-card.tsx +50 -26
- package/src/components/product-cards/product-card-plp/product-card.tsx +37 -16
- package/src/components/product-cards/product-card-plp-list/product-card.tsx +38 -16
- package/src/components/product-cards/product-card-search/product-card.tsx +34 -13
- package/src/components/product-dialog/ProductDialog.stories.tsx +20 -18
- package/src/components/product-dialog/compound/product-dialog.tsx +7 -4
- package/src/components/product-dimensions/compound/dimensions-group.tsx +4 -2
- package/src/components/product-dimensions/compound/dimensions.tsx +15 -6
- package/src/components/product-quantity-buttons/compound/quantity-buttons-root.tsx +21 -13
- package/src/components/products-view/compound/products-grid-view.tsx +11 -4
- package/src/components/products-view/compound/products-list-view.tsx +11 -4
- package/src/components/search-bar/compound/search-bar-root.tsx +6 -4
- package/src/components/search-bar/compound/textfield-adornment-category.tsx +25 -8
- package/src/components/search-bar/hooks/use-click-outside.ts +7 -1
- package/src/components/search-bar/hooks/use-search.ts +3 -3
- package/src/components/user/index.ts +3 -0
- package/src/components/user/use-user-auth.ts +163 -0
- package/src/components/user/user.tsx +215 -0
- package/src/components/wishlist-dialogs/add-to-wishlist/AddToWishlist.stories.tsx +20 -15
- package/src/components/wishlist-dialogs/add-to-wishlist/compound/add-to-wishlist-dialog.tsx +11 -8
- package/src/components/wishlist-dialogs/add-to-wishlist/compound/add-to-wishlist-list.tsx +18 -3
- package/src/components/wishlist-dialogs/add-to-wishlist/index.tsx +0 -1
- package/src/components/wishlist-dialogs/create-wishlist/compound/create-wishlist-dialog.tsx +6 -2
- package/src/hooks/use-overflow-detect.ts +4 -4
- package/src/lib/graphqlRequestSdk.ts +0 -0
- package/src/lib/page-slugs.ts +16 -0
- package/src/pages/account/account-navigation.tsx +7 -14
- package/src/pages/account/account-routes.ts +4 -9
- package/src/pages/account/addresses/address-form.tsx +38 -16
- package/src/pages/account/addresses/address-item.tsx +18 -8
- package/src/pages/account/dashboard-header.tsx +20 -13
- package/src/pages/account/orders/order-history-filters-panel.tsx +21 -20
- package/src/pages/account/orders/order-history-header.tsx +14 -4
- package/src/pages/account/orders/order-history-root.tsx +3 -1
- package/src/pages/account/orders/order-history-row.tsx +12 -4
- package/src/pages/account/orders/order-history-search-and-filter.tsx +88 -59
- package/src/pages/account/orders/order-history-search-dropdown.tsx +26 -9
- package/src/pages/account/orders/order-history-sort.tsx +3 -1
- package/src/pages/account/orders/order-history-table.tsx +27 -5
- package/src/pages/account/orders/order-progress.tsx +12 -6
- package/src/pages/account/orders/order-row.tsx +18 -6
- package/src/pages/account/orders/order-summary.tsx +24 -8
- package/src/pages/account/orders/ordered-products.tsx +15 -5
- package/src/pages/account/profile/user-info.tsx +11 -3
- package/src/pages/account/table-row-skeleton.tsx +1 -1
- package/src/pages/account/wishlist/create-new-list.tsx +33 -9
- package/src/pages/account/wishlist/wishlist-item.tsx +18 -6
- package/src/pages/cart/cart-item.tsx +21 -7
- package/src/pages/cart/checkout-form.tsx +55 -22
- package/src/pages/cart/coupon-entry.tsx +143 -36
- package/src/pages/cart/estimate-shipping.tsx +45 -21
- package/src/pages/checkout/checkout-alt-form/checkout-form.tsx +22 -14
- package/src/pages/checkout/checkout-alt-form/checkout-step.tsx +37 -16
- package/src/pages/checkout/checkout-alt-form/steps/address/address-card.tsx +9 -3
- package/src/pages/checkout/checkout-alt-form/steps/address/delivery-address.tsx +84 -51
- package/src/pages/checkout/checkout-alt-form/steps/address/new-address-form.tsx +41 -19
- package/src/pages/checkout/checkout-alt-form/steps/customer-info/customer-information.tsx +17 -10
- package/src/pages/checkout/checkout-alt-form/steps/payment/payment-details.tsx +15 -6
- package/src/pages/checkout/checkout-alt-form/steps/shipping/delivery-options.tsx +6 -2
- package/src/pages/checkout/checkout-alt-summary/cart-item.tsx +12 -4
- package/src/pages/checkout/checkout-alt-summary/checkout-alt-summary.tsx +12 -5
- package/src/pages/checkout/checkout-alt-summary/list-item.tsx +17 -10
- package/src/pages/confirmation/address.tsx +6 -2
- package/src/pages/confirmation/confirmation-summary.tsx +12 -4
- package/src/pages/confirmation/ordered-products.tsx +12 -4
- package/src/pages/product-details/bopis/store-card/shop-card.tsx +2 -2
- package/src/pages/product-details/bopis/store-card/utils/getDaysResources.ts +1 -1
- package/src/pages/product-details/product-intro/compound/product-info.tsx +41 -18
- package/src/pages/product-details/product-intro/compound/product-intro-images.tsx +18 -12
- package/src/pages/product-details/product-intro/compound/product-intro.tsx +20 -10
- package/src/pages/product-details/product-intro/compound/thumbnail-with-skeleton.tsx +1 -1
- package/src/pages/product-details/product-tabs.tsx +20 -12
- package/src/pages/product-list/product-list-view.tsx +4 -2
- package/src/pages/product-list/search-bar.tsx +7 -7
- package/src/pages/product-list/side-nav.tsx +5 -3
- package/src/pages/quickorder/order-upload.tsx +1 -1
- package/src/pages/quickorder/provider.tsx +26 -14
- package/src/pages/quickorder/quick-order.tsx +49 -22
- package/src/providers/nav-provider/index.tsx +16 -11
- package/tsconfig.json +0 -5
- package/src/components/header/components/user.tsx +0 -322
- /package/src/components/{header → user}/__tests__/user.test.tsx +0 -0
|
@@ -4,7 +4,7 @@ import type { Cart } from "@evenicanpm/storefront-core/src/api-manager/schemas/c
|
|
|
4
4
|
import { MiniCart } from "@evenicanpm/storefront-core/src/components/mini-cart/index";
|
|
5
5
|
import { useNotification } from "@evenicanpm/storefront-core/src/providers/notifications/use-notification";
|
|
6
6
|
import { Badge, Drawer, IconButton } from "@mui/material";
|
|
7
|
-
import React, { createContext, useContext, useState } from "react";
|
|
7
|
+
import React, { createContext, useContext, useMemo, useState } from "react";
|
|
8
8
|
|
|
9
9
|
const BADGE_STYLES = {
|
|
10
10
|
"& .MuiBadge-badge": {
|
|
@@ -43,8 +43,10 @@ const MiniCartTrigger = ({ cart, children }: MiniCartTriggerProps) => {
|
|
|
43
43
|
}
|
|
44
44
|
}, [alert]);
|
|
45
45
|
|
|
46
|
+
const contextValue = useMemo(() => ({ cart, open, setOpen }), [cart, open]);
|
|
47
|
+
|
|
46
48
|
return (
|
|
47
|
-
<MiniCartTriggerContext.Provider value={
|
|
49
|
+
<MiniCartTriggerContext.Provider value={contextValue}>
|
|
48
50
|
{children}
|
|
49
51
|
</MiniCartTriggerContext.Provider>
|
|
50
52
|
);
|
|
@@ -52,7 +54,7 @@ const MiniCartTrigger = ({ cart, children }: MiniCartTriggerProps) => {
|
|
|
52
54
|
|
|
53
55
|
/* ---------- Subcomponents ---------- */
|
|
54
56
|
|
|
55
|
-
|
|
57
|
+
const TriggerBadge = ({ children }: { children: React.ReactNode }) => {
|
|
56
58
|
const ctx = useContext(MiniCartTriggerContext);
|
|
57
59
|
if (!ctx) return null;
|
|
58
60
|
const { cart } = ctx;
|
|
@@ -67,7 +69,7 @@ MiniCartTrigger.Badge = ({ children }: { children: React.ReactNode }) => {
|
|
|
67
69
|
);
|
|
68
70
|
};
|
|
69
71
|
|
|
70
|
-
|
|
72
|
+
const TriggerButton = ({ children }: { children: React.ReactNode }) => {
|
|
71
73
|
const ctx = useContext(MiniCartTriggerContext);
|
|
72
74
|
if (!ctx) return null;
|
|
73
75
|
const { setOpen } = ctx;
|
|
@@ -78,7 +80,7 @@ MiniCartTrigger.Button = ({ children }: { children: React.ReactNode }) => {
|
|
|
78
80
|
);
|
|
79
81
|
};
|
|
80
82
|
|
|
81
|
-
|
|
83
|
+
const TriggerDrawer = () => {
|
|
82
84
|
const ctx = useContext(MiniCartTriggerContext);
|
|
83
85
|
if (!ctx) return null;
|
|
84
86
|
const { cart, open, setOpen } = ctx;
|
|
@@ -94,4 +96,8 @@ MiniCartTrigger.Drawer = () => {
|
|
|
94
96
|
);
|
|
95
97
|
};
|
|
96
98
|
|
|
99
|
+
MiniCartTrigger.Badge = TriggerBadge;
|
|
100
|
+
MiniCartTrigger.Button = TriggerButton;
|
|
101
|
+
MiniCartTrigger.Drawer = TriggerDrawer;
|
|
102
|
+
|
|
97
103
|
export default MiniCartTrigger;
|
|
@@ -12,7 +12,7 @@ import Divider from "@mui/material/Divider";
|
|
|
12
12
|
import { useRouter } from "next/navigation";
|
|
13
13
|
import { useFormatter } from "next-intl";
|
|
14
14
|
import type React from "react";
|
|
15
|
-
import { createContext, useContext } from "react";
|
|
15
|
+
import { createContext, useContext, useMemo } from "react";
|
|
16
16
|
import type { Cart, CartLine } from "@/schemas/cart.schema";
|
|
17
17
|
|
|
18
18
|
interface MiniCartContextValue {
|
|
@@ -74,17 +74,27 @@ const MiniCart = ({ cart, closeDrawer, children }: MiniCartProps) => {
|
|
|
74
74
|
push(path);
|
|
75
75
|
};
|
|
76
76
|
|
|
77
|
+
const contextValue = useMemo(
|
|
78
|
+
() => ({
|
|
79
|
+
cart,
|
|
80
|
+
closeDrawer,
|
|
81
|
+
currency,
|
|
82
|
+
handleCartAmountChange,
|
|
83
|
+
shouldShowLoadingSpinner,
|
|
84
|
+
handleNavigate,
|
|
85
|
+
}),
|
|
86
|
+
[
|
|
87
|
+
cart,
|
|
88
|
+
closeDrawer,
|
|
89
|
+
currency,
|
|
90
|
+
handleCartAmountChange,
|
|
91
|
+
shouldShowLoadingSpinner,
|
|
92
|
+
handleNavigate,
|
|
93
|
+
],
|
|
94
|
+
);
|
|
95
|
+
|
|
77
96
|
return (
|
|
78
|
-
<MiniCartContext.Provider
|
|
79
|
-
value={{
|
|
80
|
-
cart,
|
|
81
|
-
closeDrawer,
|
|
82
|
-
currency,
|
|
83
|
-
handleCartAmountChange,
|
|
84
|
-
shouldShowLoadingSpinner,
|
|
85
|
-
handleNavigate,
|
|
86
|
-
}}
|
|
87
|
-
>
|
|
97
|
+
<MiniCartContext.Provider value={contextValue}>
|
|
88
98
|
<Box width="100%" minWidth={380}>
|
|
89
99
|
{children}
|
|
90
100
|
</Box>
|
|
@@ -94,7 +104,7 @@ const MiniCart = ({ cart, closeDrawer, children }: MiniCartProps) => {
|
|
|
94
104
|
|
|
95
105
|
/* ---------- Subcomponents ---------- */
|
|
96
106
|
|
|
97
|
-
|
|
107
|
+
const Header = () => {
|
|
98
108
|
const ctx = useContext(MiniCartContext);
|
|
99
109
|
if (!ctx) return null;
|
|
100
110
|
const { cart, closeDrawer } = ctx;
|
|
@@ -106,11 +116,11 @@ MiniCart.Header = () => {
|
|
|
106
116
|
);
|
|
107
117
|
};
|
|
108
118
|
|
|
109
|
-
|
|
119
|
+
const MiniCartDivider = () => {
|
|
110
120
|
return <Divider />;
|
|
111
121
|
};
|
|
112
122
|
|
|
113
|
-
|
|
123
|
+
const Items = () => {
|
|
114
124
|
const ctx = useContext(MiniCartContext);
|
|
115
125
|
if (!ctx) return null;
|
|
116
126
|
const {
|
|
@@ -149,7 +159,7 @@ MiniCart.Items = () => {
|
|
|
149
159
|
);
|
|
150
160
|
};
|
|
151
161
|
|
|
152
|
-
|
|
162
|
+
const Actions = () => {
|
|
153
163
|
const ctx = useContext(MiniCartContext);
|
|
154
164
|
if (!ctx) return null;
|
|
155
165
|
const { cart, currency, handleNavigate } = ctx;
|
|
@@ -166,4 +176,9 @@ MiniCart.Actions = () => {
|
|
|
166
176
|
) : null;
|
|
167
177
|
};
|
|
168
178
|
|
|
179
|
+
MiniCart.Header = Header;
|
|
180
|
+
MiniCart.Divider = MiniCartDivider;
|
|
181
|
+
MiniCart.Items = Items;
|
|
182
|
+
MiniCart.Actions = Actions;
|
|
183
|
+
|
|
169
184
|
export default MiniCart;
|
|
@@ -21,7 +21,7 @@ import Box from "@mui/material/Box";
|
|
|
21
21
|
import type { Theme } from "@mui/material/styles";
|
|
22
22
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
23
23
|
import { useTranslations } from "next-intl";
|
|
24
|
-
import { createContext, Fragment, useContext } from "react";
|
|
24
|
+
import { createContext, Fragment, useContext, useMemo } from "react";
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
Usage Examples:
|
|
@@ -73,11 +73,15 @@ export default function MobileNavigationBar({
|
|
|
73
73
|
];
|
|
74
74
|
|
|
75
75
|
const listToRender = menuList || defaultMenuList;
|
|
76
|
+
const contextValue = useMemo(
|
|
77
|
+
() => ({ menuList: listToRender }),
|
|
78
|
+
[listToRender],
|
|
79
|
+
);
|
|
76
80
|
|
|
77
81
|
if (!DOWN_900) return null;
|
|
78
82
|
|
|
79
83
|
return (
|
|
80
|
-
<MobileNavContext.Provider value={
|
|
84
|
+
<MobileNavContext.Provider value={contextValue}>
|
|
81
85
|
<Box position="relative" display="flex" flexDirection="column">
|
|
82
86
|
<Wrapper>
|
|
83
87
|
{children ??
|
|
@@ -6,7 +6,12 @@ import { CategoryMenuButton } from "@evenicanpm/storefront-core/src/components/n
|
|
|
6
6
|
|
|
7
7
|
import { Paragraph } from "@evenicanpm/storefront-core/src/components/Typography";
|
|
8
8
|
import { useTranslations } from "next-intl";
|
|
9
|
-
import React, {
|
|
9
|
+
import React, {
|
|
10
|
+
createContext,
|
|
11
|
+
type ReactNode,
|
|
12
|
+
useContext,
|
|
13
|
+
useMemo,
|
|
14
|
+
} from "react";
|
|
10
15
|
import { MdChevronRight as ChevronRight } from "react-icons/md";
|
|
11
16
|
|
|
12
17
|
type CategoriesContextValue = {
|
|
@@ -23,9 +28,10 @@ export default function Categories({
|
|
|
23
28
|
const t = useTranslations("Navigation");
|
|
24
29
|
|
|
25
30
|
const label = t("topLevelCategoryName");
|
|
31
|
+
const contextValue = useMemo(() => ({ label }), [label]);
|
|
26
32
|
|
|
27
33
|
return (
|
|
28
|
-
<CategoriesContext.Provider value={
|
|
34
|
+
<CategoriesContext.Provider value={contextValue}>
|
|
29
35
|
<CategoryMenu
|
|
30
36
|
render={(handler) =>
|
|
31
37
|
React.isValidElement(children) ? (
|
|
@@ -23,7 +23,7 @@ import Avatar from "@mui/material/Avatar";
|
|
|
23
23
|
import Box from "@mui/material/Box";
|
|
24
24
|
import Link from "next/link";
|
|
25
25
|
import type React from "react";
|
|
26
|
-
import { createContext, useContext, useState } from "react";
|
|
26
|
+
import { createContext, useContext, useMemo, useState } from "react";
|
|
27
27
|
|
|
28
28
|
type MenuContextValue = {
|
|
29
29
|
openList: string;
|
|
@@ -48,9 +48,13 @@ type RootProps = {
|
|
|
48
48
|
|
|
49
49
|
const CategoryBasedMenu = ({ title, menuList, children }: RootProps) => {
|
|
50
50
|
const [openList, setOpenList] = useState(menuList[0]?.title || "");
|
|
51
|
+
const contextValue = useMemo(
|
|
52
|
+
() => ({ openList, setOpenList, menuList }),
|
|
53
|
+
[openList, menuList],
|
|
54
|
+
);
|
|
51
55
|
|
|
52
56
|
return (
|
|
53
|
-
<MenuContext.Provider value={
|
|
57
|
+
<MenuContext.Provider value={contextValue}>
|
|
54
58
|
<Wrapper>
|
|
55
59
|
<CategoryBasedMenu.Trigger>{title}</CategoryBasedMenu.Trigger>
|
|
56
60
|
<MenusContainer className="menu-list">
|
|
@@ -17,7 +17,7 @@ import Card from "@mui/material/Card";
|
|
|
17
17
|
import Grid from "@mui/material/Grid2";
|
|
18
18
|
import List from "@mui/material/List";
|
|
19
19
|
import type React from "react";
|
|
20
|
-
import { createContext, type RefObject, useContext } from "react";
|
|
20
|
+
import { createContext, type RefObject, useContext, useMemo } from "react";
|
|
21
21
|
|
|
22
22
|
type MegaMenuContextType = {
|
|
23
23
|
isLeftOverflowing: boolean;
|
|
@@ -51,16 +51,18 @@ const MegaMenu = ({
|
|
|
51
51
|
}) => {
|
|
52
52
|
const { elementRef, isLeftOverflowing, isRightOverflowing, checkOverflow } =
|
|
53
53
|
useOverflowDetect();
|
|
54
|
+
const contextValue = useMemo(
|
|
55
|
+
() => ({
|
|
56
|
+
elementRef: elementRef as RefObject<HTMLLIElement>,
|
|
57
|
+
isLeftOverflowing,
|
|
58
|
+
isRightOverflowing,
|
|
59
|
+
checkOverflow,
|
|
60
|
+
}),
|
|
61
|
+
[elementRef, isLeftOverflowing, isRightOverflowing, checkOverflow],
|
|
62
|
+
);
|
|
54
63
|
|
|
55
64
|
return (
|
|
56
|
-
<MegaMenuContext.Provider
|
|
57
|
-
value={{
|
|
58
|
-
elementRef: elementRef as RefObject<HTMLLIElement>,
|
|
59
|
-
isLeftOverflowing,
|
|
60
|
-
isRightOverflowing,
|
|
61
|
-
checkOverflow,
|
|
62
|
-
}}
|
|
63
|
-
>
|
|
65
|
+
<MegaMenuContext.Provider value={contextValue}>
|
|
64
66
|
<Wrapper onMouseOver={checkOverflow}>
|
|
65
67
|
{title && (
|
|
66
68
|
<FlexRowCenter fontWeight={600} alignItems="flex-end" gap={0.3}>
|
|
@@ -74,7 +76,7 @@ const MegaMenu = ({
|
|
|
74
76
|
);
|
|
75
77
|
};
|
|
76
78
|
|
|
77
|
-
|
|
79
|
+
const Trigger = ({ children }: { children?: React.ReactNode }) => {
|
|
78
80
|
return (
|
|
79
81
|
<FlexRowCenter fontWeight={600} alignItems="flex-end" gap={0.3}>
|
|
80
82
|
{children}
|
|
@@ -83,7 +85,7 @@ MegaMenu.Trigger = ({ children }: { children?: React.ReactNode }) => {
|
|
|
83
85
|
);
|
|
84
86
|
};
|
|
85
87
|
|
|
86
|
-
|
|
88
|
+
const Container = ({ children }: { children: React.ReactNode }) => {
|
|
87
89
|
const { elementRef, isLeftOverflowing, isRightOverflowing } = useMegaMenu();
|
|
88
90
|
return (
|
|
89
91
|
<MenusContainer
|
|
@@ -99,7 +101,7 @@ MegaMenu.Container = ({ children }: { children: React.ReactNode }) => {
|
|
|
99
101
|
);
|
|
100
102
|
};
|
|
101
103
|
|
|
102
|
-
|
|
104
|
+
const Column = ({
|
|
103
105
|
children,
|
|
104
106
|
count,
|
|
105
107
|
}: {
|
|
@@ -116,7 +118,7 @@ MegaMenu.Column = ({
|
|
|
116
118
|
);
|
|
117
119
|
};
|
|
118
120
|
|
|
119
|
-
|
|
121
|
+
const Category = ({
|
|
120
122
|
title,
|
|
121
123
|
children,
|
|
122
124
|
}: {
|
|
@@ -133,7 +135,7 @@ MegaMenu.Category = ({
|
|
|
133
135
|
);
|
|
134
136
|
};
|
|
135
137
|
|
|
136
|
-
|
|
138
|
+
const Item = ({
|
|
137
139
|
href,
|
|
138
140
|
children,
|
|
139
141
|
}: {
|
|
@@ -147,4 +149,10 @@ MegaMenu.Item = ({
|
|
|
147
149
|
);
|
|
148
150
|
};
|
|
149
151
|
|
|
152
|
+
MegaMenu.Trigger = Trigger;
|
|
153
|
+
MegaMenu.Container = Container;
|
|
154
|
+
MegaMenu.Column = Column;
|
|
155
|
+
MegaMenu.Category = Category;
|
|
156
|
+
MegaMenu.Item = Item;
|
|
157
|
+
|
|
150
158
|
export default MegaMenu;
|
|
@@ -3,7 +3,7 @@ import MobileMenu from "@evenicanpm/storefront-core/src/components/navbar/mobile
|
|
|
3
3
|
import { transformCmsNav } from "@evenicanpm/storefront-core/src/components/navbar/utils/transform-nav";
|
|
4
4
|
import { fireEvent, render, screen } from "@testing-library/react";
|
|
5
5
|
import type React from "react";
|
|
6
|
-
import { beforeEach, describe, it, vi } from "vitest";
|
|
6
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
7
7
|
|
|
8
8
|
const useContext = vi.fn();
|
|
9
9
|
|
|
@@ -20,7 +20,7 @@ vi.mock("@evenicanpm/storefront-core/src/providers/nav-provider", () => {
|
|
|
20
20
|
};
|
|
21
21
|
});
|
|
22
22
|
describe.skip("MobileMenu", () => {
|
|
23
|
-
const cmsNav = transformCmsNav(mockCmsData
|
|
23
|
+
const cmsNav = transformCmsNav(mockCmsData);
|
|
24
24
|
const handleClose = vi.fn();
|
|
25
25
|
beforeEach(() => {
|
|
26
26
|
vi.clearAllMocks();
|
|
@@ -38,7 +38,7 @@ describe.skip("MobileMenu", () => {
|
|
|
38
38
|
</MobileMenu.Drawer>,
|
|
39
39
|
);
|
|
40
40
|
|
|
41
|
-
expect(screen.getByText("My Page")).
|
|
41
|
+
expect(screen.getByText("My Page")).toBeTruthy();
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
it("should expand nested items when clicked", () => {
|
|
@@ -49,7 +49,7 @@ describe.skip("MobileMenu", () => {
|
|
|
49
49
|
);
|
|
50
50
|
|
|
51
51
|
fireEvent.click(screen.getByText("My Page"));
|
|
52
|
-
expect(screen.getByText("NESTED")).
|
|
52
|
+
expect(screen.getByText("NESTED")).toBeTruthy();
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
it("should call onClose when leaf link is clicked", () => {
|
|
@@ -60,19 +60,17 @@ describe.skip("MobileMenu", () => {
|
|
|
60
60
|
);
|
|
61
61
|
|
|
62
62
|
const link = screen.getByText("NESTED");
|
|
63
|
-
expect(link.closest("a")
|
|
63
|
+
expect(link.closest("a")?.getAttribute("href")).toBe("https://google.com");
|
|
64
64
|
|
|
65
65
|
fireEvent.click(link);
|
|
66
66
|
expect(handleClose).toHaveBeenCalled();
|
|
67
67
|
});
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
const mockCmsData = [
|
|
70
|
+
const mockCmsData: CmsNavItem[] = [
|
|
71
71
|
{
|
|
72
|
-
id: 3,
|
|
73
72
|
title: "My Page",
|
|
74
73
|
menuAttached: false,
|
|
75
|
-
order: 1,
|
|
76
74
|
path: "/pages/my-page",
|
|
77
75
|
type: "INTERNAL",
|
|
78
76
|
uiRouterKey: "my-page",
|
|
@@ -86,20 +84,19 @@ const mockCmsData = [
|
|
|
86
84
|
updatedAt: "2025-01-20T17:27:19.608Z",
|
|
87
85
|
publishedAt: "2025-01-20T17:27:19.603Z",
|
|
88
86
|
__contentType: "api::page.page",
|
|
89
|
-
navigationItemId: 3,
|
|
90
87
|
__templateName: "Generic",
|
|
91
88
|
},
|
|
92
89
|
items: [
|
|
93
90
|
{
|
|
94
|
-
id: 4,
|
|
95
91
|
title: "NESTED",
|
|
96
92
|
menuAttached: false,
|
|
97
|
-
order: 1,
|
|
98
93
|
path: "https://google.com",
|
|
99
94
|
type: "EXTERNAL",
|
|
100
95
|
uiRouterKey: "nested",
|
|
101
96
|
slug: "",
|
|
97
|
+
related: null,
|
|
102
98
|
external: true,
|
|
99
|
+
items: [],
|
|
103
100
|
},
|
|
104
101
|
],
|
|
105
102
|
},
|
|
@@ -24,7 +24,7 @@ const MobileMenu = ({ children }: RootProps) => {
|
|
|
24
24
|
return <>{children}</>;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
const Trigger = () => {
|
|
28
28
|
const { toggleMobileSideNav } = useContext(NavContext);
|
|
29
29
|
return (
|
|
30
30
|
<IconButton
|
|
@@ -36,7 +36,7 @@ MobileMenu.Trigger = () => {
|
|
|
36
36
|
);
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
const MobileMenuDrawer = ({ children }: { children: React.ReactNode }) => {
|
|
40
40
|
const { isMobileSideNavOpen, toggleMobileSideNav } = useContext(NavContext);
|
|
41
41
|
|
|
42
42
|
return (
|
|
@@ -71,12 +71,12 @@ MobileMenu.Drawer = ({ children }: { children: React.ReactNode }) => {
|
|
|
71
71
|
);
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
const MenuList = ({ data }: { data: TransformedNavItem[] }) => {
|
|
75
75
|
const { toggleMobileSideNav } = useContext(NavContext);
|
|
76
76
|
return (
|
|
77
77
|
<>
|
|
78
78
|
{data.map((item, idx) => (
|
|
79
|
-
<
|
|
79
|
+
<MenuItem
|
|
80
80
|
key={item.title + idx}
|
|
81
81
|
item={item}
|
|
82
82
|
onClose={toggleMobileSideNav}
|
|
@@ -102,7 +102,7 @@ const ACCORDION_SUMMARY_STYLES = {
|
|
|
102
102
|
};
|
|
103
103
|
|
|
104
104
|
// Single Item (link or accordion)
|
|
105
|
-
|
|
105
|
+
const MenuItem = ({
|
|
106
106
|
item,
|
|
107
107
|
onClose,
|
|
108
108
|
}: {
|
|
@@ -122,11 +122,7 @@ MobileMenu.Item = ({
|
|
|
122
122
|
</AccordionSummary>
|
|
123
123
|
<Box mx={2}>
|
|
124
124
|
{items.map((sub, idx) => (
|
|
125
|
-
<
|
|
126
|
-
key={sub.title + idx}
|
|
127
|
-
item={sub}
|
|
128
|
-
onClose={onClose}
|
|
129
|
-
/>
|
|
125
|
+
<MenuItem key={sub.title + idx} item={sub} onClose={onClose} />
|
|
130
126
|
))}
|
|
131
127
|
</Box>
|
|
132
128
|
</Accordion>
|
|
@@ -142,8 +138,14 @@ MobileMenu.Item = ({
|
|
|
142
138
|
);
|
|
143
139
|
};
|
|
144
140
|
|
|
145
|
-
|
|
141
|
+
const MenuDivider = () => {
|
|
146
142
|
return <Divider sx={{ my: 2 }} />;
|
|
147
143
|
};
|
|
148
144
|
|
|
145
|
+
MobileMenu.Trigger = Trigger;
|
|
146
|
+
MobileMenu.Drawer = MobileMenuDrawer;
|
|
147
|
+
MobileMenu.List = MenuList;
|
|
148
|
+
MobileMenu.Item = MenuItem;
|
|
149
|
+
MobileMenu.Divider = MenuDivider;
|
|
150
|
+
|
|
149
151
|
export default MobileMenu;
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
import { NavContext } from "@evenicanpm/storefront-core/src/providers/nav-provider";
|
|
19
19
|
import KeyboardArrowDown from "@mui/icons-material/KeyboardArrowDown";
|
|
20
20
|
import MenuItem from "@mui/material/MenuItem";
|
|
21
|
-
import { createContext, type ReactNode, useContext } from "react";
|
|
21
|
+
import { createContext, type ReactNode, useContext, useMemo } from "react";
|
|
22
22
|
|
|
23
23
|
type NavigationContextValue = {
|
|
24
24
|
list: TransformedNavItem[];
|
|
@@ -118,9 +118,10 @@ export default function Navigation({
|
|
|
118
118
|
children,
|
|
119
119
|
}: Readonly<{ children?: ReactNode }>) {
|
|
120
120
|
const cmsList = transformCmsNav(useContext(NavContext).cmsNav ?? []);
|
|
121
|
+
const contextValue = useMemo(() => ({ list: cmsList }), [cmsList]);
|
|
121
122
|
|
|
122
123
|
return (
|
|
123
|
-
<NavigationContext.Provider value={
|
|
124
|
+
<NavigationContext.Provider value={contextValue}>
|
|
124
125
|
<FlexBox gap={4}>{children ?? <Navigation.List />}</FlexBox>
|
|
125
126
|
</NavigationContext.Provider>
|
|
126
127
|
);
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
NavBarWrapper,
|
|
10
10
|
} from "@evenicanpm/storefront-core/src/components/navbar/styles";
|
|
11
11
|
import type React from "react";
|
|
12
|
-
import { createContext, useContext } from "react";
|
|
12
|
+
import { createContext, useContext, useMemo } from "react";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Usage Examples:
|
|
@@ -57,8 +57,10 @@ export default function Navbar({
|
|
|
57
57
|
hideCategories = false,
|
|
58
58
|
children,
|
|
59
59
|
}: Readonly<Props>) {
|
|
60
|
+
const contextValue = useMemo(() => ({ hideCategories }), [hideCategories]);
|
|
61
|
+
|
|
60
62
|
return (
|
|
61
|
-
<NavbarContext.Provider value={
|
|
63
|
+
<NavbarContext.Provider value={contextValue}>
|
|
62
64
|
<NavBarWrapper hoverEffect={false} elevation={elevation} border={border}>
|
|
63
65
|
{children ?? (
|
|
64
66
|
<Navbar.Content>
|
|
@@ -247,21 +247,35 @@ const ProductCard = ({
|
|
|
247
247
|
}
|
|
248
248
|
};
|
|
249
249
|
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
250
|
+
const productContextValue = useMemo(() => ({ product }), [product]);
|
|
251
|
+
const memoizedInternalContextValue = useMemo(
|
|
252
|
+
() => ({
|
|
253
|
+
handleToggleFavorite,
|
|
254
|
+
openProductModal,
|
|
255
|
+
handleToggleProductModal,
|
|
256
|
+
quantity,
|
|
257
|
+
handleIncrementQuantity,
|
|
258
|
+
handleDecrementQuantity,
|
|
259
|
+
isLineUpdating,
|
|
260
|
+
handleAddItemToCart,
|
|
261
|
+
handleRemoveFromWishlist,
|
|
262
|
+
}),
|
|
263
|
+
[
|
|
264
|
+
handleToggleFavorite,
|
|
265
|
+
openProductModal,
|
|
266
|
+
handleToggleProductModal,
|
|
267
|
+
quantity,
|
|
268
|
+
handleIncrementQuantity,
|
|
269
|
+
handleDecrementQuantity,
|
|
270
|
+
isLineUpdating,
|
|
271
|
+
handleAddItemToCart,
|
|
272
|
+
handleRemoveFromWishlist,
|
|
273
|
+
],
|
|
274
|
+
);
|
|
261
275
|
|
|
262
276
|
return (
|
|
263
|
-
<ProductCardContext.Provider value={
|
|
264
|
-
<ProductCardInternalContext.Provider value={
|
|
277
|
+
<ProductCardContext.Provider value={productContextValue}>
|
|
278
|
+
<ProductCardInternalContext.Provider value={memoizedInternalContextValue}>
|
|
265
279
|
<StyledBazaarCard hoverEffect={product.hoverEffect}>
|
|
266
280
|
{children}
|
|
267
281
|
</StyledBazaarCard>
|
|
@@ -285,7 +299,7 @@ const ProductCard = ({
|
|
|
285
299
|
);
|
|
286
300
|
};
|
|
287
301
|
|
|
288
|
-
|
|
302
|
+
const CardImage = () => {
|
|
289
303
|
const ctx = useContext(ProductCardContext);
|
|
290
304
|
if (!ctx) return null;
|
|
291
305
|
|
|
@@ -301,11 +315,11 @@ ProductCard.Image = () => {
|
|
|
301
315
|
);
|
|
302
316
|
};
|
|
303
317
|
|
|
304
|
-
|
|
318
|
+
const CardBody = ({ children }: { children: React.ReactNode }) => {
|
|
305
319
|
return <ContentWrapper>{children}</ContentWrapper>;
|
|
306
320
|
};
|
|
307
321
|
|
|
308
|
-
|
|
322
|
+
const CardInfo = ({ children }: { children: React.ReactNode }) => {
|
|
309
323
|
return (
|
|
310
324
|
<Box flex="1 1 0" minWidth="0px" mr={1}>
|
|
311
325
|
{children}
|
|
@@ -313,11 +327,11 @@ ProductCard.Info = ({ children }: { children: React.ReactNode }) => {
|
|
|
313
327
|
);
|
|
314
328
|
};
|
|
315
329
|
|
|
316
|
-
|
|
330
|
+
const CardActions = ({ children }: { children: React.ReactNode }) => {
|
|
317
331
|
return <>{children}</>;
|
|
318
332
|
};
|
|
319
333
|
|
|
320
|
-
|
|
334
|
+
const CardTitle = () => {
|
|
321
335
|
const ctx = useContext(ProductCardContext);
|
|
322
336
|
if (!ctx) return null;
|
|
323
337
|
|
|
@@ -325,7 +339,7 @@ ProductCard.Title = () => {
|
|
|
325
339
|
return <ProductTitle title={title} slug={slug} />;
|
|
326
340
|
};
|
|
327
341
|
|
|
328
|
-
|
|
342
|
+
const CardRating = () => {
|
|
329
343
|
const ctx = useContext(ProductCardContext);
|
|
330
344
|
if (!ctx) return null;
|
|
331
345
|
|
|
@@ -334,7 +348,7 @@ ProductCard.Rating = () => {
|
|
|
334
348
|
return <Rating size="small" value={rating} color="warn" readOnly />;
|
|
335
349
|
};
|
|
336
350
|
|
|
337
|
-
|
|
351
|
+
const CardSize = () => {
|
|
338
352
|
const ctx = useContext(ProductCardContext);
|
|
339
353
|
if (!ctx) return null;
|
|
340
354
|
|
|
@@ -347,7 +361,7 @@ ProductCard.Size = () => {
|
|
|
347
361
|
);
|
|
348
362
|
};
|
|
349
363
|
|
|
350
|
-
|
|
364
|
+
const CardPrice = () => {
|
|
351
365
|
const ctx = useContext(ProductCardContext);
|
|
352
366
|
if (!ctx) return null;
|
|
353
367
|
|
|
@@ -377,15 +391,13 @@ const QuantityButtons = () => {
|
|
|
377
391
|
);
|
|
378
392
|
};
|
|
379
393
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
ProductCard.HoverActions = ({ children }: { children: React.ReactNode }) => {
|
|
394
|
+
const CardHoverActions = ({ children }: { children: React.ReactNode }) => {
|
|
383
395
|
return (
|
|
384
396
|
<HoverIconWrapper className="hover-box"> {children} </HoverIconWrapper>
|
|
385
397
|
);
|
|
386
398
|
};
|
|
387
399
|
|
|
388
|
-
|
|
400
|
+
const CardQuickViewButton = () => {
|
|
389
401
|
const internal = useContext(ProductCardInternalContext);
|
|
390
402
|
if (!internal) return null;
|
|
391
403
|
|
|
@@ -398,7 +410,7 @@ ProductCard.QuickViewButton = () => {
|
|
|
398
410
|
);
|
|
399
411
|
};
|
|
400
412
|
|
|
401
|
-
|
|
413
|
+
const CardFavoriteButton = () => {
|
|
402
414
|
const internal = useContext(ProductCardInternalContext);
|
|
403
415
|
const ctx = useContext(ProductCardContext);
|
|
404
416
|
|
|
@@ -450,6 +462,18 @@ const WishlistControls = () => {
|
|
|
450
462
|
);
|
|
451
463
|
};
|
|
452
464
|
|
|
465
|
+
ProductCard.Image = CardImage;
|
|
466
|
+
ProductCard.Body = CardBody;
|
|
467
|
+
ProductCard.Info = CardInfo;
|
|
468
|
+
ProductCard.Actions = CardActions;
|
|
469
|
+
ProductCard.Title = CardTitle;
|
|
470
|
+
ProductCard.Rating = CardRating;
|
|
471
|
+
ProductCard.Size = CardSize;
|
|
472
|
+
ProductCard.Price = CardPrice;
|
|
473
|
+
ProductCard.QuantityButtons = QuantityButtons;
|
|
474
|
+
ProductCard.HoverActions = CardHoverActions;
|
|
475
|
+
ProductCard.QuickViewButton = CardQuickViewButton;
|
|
476
|
+
ProductCard.FavoriteButton = CardFavoriteButton;
|
|
453
477
|
ProductCard.WishlistControls = WishlistControls;
|
|
454
478
|
|
|
455
479
|
export default ProductCard;
|