@evenicanpm/storefront-core 1.3.0 → 1.6.0

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 (188) hide show
  1. package/__mocks__/get-product-by-id.ts +0 -1
  2. package/__mocks__/query-client.ts +1 -1
  3. package/package.json +3 -3
  4. package/src/api-manager/datasources/d365/d365-address.datasource.ts +2 -2
  5. package/src/api-manager/datasources/d365/d365-cart.datasource.ts +4 -5
  6. package/src/api-manager/datasources/d365/d365-categories.datasource.ts +1 -1
  7. package/src/api-manager/datasources/d365/d365-product.datasource.ts +21 -7
  8. package/src/api-manager/datasources/d365/d365-session.datasource.ts +5 -7
  9. package/src/api-manager/datasources/d365/d365-user.datasource.ts +12 -23
  10. package/src/api-manager/datasources/d365/d365.datasource.ts +0 -2
  11. package/src/api-manager/datasources/d365/index.ts +2 -4
  12. package/src/api-manager/datasources/d365/utils/decode-jwt.ts +1 -2
  13. package/src/api-manager/datasources/d365/utils/parse-object.ts +1 -1
  14. package/src/api-manager/datasources/e4/address/e4-address.remaps.ts +2 -2
  15. package/src/api-manager/datasources/e4/address/e4-address.translator.ts +1 -1
  16. package/src/api-manager/datasources/e4/cart/e4-cart.datasource.ts +28 -27
  17. package/src/api-manager/datasources/e4/cart/e4-cart.remaps.ts +1 -1
  18. package/src/api-manager/datasources/e4/cart/e4-cart.translator.ts +1 -1
  19. package/src/api-manager/datasources/e4/categories/e4-categories.translator.ts +20 -20
  20. package/src/api-manager/datasources/e4/graphqlRequestSdk.ts +471 -7
  21. package/src/api-manager/datasources/e4/order/e4-order.remaps.ts +2 -2
  22. package/src/api-manager/datasources/e4/organization/e4-organization.datasource.ts +2 -8
  23. package/src/api-manager/datasources/e4/product/e4-product.remaps.ts +3 -3
  24. package/src/api-manager/datasources/e4/product/e4-product.translator.ts +13 -29
  25. package/src/api-manager/datasources/e4/session/e4-session.datasource.ts +1 -3
  26. package/src/api-manager/datasources/e4/user/e4-user.remaps.ts +2 -4
  27. package/src/api-manager/datasources/e4/utils/unwrap-e4-variants.ts +4 -4
  28. package/src/api-manager/index.ts +1 -1
  29. package/src/api-manager/lib/category-helpers.ts +3 -2
  30. package/src/api-manager/lib/get-graphql-client.ts +1 -2
  31. package/src/api-manager/services/cart/mutations/add-discount-code.ts +2 -2
  32. package/src/api-manager/services/cart/mutations/add-to-cart.ts +2 -2
  33. package/src/api-manager/services/product/server/product.server.ts +1 -1
  34. package/src/api-manager/types/Datasource.ts +4 -9
  35. package/src/auth/auth-options.test.ts +2 -58
  36. package/src/auth/auth-options.ts +1 -2
  37. package/src/auth/next-auth-cookie-manager.ts +0 -2
  38. package/src/auth/refresh-token.ts +3 -8
  39. package/src/auth/signout.ts +1 -1
  40. package/src/cms/blocks/block-manager.tsx +3 -3
  41. package/src/cms/blocks/components/category-card.tsx +1 -1
  42. package/src/cms/blocks/components/category-carousel/index.tsx +3 -1
  43. package/src/cms/blocks/components/footer/index.tsx +2 -2
  44. package/src/cms/blocks/components/footer/sections/footer-links.tsx +2 -2
  45. package/src/cms/blocks/components/footer/sections/footer-logo.tsx +1 -1
  46. package/src/cms/blocks/components/footer/sections/footer-social-links.tsx +10 -2
  47. package/src/cms/blocks/components/hero-carousel/index.tsx +29 -25
  48. package/src/cms/blocks/components/media-card.tsx +1 -1
  49. package/src/cms/blocks/components/product-carousel/index.tsx +1 -1
  50. package/src/cms/blocks/components/product-section-fullwidth/index.tsx +2 -2
  51. package/src/cms/blocks/components/seo/index.tsx +5 -1
  52. package/src/cms/blocks/components/services/index.tsx +1 -1
  53. package/src/cms/blocks/components/services/service-card.tsx +1 -1
  54. package/src/cms/blocks/components/shared/featured-product-card.tsx +4 -1
  55. package/src/cms/blocks/components/shared/product-category-item.tsx +1 -1
  56. package/src/cms/blocks/components/shared/product-grid.tsx +4 -1
  57. package/src/cms/blocks/components/shared/top-categories-card.tsx +5 -1
  58. package/src/cms/blocks/components/shared/top-rating-product-card.tsx +1 -1
  59. package/src/components-v2/BazaarImage.tsx +1 -1
  60. package/src/components-v2/BazaarMenu.tsx +2 -2
  61. package/src/components-v2/BazaarTextField.tsx +3 -3
  62. package/src/components-v2/BorderShadowCard.stories.tsx +1 -1
  63. package/src/components-v2/DropZone.tsx +1 -2
  64. package/src/components-v2/Typography.tsx +12 -12
  65. package/src/components-v2/accordion/accordion-header.tsx +1 -1
  66. package/src/components-v2/categories/category-list/styles.ts +0 -7
  67. package/src/components-v2/categories/category-list-item/category-menu-item.tsx +1 -1
  68. package/src/components-v2/categories/category-menu.tsx +16 -13
  69. package/src/components-v2/categories/mega-menu/mega-menu-1.tsx +1 -1
  70. package/src/components-v2/categories/mega-menu/mega-menu-2.tsx +2 -2
  71. package/src/components-v2/header/components/user.tsx +5 -5
  72. package/src/components-v2/mini-cart/__tests__/cart-item.test.tsx +13 -13
  73. package/src/components-v2/mini-cart/components/bottom-actions.tsx +1 -1
  74. package/src/components-v2/mini-cart/components/cart-item.tsx +3 -3
  75. package/src/components-v2/mini-cart/components/top-header.tsx +2 -2
  76. package/src/components-v2/mobile-navigation/mobile-navigation-bar.tsx +1 -1
  77. package/src/components-v2/nav-link/nav-link-2.tsx +1 -1
  78. package/src/components-v2/nav-link/nav-link-3.tsx +1 -1
  79. package/src/components-v2/nav-link/nav-link.tsx +1 -1
  80. package/src/components-v2/navbar/categories.tsx +3 -1
  81. package/src/components-v2/navbar/category-based-menu/category-based-menu.tsx +2 -2
  82. package/src/components-v2/navbar/mobile-menu/mobile-menu.tsx +11 -3
  83. package/src/components-v2/navbar/nav-list/nav-item-child.tsx +1 -1
  84. package/src/components-v2/navbar/nav-list/nav-list.tsx +118 -80
  85. package/src/components-v2/navbar/navbar.tsx +1 -1
  86. package/src/components-v2/no-records.tsx +1 -1
  87. package/src/components-v2/product-cards/favorite-button.tsx +1 -1
  88. package/src/components-v2/product-cards/product-card/product-card.tsx +5 -7
  89. package/src/components-v2/product-cards/product-card-plp/product-card.tsx +7 -6
  90. package/src/components-v2/product-cards/product-card-plp-list/components/tags.tsx +1 -1
  91. package/src/components-v2/product-cards/product-card-plp-list/product-card.tsx +7 -4
  92. package/src/components-v2/product-cards/product-card-search/product-card.tsx +4 -2
  93. package/src/components-v2/product-cards/product-price.tsx +1 -1
  94. package/src/components-v2/product-cards/product-rating.tsx +1 -1
  95. package/src/components-v2/product-cards/product-title.tsx +1 -1
  96. package/src/components-v2/product-cards/quantity-buttons.tsx +1 -1
  97. package/src/components-v2/product-dialog/compound/product-dialog.tsx +2 -2
  98. package/src/components-v2/product-dialog/index.tsx +1 -1
  99. package/src/components-v2/product-dimensions/compound/dimensions-group-list.tsx +6 -1
  100. package/src/components-v2/product-dimensions/index.tsx +3 -3
  101. package/src/components-v2/product-quantity-buttons/compound/quantity-buttons-root.tsx +1 -1
  102. package/src/components-v2/product-quantity-variants/index.tsx +1 -2
  103. package/src/components-v2/products-view/ProductsList.stories.tsx +0 -2
  104. package/src/components-v2/products-view/compound/products-grid-view.tsx +2 -2
  105. package/src/components-v2/products-view/compound/products-list-view.tsx +3 -3
  106. package/src/components-v2/products-view/index.tsx +2 -2
  107. package/src/components-v2/progress/progress.tsx +1 -1
  108. package/src/components-v2/scrollbar/scrollbar.tsx +1 -1
  109. package/src/components-v2/search-bar/compound/results-suspense.tsx +1 -2
  110. package/src/components-v2/search-bar/compound/results.tsx +6 -3
  111. package/src/components-v2/search-bar/compound/search-bar-root.tsx +4 -2
  112. package/src/components-v2/search-bar/compound/textfield-adornment-category.tsx +25 -27
  113. package/src/components-v2/search-bar/compound/textfield.tsx +0 -1
  114. package/src/components-v2/search-bar/hooks/use-search.ts +1 -1
  115. package/src/components-v2/section/Section.stories.tsx +1 -1
  116. package/src/components-v2/side-nav/side-nav.tsx +1 -1
  117. package/src/components-v2/wishlist-dialogs/add-to-wishlist/compound/add-to-wishlist-dialog.tsx +1 -1
  118. package/src/components-v2/wishlist-dialogs/add-to-wishlist/compound/add-to-wishlist-list.tsx +2 -2
  119. package/src/components-v2/wishlist-dialogs/add-to-wishlist/hooks/use-add-to-wishlist.ts +2 -0
  120. package/src/components-v2/wishlist-dialogs/add-to-wishlist/index.tsx +11 -13
  121. package/src/components-v2/wishlist-dialogs/create-wishlist/compound/create-wishlist-dialog.tsx +2 -1
  122. package/src/hooks/use-variants.ts +25 -27
  123. package/src/lib/build-detail-url.ts +1 -1
  124. package/src/lib/category-helpers.ts +3 -3
  125. package/src/lib/get-palette-color.ts +35 -15
  126. package/src/lib/hexToLuma.ts +2 -2
  127. package/src/lib/store-hours-helper.ts +5 -5
  128. package/src/pages-v2/account/addresses/address-form.tsx +10 -8
  129. package/src/pages-v2/account/addresses/address-item.tsx +1 -1
  130. package/src/pages-v2/account/orders/order-progress.tsx +1 -1
  131. package/src/pages-v2/account/orders/ordered-products.tsx +1 -1
  132. package/src/pages-v2/account/profile/user-info.tsx +1 -2
  133. package/src/pages-v2/account/wishlist/create-new-list.tsx +1 -0
  134. package/src/pages-v2/account/wishlist/wishlist-item.tsx +2 -1
  135. package/src/pages-v2/blog/blog-card.tsx +1 -1
  136. package/src/pages-v2/cart/__tests__/cart-item.test.tsx +0 -1
  137. package/src/pages-v2/cart/__tests__/checkout-form.test.tsx +0 -8
  138. package/src/pages-v2/cart/cart-item.tsx +86 -77
  139. package/src/pages-v2/cart/checkout-form.tsx +4 -4
  140. package/src/pages-v2/cart/coupon-entry.tsx +3 -3
  141. package/src/pages-v2/cart/estimate-shipping.tsx +6 -6
  142. package/src/pages-v2/checkout/checkout-alt-form/checkout-form.tsx +2 -2
  143. package/src/pages-v2/checkout/checkout-alt-form/checkout-step.tsx +2 -2
  144. package/src/pages-v2/checkout/checkout-alt-form/step-heading.tsx +1 -1
  145. package/src/pages-v2/checkout/checkout-alt-form/steps/address/delivery-address.tsx +12 -10
  146. package/src/pages-v2/checkout/checkout-alt-form/steps/address/new-address-form.tsx +5 -6
  147. package/src/pages-v2/checkout/checkout-alt-form/steps/customer-info/customer-information.tsx +8 -9
  148. package/src/pages-v2/checkout/checkout-alt-form/steps/payment/payment-details.tsx +17 -20
  149. package/src/pages-v2/checkout/checkout-alt-form/steps/shipping/delivery-date.tsx +1 -1
  150. package/src/pages-v2/checkout/checkout-alt-form/steps/shipping/delivery-options.tsx +14 -15
  151. package/src/pages-v2/confirmation/confirmation-summary.tsx +8 -7
  152. package/src/pages-v2/confirmation/ordered-products.tsx +3 -3
  153. package/src/pages-v2/product-details/bopis/pickup-option-select.tsx +1 -1
  154. package/src/pages-v2/product-details/bopis/store-card/shop-card.tsx +1 -1
  155. package/src/pages-v2/product-details/checkbox-label.tsx +1 -1
  156. package/src/pages-v2/product-details/frequently-bought.tsx +1 -1
  157. package/src/pages-v2/product-details/frequently-product-card.tsx +1 -1
  158. package/src/pages-v2/product-details/product-comment.tsx +1 -1
  159. package/src/pages-v2/product-details/product-description.tsx +2 -2
  160. package/src/pages-v2/product-details/product-filter-card.tsx +4 -1
  161. package/src/pages-v2/product-details/product-intro/compound/product-intro-images.tsx +36 -31
  162. package/src/pages-v2/product-details/product-intro/compound/product-intro.tsx +1 -1
  163. package/src/pages-v2/product-details/product-intro/compound/thumbnail-with-skeleton.tsx +1 -1
  164. package/src/pages-v2/product-details/product-intro/utils.ts +2 -4
  165. package/src/pages-v2/product-details/product-review.tsx +1 -1
  166. package/src/pages-v2/product-details/product-tabs.tsx +5 -2
  167. package/src/pages-v2/product-details/related-products.tsx +7 -4
  168. package/src/pages-v2/product-list/breadcrumbs.tsx +1 -1
  169. package/src/pages-v2/product-list/checkbox-label.tsx +1 -1
  170. package/src/pages-v2/product-list/facet-group.tsx +1 -1
  171. package/src/pages-v2/product-list/list-filter.tsx +2 -2
  172. package/src/pages-v2/product-list/product-list-context.tsx +5 -1
  173. package/src/pages-v2/product-list/product-list-view.tsx +4 -1
  174. package/src/pages-v2/product-list/product-rating.tsx +4 -1
  175. package/src/pages-v2/product-list/quick-view-dialog-content.tsx +2 -4
  176. package/src/pages-v2/product-list/quick-view-dialog.tsx +5 -1
  177. package/src/pages-v2/product-list/range-filter.tsx +0 -1
  178. package/src/pages-v2/product-list/scrollbar.tsx +1 -1
  179. package/src/pages-v2/product-list/side-nav.tsx +1 -1
  180. package/src/pages-v2/product-list/swatch.tsx +12 -7
  181. package/src/pages-v2/product-list/utils/getCategoryFilterFromSlug.ts +33 -68
  182. package/src/pages-v2/product-list/utils/product-list-helper.ts +1 -1
  183. package/src/pages-v2/product-list/utils/search-for-category.ts +2 -2
  184. package/src/pages-v2/quickorder/order-upload.tsx +90 -76
  185. package/src/pages-v2/quickorder/provider.tsx +6 -4
  186. package/src/pages-v2/quickorder/quick-order-row.tsx +1 -1
  187. package/src/pages-v2/quickorder/quick-order.tsx +28 -21
  188. package/src/providers/notifications/notification-context.tsx +0 -1
@@ -62,7 +62,7 @@ const MobileNavContext = createContext<{ menuList: MenuItemProps[] }>({
62
62
  export default function MobileNavigationBar({
63
63
  children,
64
64
  menuList,
65
- }: MobileNavigationBarProps) {
65
+ }: Readonly<MobileNavigationBarProps>) {
66
66
  const DOWN_900 = useMediaQuery((theme: Theme) => theme.breakpoints.down(900));
67
67
 
68
68
  const defaultMenuList: MenuItemProps[] = [
@@ -16,7 +16,7 @@ export default function NavLink2({
16
16
  color,
17
17
  title = "SHOP NOW",
18
18
  borderColor = "primary.600",
19
- }: NavLinkProps) {
19
+ }: Readonly<NavLinkProps>) {
20
20
  return (
21
21
  <Link href={url}>
22
22
  <Small
@@ -21,7 +21,7 @@ export default function NavLink3({
21
21
  color = "text.primary",
22
22
  hoverColor = "primary.main",
23
23
  ...props
24
- }: Props) {
24
+ }: Readonly<Props>) {
25
25
  return (
26
26
  <Link href={href}>
27
27
  <Span
@@ -30,7 +30,7 @@ export default function NavLink({
30
30
  style,
31
31
  className,
32
32
  ...props
33
- }: NavLinkProps) {
33
+ }: Readonly<NavLinkProps>) {
34
34
  const pathname = usePathname();
35
35
 
36
36
  // CHECK CURRENT ROUTE
@@ -17,7 +17,9 @@ const CategoriesContext = createContext<CategoriesContextValue | undefined>(
17
17
  undefined,
18
18
  );
19
19
 
20
- export default function Categories({ children }: { children?: ReactNode }) {
20
+ export default function Categories({
21
+ children,
22
+ }: Readonly<{ children?: ReactNode }>) {
21
23
  const t = useTranslations("Navigation");
22
24
 
23
25
  const label = t("topLevelCategoryName");
@@ -107,13 +107,13 @@ CategoryBasedMenu.Section = () => {
107
107
  <Scrollbar autoHide={false} sx={{ width: "100%" }}>
108
108
  <Box px={6} py={2} height="100%">
109
109
  {category.items.map((item, idx) => (
110
- <div key={idx}>
110
+ <div key={item.title + idx}>
111
111
  <H6 fontWeight={700} my={3}>
112
112
  {item.title}
113
113
  </H6>
114
114
  <SubCategoryList>
115
115
  {item.items.map((sub, subIdx) => (
116
- <CategoryBasedMenu.Item item={sub} key={subIdx} />
116
+ <CategoryBasedMenu.Item item={sub} key={sub.title + subIdx} />
117
117
  ))}
118
118
  </SubCategoryList>
119
119
  </div>
@@ -76,7 +76,11 @@ MobileMenu.List = ({ data }: { data: TransformedNavItem[] }) => {
76
76
  return (
77
77
  <>
78
78
  {data.map((item, idx) => (
79
- <MobileMenu.Item key={idx} item={item} onClose={toggleMobileSideNav} />
79
+ <MobileMenu.Item
80
+ key={item.title + idx}
81
+ item={item}
82
+ onClose={toggleMobileSideNav}
83
+ />
80
84
  ))}
81
85
  </>
82
86
  );
@@ -107,7 +111,7 @@ MobileMenu.Item = ({
107
111
  }) => {
108
112
  const { href, title, items } = item;
109
113
 
110
- if (items && items.length) {
114
+ if (items?.length) {
111
115
  return (
112
116
  <Accordion square elevation={0} disableGutters sx={ACCORDION_STYLES}>
113
117
  <AccordionSummary
@@ -118,7 +122,11 @@ MobileMenu.Item = ({
118
122
  </AccordionSummary>
119
123
  <Box mx={2}>
120
124
  {items.map((sub, idx) => (
121
- <MobileMenu.Item key={idx} item={sub} onClose={onClose} />
125
+ <MobileMenu.Item
126
+ key={sub.title + idx}
127
+ item={sub}
128
+ onClose={onClose}
129
+ />
122
130
  ))}
123
131
  </Box>
124
132
  </Accordion>
@@ -25,7 +25,7 @@ interface Props extends PropsWithChildren {
25
25
  }
26
26
  // ==============================================================
27
27
 
28
- export default function NavItemChild({ nav, children }: Props) {
28
+ export default function NavItemChild({ nav, children }: Readonly<Props>) {
29
29
  const pathname = usePathname();
30
30
  const { settings } = useSettings();
31
31
  const { checkOverflow, elementRef, isLeftOverflowing, isRightOverflowing } =
@@ -17,19 +17,108 @@ import {
17
17
  } from "@evenicanpm/storefront-core/src/components-v2/navbar/utils/transform-nav";
18
18
  import { NavContext } from "@evenicanpm/storefront-core/src/providers/nav-provider";
19
19
  import KeyboardArrowDown from "@mui/icons-material/KeyboardArrowDown";
20
- // import { useContext as useReactContext } from "react";
21
20
  import MenuItem from "@mui/material/MenuItem";
22
21
  import { createContext, type ReactNode, useContext } from "react";
23
22
 
24
23
  type NavigationContextValue = {
25
24
  list: TransformedNavItem[];
26
25
  };
26
+
27
27
  const NavigationContext = createContext<NavigationContextValue | undefined>(
28
28
  undefined,
29
29
  );
30
30
 
31
- export default function Navigation({ children }: { children?: ReactNode }) {
31
+ type NavigationItemProps = Readonly<{
32
+ nav: TransformedNavItem;
33
+ isRoot?: boolean;
34
+ }>;
35
+
36
+ function RenderMegaMenu(props: Readonly<{ nav: TransformedNavItem }>) {
37
+ const { nav } = props;
38
+ const columns = nav.items.map((item) => [item]);
39
+ const columnCount = columns.length;
40
+
41
+ return (
42
+ <MegaMenu title={nav.title}>
43
+ <MegaMenu.Container>
44
+ {columns.map((column) => {
45
+ const columnKey = column.map((c) => c.title).join("-");
46
+
47
+ return (
48
+ <MegaMenu.Column key={columnKey} count={columnCount}>
49
+ {column.map((cat) => (
50
+ <MegaMenu.Category key={cat.title} title={cat.title}>
51
+ {cat.items.map((sub) => (
52
+ <MegaMenu.Item key={sub.title} href={sub.href}>
53
+ {sub.title}
54
+ </MegaMenu.Item>
55
+ ))}
56
+ </MegaMenu.Category>
57
+ ))}
58
+ </MegaMenu.Column>
59
+ );
60
+ })}
61
+ </MegaMenu.Container>
62
+ </MegaMenu>
63
+ );
64
+ }
65
+
66
+ function RenderFullScreenMenu({ nav }: Readonly<{ nav: TransformedNavItem }>) {
67
+ return (
68
+ <CategoryBasedMenu key={nav.title} title={nav.title} menuList={nav.items}>
69
+ <CategoryBasedMenu.MainList />
70
+ <CategoryBasedMenu.Section />
71
+ </CategoryBasedMenu>
72
+ );
73
+ }
74
+
75
+ function RenderRootWithChildren({
76
+ nav,
77
+ }: Readonly<{ nav: TransformedNavItem }>) {
78
+ const { title, items } = nav;
79
+
80
+ return (
81
+ <FlexBox
82
+ alignItems="center"
83
+ position="relative"
84
+ flexDirection="column"
85
+ sx={{
86
+ "&:hover": { "& > .child-nav-item": { display: "block" } },
87
+ }}
88
+ >
89
+ <FlexBox alignItems="flex-end" gap={0.3} sx={NAV_LINK_STYLES}>
90
+ {title}
91
+ <KeyboardArrowDown sx={{ color: "grey.500", fontSize: "1.1rem" }} />
92
+ </FlexBox>
93
+
94
+ <ChildNavListWrapper className="child-nav-item">
95
+ <BazaarCard elevation={3} sx={{ mt: 2.5, py: 1, minWidth: 100 }}>
96
+ {items.map((child) => (
97
+ <Navigation.Item key={child.title} nav={child} />
98
+ ))}
99
+ </BazaarCard>
100
+ </ChildNavListWrapper>
101
+ </FlexBox>
102
+ );
103
+ }
104
+
105
+ function RenderChildWithChildren({
106
+ nav,
107
+ }: Readonly<{ nav: TransformedNavItem }>) {
108
+ return (
109
+ <Navigation.Child nav={nav}>
110
+ {nav.items.map((child) => (
111
+ <Navigation.Item key={child.title} nav={child} />
112
+ ))}
113
+ </Navigation.Child>
114
+ );
115
+ }
116
+
117
+ export default function Navigation({
118
+ children,
119
+ }: Readonly<{ children?: ReactNode }>) {
32
120
  const cmsList = transformCmsNav(useContext(NavContext).cmsNav ?? []);
121
+
33
122
  return (
34
123
  <NavigationContext.Provider value={{ list: cmsList }}>
35
124
  <FlexBox gap={4}>{children ?? <Navigation.List />}</FlexBox>
@@ -40,6 +129,7 @@ export default function Navigation({ children }: { children?: ReactNode }) {
40
129
  Navigation.List = function List() {
41
130
  const ctx = useContext(NavigationContext);
42
131
  if (!ctx) return null;
132
+
43
133
  return (
44
134
  <>
45
135
  {ctx.list.map((nav) => (
@@ -49,96 +139,44 @@ Navigation.List = function List() {
49
139
  );
50
140
  };
51
141
 
52
- Navigation.Item = function Item({
53
- nav,
54
- isRoot = false,
55
- }: {
56
- nav: TransformedNavItem;
57
- isRoot?: boolean;
58
- }) {
142
+ Navigation.Item = function Item({ nav, isRoot = false }: NavigationItemProps) {
59
143
  const { href, title, items, menuType } = nav;
60
- const hasChildren = !!(items && items.length);
144
+ const hasChildren = !!items?.length;
61
145
 
146
+ // ROOT ITEMS
62
147
  if (isRoot) {
63
148
  if (menuType === "mega-menu") {
64
- return (
65
- <MegaMenu title={title}>
66
- <MegaMenu.Container>
67
- {nav.items
68
- .map((i) => [i])
69
- .map((column, i) => (
70
- <MegaMenu.Column
71
- key={i}
72
- count={nav.items.map((i) => [i]).length}
73
- >
74
- {column.map((cat) => (
75
- <MegaMenu.Category key={cat.title} title={cat.title}>
76
- {cat.items.map((sub) => (
77
- <MegaMenu.Item key={sub.title} href={sub.href}>
78
- {sub.title}
79
- </MegaMenu.Item>
80
- ))}
81
- </MegaMenu.Category>
82
- ))}
83
- </MegaMenu.Column>
84
- ))}
85
- </MegaMenu.Container>
86
- </MegaMenu>
87
- );
149
+ return <RenderMegaMenu nav={nav} />;
88
150
  }
151
+
89
152
  if (menuType === "full-screen-mega-menu") {
90
- return (
91
- <CategoryBasedMenu key={title} title={title} menuList={nav.items}>
92
- <CategoryBasedMenu.MainList />
93
- <CategoryBasedMenu.Section />
94
- </CategoryBasedMenu>
95
- );
153
+ return <RenderFullScreenMenu nav={nav} />;
96
154
  }
97
- if (href && !hasChildren) {
155
+
156
+ if (!hasChildren && href) {
98
157
  return <StyledNavLink href={href}>{title}</StyledNavLink>;
99
158
  }
159
+
100
160
  if (hasChildren) {
101
- return (
102
- <FlexBox
103
- alignItems="center"
104
- position="relative"
105
- flexDirection="column"
106
- sx={{
107
- "&:hover": { "& > .child-nav-item": { display: "block" } },
108
- }}
109
- >
110
- <FlexBox alignItems="flex-end" gap={0.3} sx={NAV_LINK_STYLES}>
111
- {title}{" "}
112
- <KeyboardArrowDown sx={{ color: "grey.500", fontSize: "1.1rem" }} />
113
- </FlexBox>
114
- <ChildNavListWrapper className="child-nav-item">
115
- <BazaarCard elevation={3} sx={{ mt: 2.5, py: 1, minWidth: 100 }}>
116
- {items.map((child) => (
117
- <Navigation.Item key={child.title} nav={child} />
118
- ))}
119
- </BazaarCard>
120
- </ChildNavListWrapper>
121
- </FlexBox>
122
- );
123
- }
124
- } else {
125
- if (href && !hasChildren) {
126
- return (
127
- <NavLink href={href}>
128
- <MenuItem>{title}</MenuItem>
129
- </NavLink>
130
- );
131
- }
132
- if (hasChildren) {
133
- return (
134
- <Navigation.Child nav={nav}>
135
- {items.map((child) => (
136
- <Navigation.Item key={child.title} nav={child} />
137
- ))}
138
- </Navigation.Child>
139
- );
161
+ return <RenderRootWithChildren nav={nav} />;
140
162
  }
163
+
164
+ return null;
165
+ }
166
+
167
+ // CHILD ITEMS
168
+ if (!hasChildren && href) {
169
+ return (
170
+ <NavLink href={href}>
171
+ <MenuItem>{title}</MenuItem>
172
+ </NavLink>
173
+ );
141
174
  }
175
+
176
+ if (hasChildren) {
177
+ return <RenderChildWithChildren nav={nav} />;
178
+ }
179
+
142
180
  return null;
143
181
  };
144
182
 
@@ -56,7 +56,7 @@ export default function Navbar({
56
56
  elevation = 2,
57
57
  hideCategories = false,
58
58
  children,
59
- }: Props) {
59
+ }: Readonly<Props>) {
60
60
  return (
61
61
  <NavbarContext.Provider value={{ hideCategories }}>
62
62
  <NavBarWrapper hoverEffect={false} elevation={elevation} border={border}>
@@ -5,7 +5,7 @@ import React from "react";
5
5
 
6
6
  type Props = { message: string };
7
7
 
8
- export default function NoRecords({ message }: Props) {
8
+ export default function NoRecords({ message }: Readonly<Props>) {
9
9
  const t = useTranslations();
10
10
  return (
11
11
  <Box
@@ -28,7 +28,7 @@ export default function FavoriteButton({
28
28
  activeColor = "primary",
29
29
  inactiveColor = "disabled",
30
30
  ...rest
31
- }: FavoriteButtonProps) {
31
+ }: Readonly<FavoriteButtonProps>) {
32
32
  const iconProps = customIconSize
33
33
  ? { sx: { fontSize: customIconSize } }
34
34
  : { fontSize: iconSize };
@@ -397,16 +397,14 @@ ProductCard.QuickViewButton = () => {
397
397
 
398
398
  ProductCard.FavoriteButton = () => {
399
399
  const internal = useContext(ProductCardInternalContext);
400
- if (!internal) return null;
401
-
402
- const { handleToggleFavorite } = internal;
403
-
404
400
  const ctx = useContext(ProductCardContext);
405
- if (!ctx) return null;
406
401
 
407
- const { id } = ctx.product;
402
+ const id = ctx?.product?.id;
403
+ const isFavorite = useFavorite(id ? Number(id) : undefined);
404
+
405
+ if (!internal || !ctx) return null;
408
406
 
409
- const isFavorite = useFavorite(Number(id));
407
+ const { handleToggleFavorite } = internal;
410
408
 
411
409
  return (
412
410
  <FavoriteButton isFavorite={isFavorite} onToggle={handleToggleFavorite} />
@@ -201,11 +201,11 @@ ProductCardPlp.QuickViewButton = () => {
201
201
 
202
202
  ProductCardPlp.FavoriteButton = () => {
203
203
  const ctx = useContext(ProductCardPlpContext);
204
- if (!ctx) return null;
205
204
 
206
- const { id } = ctx.product;
205
+ const id = ctx?.product?.id;
206
+ const isFavorite = useFavorite(id ? Number(id) : undefined);
207
207
 
208
- const isFavorite = useFavorite(Number(id));
208
+ if (!ctx) return null;
209
209
 
210
210
  return (
211
211
  <FavoriteButton
@@ -216,17 +216,18 @@ ProductCardPlp.FavoriteButton = () => {
216
216
  />
217
217
  );
218
218
  };
219
-
220
219
  // Using quantity buttons (addtocart) doesn't make sense when we have product variants to select.
221
220
  // So hide them for now, implement based on client requirements.
222
221
  ProductCardPlp.QuantityButtons = () => {
223
222
  const ctx = useContext(ProductCardPlpContext);
224
- if (!ctx) return null;
225
223
 
226
- const { id } = ctx.product;
224
+ const id = ctx?.product?.id;
227
225
  const { currentCartLine, handleCartAmountChange, loading } = useCart(id);
226
+
228
227
  const [quantity, setQuantity] = useState(currentCartLine?.Quantity || 0);
229
228
 
229
+ if (!ctx) return null;
230
+
230
231
  const handleIncrementQuantity = () => {
231
232
  const qty = quantity + 1;
232
233
  handleCartAmountChange(qty);
@@ -20,7 +20,7 @@ interface Props {
20
20
  }
21
21
  // ==============================================================
22
22
 
23
- export default function ProductTags({ tags }: Props) {
23
+ export default function ProductTags({ tags }: Readonly<Props>) {
24
24
  return (
25
25
  <StyledRoot>
26
26
  {tags.map((item) => (
@@ -201,11 +201,12 @@ ProductCardPlpList.Price = () => {
201
201
 
202
202
  ProductCardPlpList.FavoriteButton = () => {
203
203
  const ctx = useContext(ProductCardPlpListContext);
204
- if (!ctx) return null;
205
204
 
206
- const { id } = ctx.product;
205
+ const id = ctx?.product?.id;
207
206
  const isFavorite = useFavorite(id);
208
207
 
208
+ if (!ctx) return null;
209
+
209
210
  return (
210
211
  <FavoriteButton
211
212
  isFavorite={isFavorite}
@@ -220,12 +221,14 @@ ProductCardPlpList.FavoriteButton = () => {
220
221
  // So hide them for now, implement based on client requirements.
221
222
  ProductCardPlpList.QuantityButtons = () => {
222
223
  const ctx = useContext(ProductCardPlpListContext);
223
- if (!ctx) return null;
224
224
 
225
- const { id } = ctx.product;
225
+ const id = ctx?.product?.id;
226
226
  const { currentCartLine, handleCartAmountChange, loading } = useCart(id);
227
+
227
228
  const [quantity, setQuantity] = useState(currentCartLine?.Quantity || 0);
228
229
 
230
+ if (!ctx) return null;
231
+
229
232
  const handleIncrementQuantity = () => {
230
233
  const qty = quantity + 1;
231
234
  handleCartAmountChange(qty);
@@ -171,9 +171,11 @@ ProductCardSearch.QuickViewButton = () => {
171
171
 
172
172
  ProductCardSearch.FavoriteButton = () => {
173
173
  const ctx = useContext(ProductCardSearchContext);
174
+
175
+ const id = ctx?.product?.id;
176
+ const isFavorite = useFavorite(id ? Number(id) : undefined);
177
+
174
178
  if (!ctx) return null;
175
- const { id } = ctx.product;
176
- const isFavorite = useFavorite(Number(id));
177
179
 
178
180
  return (
179
181
  <FavoriteButton
@@ -12,7 +12,7 @@ import Box from "@mui/material/Box";
12
12
  type Props = { price: number; discount: number };
13
13
  // ==============================================================
14
14
 
15
- export default function ProductPrice({ discount, price }: Props) {
15
+ export default function ProductPrice({ discount, price }: Readonly<Props>) {
16
16
  return (
17
17
  <FlexBox alignItems="center" gap={1} mt={0.5}>
18
18
  <Paragraph fontWeight={600} color="primary.main">
@@ -15,7 +15,7 @@ export default function ProductRating({
15
15
  showRating,
16
16
  rating = 0,
17
17
  ...props
18
- }: Props) {
18
+ }: Readonly<Props>) {
19
19
  if (showRating) {
20
20
  return (
21
21
  <FlexBox gap={1} alignItems="center" {...props}>
@@ -5,7 +5,7 @@ import Link from "next/link";
5
5
  type Props = { title: string; slug: string };
6
6
  // ==============================================================
7
7
 
8
- export default function ProductTitle({ title, slug }: Props) {
8
+ export default function ProductTitle({ title, slug }: Readonly<Props>) {
9
9
  return (
10
10
  <Link href={`/products/${slug}`}>
11
11
  <H3
@@ -17,7 +17,7 @@ interface Props {
17
17
  }
18
18
  // ==============================================================
19
19
 
20
- export default function QuantityButtons(props: Props) {
20
+ export default function QuantityButtons(props: Readonly<Props>) {
21
21
  const { quantity, handleDecrement, handleIncrement, isLineUpdating } =
22
22
  props || {};
23
23
 
@@ -87,7 +87,7 @@ export const useProductDialog = () => {
87
87
  * </ProductViewDialog.Content>
88
88
  * </ProductViewDialog>
89
89
  */
90
- export default function ProductDialog(props: Props) {
90
+ export default function ProductDialog(props: Readonly<Props>) {
91
91
  const { children, openDialog, handleCloseDialog } = props;
92
92
  const { data: product } = getProductById.useSuspenseData({
93
93
  id: Number(props.productId),
@@ -161,7 +161,7 @@ export const ProductDialogContentImages = () => {
161
161
  >
162
162
  {product?.Images?.map((item: string, index: number) => (
163
163
  <BazaarImage
164
- key={index}
164
+ key={item + index}
165
165
  src={item}
166
166
  alt="product"
167
167
  sx={{
@@ -10,7 +10,7 @@ interface Props {
10
10
  /**
11
11
  * @category Composed Component
12
12
  */
13
- export default function ProductDialog(props: Props) {
13
+ export default function ProductDialog(props: Readonly<Props>) {
14
14
  return (
15
15
  <ProductViewDialog {...props}>
16
16
  <ProductViewDialog.Content>
@@ -19,6 +19,11 @@ export const DimensionGroupList = () => {
19
19
  const { dimensions } = context;
20
20
 
21
21
  return dimensions.map((dimension, i) => {
22
- return <DimensionGroupListChip dimension={dimension} key={i} />;
22
+ return (
23
+ <DimensionGroupListChip
24
+ dimension={dimension}
25
+ key={`dimension-${i + 1}`}
26
+ />
27
+ );
23
28
  });
24
29
  };
@@ -2,7 +2,7 @@
2
2
  import { Dimensions } from "@evenicanpm/storefront-core/src/components-v2/product-dimensions/compound/dimensions";
3
3
  import type { ProductDimensionValueInventoryAvailability } from "@msdyn365-commerce/retail-proxy";
4
4
 
5
- // TODO: When selecting a single dimension, loading spinner starts... not necessary, inefficient.
5
+ // When selecting a single dimension, loading spinner starts... not necessary, inefficient.
6
6
 
7
7
  export type Props = {
8
8
  dimensionGroups: ProductDimensionValueInventoryAvailability[][];
@@ -18,7 +18,7 @@ export default function ProductDimensions({
18
18
  selectDimensions,
19
19
  handleChangeVariant,
20
20
  showDimensionError,
21
- }: Props) {
21
+ }: Readonly<Props>) {
22
22
  return (
23
23
  <Dimensions
24
24
  dimensionGroups={dimensionGroups}
@@ -27,7 +27,7 @@ export default function ProductDimensions({
27
27
  showDimensionError={showDimensionError}
28
28
  >
29
29
  {dimensionGroups.map((group, i) => (
30
- <Dimensions.Group dimensions={group} key={i}>
30
+ <Dimensions.Group dimensions={group} key={`dim-grp-${i + 1}`}>
31
31
  <Dimensions.Group.Label />
32
32
  <Dimensions.Group.List />
33
33
  <Dimensions.Group.Error />
@@ -84,7 +84,7 @@ export function QuantityButtons({
84
84
  isOOS = false,
85
85
  customAddToCart,
86
86
  children,
87
- }: QuantityButtonProps) {
87
+ }: Readonly<QuantityButtonProps>) {
88
88
  const t = useTranslations("productDetail");
89
89
 
90
90
  const { handleAddToCart, handleCartAmountChange, loading, currentCartLine } =
@@ -27,7 +27,7 @@ interface Props {
27
27
  * and the quantity selector component. These two components are usually
28
28
  * used together so this is a pre-wired version for convenience.
29
29
  */
30
- export default function QuantityVariantsSelect({ product }: Props) {
30
+ export default function QuantityVariantsSelect({ product }: Readonly<Props>) {
31
31
  const [showDimensionError, setShowDimensionError] = useState(false);
32
32
 
33
33
  const { DimensionValues, RecordId } = product;
@@ -61,7 +61,6 @@ export default function QuantityVariantsSelect({ product }: Props) {
61
61
  const OOSCode = "OOS"; // Always means out of stock
62
62
 
63
63
  const aggregateCode =
64
- shippingInventoryData?.AggregatedProductInventoryAvailabilities &&
65
64
  shippingInventoryData?.AggregatedProductInventoryAvailabilities?.[0]
66
65
  ?.TotalAvailableInventoryLevelCode;
67
66
 
@@ -2,8 +2,6 @@
2
2
 
3
3
  import type { Meta, StoryObj } from "@storybook/react";
4
4
  import React from "react";
5
- //import { useSession } from "next-auth/react";
6
- import { expect, mocked } from "storybook/test";
7
5
  import { data, prices } from "../../../__mocks__/products";
8
6
  import { ProductsList } from "./index";
9
7
 
@@ -30,7 +30,7 @@ import { cloneElement } from "react";
30
30
  * </ProductsGridView.Container>
31
31
  * </ProductsGridView>
32
32
  */
33
- export default function ProductsGridView(props: ProductsViewProps) {
33
+ export default function ProductsGridView(props: Readonly<ProductsViewProps>) {
34
34
  const { products, productPrices, pagination, children } = props;
35
35
 
36
36
  return (
@@ -68,7 +68,7 @@ ProductsGridView.Grid = ({ children }: { children: React.ReactElement }) => {
68
68
  value={{ price: matchingPrice, product }}
69
69
  >
70
70
  {cloneElement(children, {
71
- key: i,
71
+ key: product.RecordId ?? product.ItemId,
72
72
  product: product,
73
73
  price: matchingPrice,
74
74
  })}