@evenicanpm/storefront-core 2.4.1 → 2.5.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 (122) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/package.json +1 -1
  3. package/src/api-manager/datasources/d365/d365-cart.datasource.ts +4 -3
  4. package/src/api-manager/datasources/d365/d365-user.datasource.ts +3 -3
  5. package/src/api-manager/datasources/d365/utils/get-context-cookie.ts +1 -0
  6. package/src/api-manager/datasources/e4/address/e4-address.datasource.ts +5 -14
  7. package/src/api-manager/datasources/e4/address/e4-address.translator.ts +4 -3
  8. package/src/api-manager/datasources/e4/categories/e4-categories.translator.ts +1 -1
  9. package/src/api-manager/datasources/e4/graphqlRequestSdk.ts +29 -3
  10. package/src/api-manager/datasources/e4/product/e4-product.translator.ts +1 -1
  11. package/src/api-manager/index.ts +1 -1
  12. package/src/api-manager/lib/get-graphql-client.ts +1 -1
  13. package/src/api-manager/services/cart/mutations/add-discount-code.ts +0 -11
  14. package/src/api-manager/services/create-query.ts +13 -7
  15. package/src/api-manager/services/user/mutations/add-to-wishlist.ts +1 -1
  16. package/src/api-manager/services/user/mutations/remove-from-wishlist.ts +1 -1
  17. package/src/auth/AUTH GUIDE.md +880 -0
  18. package/src/cms/blocks/components/featured-categories.tsx +18 -15
  19. package/src/cms/blocks/components/featured-products.tsx +3 -2
  20. package/src/cms/blocks/components/footer/sections/footer-contact.tsx +0 -2
  21. package/src/cms/blocks/components/footer/sections/footer-logo.tsx +2 -1
  22. package/src/components/SearchInput.stories.tsx +12 -10
  23. package/src/components/base-layout.tsx +42 -0
  24. package/src/components/categories/category-list/category-list.tsx +32 -12
  25. package/src/components/header/__tests__/header.test.tsx +0 -24
  26. package/src/components/header/components/mobile-header.tsx +16 -8
  27. package/src/components/header/header.tsx +6 -3
  28. package/src/components/mini-cart/components/bottom-actions.tsx +7 -2
  29. package/src/components/mini-cart/components/cart-item.tsx +16 -13
  30. package/src/components/mini-cart/components/empty-view.tsx +12 -6
  31. package/src/components/mini-cart/components/top-header.tsx +9 -8
  32. package/src/components/mini-cart/mini-cart-trigger.tsx +11 -5
  33. package/src/components/mini-cart/mini-cart.tsx +30 -15
  34. package/src/components/mobile-navigation/mobile-navigation-bar.tsx +6 -2
  35. package/src/components/navbar/categories.tsx +8 -2
  36. package/src/components/navbar/category-based-menu/category-based-menu.tsx +6 -2
  37. package/src/components/navbar/mega-menu/mega-menu.tsx +22 -14
  38. package/src/components/navbar/mobile-menu/mobile-menu.test.tsx +8 -11
  39. package/src/components/navbar/mobile-menu/mobile-menu.tsx +13 -11
  40. package/src/components/navbar/nav-list/nav-list.tsx +3 -2
  41. package/src/components/navbar/navbar.tsx +4 -2
  42. package/src/components/product-cards/product-card/product-card.tsx +50 -26
  43. package/src/components/product-cards/product-card-plp/product-card.tsx +37 -16
  44. package/src/components/product-cards/product-card-plp-list/product-card.tsx +38 -16
  45. package/src/components/product-cards/product-card-search/product-card.tsx +34 -13
  46. package/src/components/product-dialog/ProductDialog.stories.tsx +20 -18
  47. package/src/components/product-dialog/compound/product-dialog.tsx +7 -4
  48. package/src/components/product-dimensions/compound/dimensions-group.tsx +4 -2
  49. package/src/components/product-dimensions/compound/dimensions.tsx +15 -6
  50. package/src/components/product-quantity-buttons/compound/quantity-buttons-root.tsx +21 -13
  51. package/src/components/products-view/compound/products-grid-view.tsx +11 -4
  52. package/src/components/products-view/compound/products-list-view.tsx +11 -4
  53. package/src/components/search-bar/compound/search-bar-root.tsx +6 -4
  54. package/src/components/search-bar/compound/textfield-adornment-category.tsx +25 -8
  55. package/src/components/search-bar/hooks/use-click-outside.ts +7 -1
  56. package/src/components/search-bar/hooks/use-search.ts +3 -3
  57. package/src/components/user/index.ts +3 -0
  58. package/src/components/user/use-user-auth.ts +163 -0
  59. package/src/components/user/user.tsx +215 -0
  60. package/src/components/wishlist-dialogs/add-to-wishlist/AddToWishlist.stories.tsx +20 -15
  61. package/src/components/wishlist-dialogs/add-to-wishlist/compound/add-to-wishlist-dialog.tsx +11 -8
  62. package/src/components/wishlist-dialogs/add-to-wishlist/compound/add-to-wishlist-list.tsx +18 -3
  63. package/src/components/wishlist-dialogs/add-to-wishlist/index.tsx +0 -1
  64. package/src/components/wishlist-dialogs/create-wishlist/compound/create-wishlist-dialog.tsx +6 -2
  65. package/src/hooks/use-overflow-detect.ts +4 -4
  66. package/src/lib/page-slugs.ts +16 -0
  67. package/src/pages/account/account-navigation.tsx +7 -14
  68. package/src/pages/account/account-routes.ts +4 -9
  69. package/src/pages/account/addresses/address-form.tsx +38 -16
  70. package/src/pages/account/addresses/address-item.tsx +18 -8
  71. package/src/pages/account/dashboard-header.tsx +20 -13
  72. package/src/pages/account/orders/order-history-filters-panel.tsx +21 -20
  73. package/src/pages/account/orders/order-history-header.tsx +14 -4
  74. package/src/pages/account/orders/order-history-root.tsx +3 -1
  75. package/src/pages/account/orders/order-history-row.tsx +12 -4
  76. package/src/pages/account/orders/order-history-search-and-filter.tsx +88 -59
  77. package/src/pages/account/orders/order-history-search-dropdown.tsx +26 -9
  78. package/src/pages/account/orders/order-history-sort.tsx +3 -1
  79. package/src/pages/account/orders/order-history-table.tsx +27 -5
  80. package/src/pages/account/orders/order-progress.tsx +12 -6
  81. package/src/pages/account/orders/order-row.tsx +18 -6
  82. package/src/pages/account/orders/order-summary.tsx +24 -8
  83. package/src/pages/account/orders/ordered-products.tsx +15 -5
  84. package/src/pages/account/profile/user-info.tsx +11 -3
  85. package/src/pages/account/table-row-skeleton.tsx +1 -1
  86. package/src/pages/account/wishlist/create-new-list.tsx +33 -9
  87. package/src/pages/account/wishlist/wishlist-item.tsx +18 -6
  88. package/src/pages/cart/cart-item.tsx +21 -7
  89. package/src/pages/cart/checkout-form.tsx +55 -22
  90. package/src/pages/cart/coupon-entry.tsx +143 -36
  91. package/src/pages/cart/estimate-shipping.tsx +45 -21
  92. package/src/pages/checkout/checkout-alt-form/checkout-form.tsx +22 -14
  93. package/src/pages/checkout/checkout-alt-form/checkout-step.tsx +37 -16
  94. package/src/pages/checkout/checkout-alt-form/steps/address/address-card.tsx +9 -3
  95. package/src/pages/checkout/checkout-alt-form/steps/address/delivery-address.tsx +84 -51
  96. package/src/pages/checkout/checkout-alt-form/steps/address/new-address-form.tsx +41 -19
  97. package/src/pages/checkout/checkout-alt-form/steps/customer-info/customer-information.tsx +17 -10
  98. package/src/pages/checkout/checkout-alt-form/steps/payment/payment-details.tsx +15 -6
  99. package/src/pages/checkout/checkout-alt-form/steps/shipping/delivery-options.tsx +6 -2
  100. package/src/pages/checkout/checkout-alt-summary/cart-item.tsx +12 -4
  101. package/src/pages/checkout/checkout-alt-summary/checkout-alt-summary.tsx +12 -5
  102. package/src/pages/checkout/checkout-alt-summary/list-item.tsx +17 -10
  103. package/src/pages/confirmation/address.tsx +6 -2
  104. package/src/pages/confirmation/confirmation-summary.tsx +12 -4
  105. package/src/pages/confirmation/ordered-products.tsx +12 -4
  106. package/src/pages/product-details/bopis/store-card/shop-card.tsx +2 -2
  107. package/src/pages/product-details/bopis/store-card/utils/getDaysResources.ts +1 -1
  108. package/src/pages/product-details/product-intro/compound/product-info.tsx +41 -18
  109. package/src/pages/product-details/product-intro/compound/product-intro-images.tsx +18 -12
  110. package/src/pages/product-details/product-intro/compound/product-intro.tsx +20 -10
  111. package/src/pages/product-details/product-intro/compound/thumbnail-with-skeleton.tsx +1 -1
  112. package/src/pages/product-details/product-tabs.tsx +20 -12
  113. package/src/pages/product-list/product-list-view.tsx +4 -2
  114. package/src/pages/product-list/search-bar.tsx +7 -7
  115. package/src/pages/product-list/side-nav.tsx +5 -3
  116. package/src/pages/quickorder/order-upload.tsx +1 -1
  117. package/src/pages/quickorder/provider.tsx +26 -14
  118. package/src/pages/quickorder/quick-order.tsx +49 -22
  119. package/src/providers/nav-provider/index.tsx +16 -11
  120. package/tsconfig.json +0 -5
  121. package/src/components/header/components/user.tsx +0 -322
  122. /package/src/components/{header → user}/__tests__/user.test.tsx +0 -0
@@ -58,7 +58,7 @@ type PaymentDetailsCompound = React.FC<CheckoutStepComponentProps> & {
58
58
  selectedPaymentMethod: PaymentMethod;
59
59
  }>;
60
60
  Form: React.FC<{
61
- iframeReference: React.RefObject<HTMLIFrameElement>;
61
+ iframeReference: React.RefObject<HTMLIFrameElement | null>;
62
62
  iframeHeight: number;
63
63
  errorState: PaymentError[];
64
64
  paymentAcceptPointData: CardPaymentAcceptPoint | null | undefined;
@@ -111,7 +111,7 @@ const PaymentDetails: PaymentDetailsCompound = (props) => {
111
111
 
112
112
  const { mutateAsync: retrievePaymentAcceptResult } = usePaymentAcceptResult();
113
113
  const iframeReference = React.useRef<HTMLIFrameElement>(null);
114
- const [iframeHeight, setIFrameHeight] = React.useState<number>(1);
114
+ const [iframeHeight, setIframeHeight] = React.useState<number>(1);
115
115
  const [errorState, setErrorState] = React.useState<PaymentError[]>([]);
116
116
 
117
117
  const paymentConnectorMessage = React.useCallback(
@@ -182,7 +182,9 @@ const PaymentDetails: PaymentDetailsCompound = (props) => {
182
182
  switch (type) {
183
183
  case PaymentConnectorPostMessageType.Height: {
184
184
  // Payment connector posts height after initialization
185
- setIFrameHeight(value as number);
185
+ if (typeof value === "number") {
186
+ setIframeHeight(value);
187
+ }
186
188
  postMessageToIframe(
187
189
  paymentConnectorMessage(
188
190
  cart.TotalAmount.toString(),
@@ -194,7 +196,7 @@ const PaymentDetails: PaymentDetailsCompound = (props) => {
194
196
  case PaymentConnectorPostMessageType.Result: {
195
197
  try {
196
198
  if (typeof value !== "string") {
197
- throw new Error("Invalid payment result access code");
199
+ throw new TypeError("Invalid payment result access code");
198
200
  }
199
201
  await handleResultsAccessCode(value);
200
202
  setStepSubmissionState("idle");
@@ -363,7 +365,10 @@ const PaymentDetails: PaymentDetailsCompound = (props) => {
363
365
  );
364
366
  };
365
367
 
366
- PaymentDetails.Preview = ({ paymentAcceptResult, selectedPaymentMethod }) => {
368
+ const PaymentDetailsPreview = ({
369
+ paymentAcceptResult,
370
+ selectedPaymentMethod,
371
+ }) => {
367
372
  const t = useTranslations("Checkout.Payment");
368
373
 
369
374
  if (selectedPaymentMethod === "on-account") {
@@ -422,7 +427,9 @@ PaymentDetails.Preview = ({ paymentAcceptResult, selectedPaymentMethod }) => {
422
427
  );
423
428
  };
424
429
 
425
- PaymentDetails.Form = ({
430
+ PaymentDetails.Preview = PaymentDetailsPreview;
431
+
432
+ const PaymentDetailsForm = ({
426
433
  iframeReference,
427
434
  iframeHeight,
428
435
  errorState,
@@ -468,4 +475,6 @@ PaymentDetails.Form = ({
468
475
  );
469
476
  };
470
477
 
478
+ PaymentDetails.Form = PaymentDetailsForm;
479
+
471
480
  export default PaymentDetails;
@@ -173,7 +173,7 @@ const DeliveryOptions: DeliveryOptionsCompound = (props) => {
173
173
  );
174
174
  };
175
175
 
176
- DeliveryOptions.Preview = ({ deliveryOptions, selectedOption }) => {
176
+ const DeliveryOptionsPreview = ({ deliveryOptions, selectedOption }) => {
177
177
  const { formatCurrency } = useCurrencyFormatter();
178
178
  const deliveryOption = deliveryOptions.find(
179
179
  (dlv) => dlv.Code === selectedOption,
@@ -189,7 +189,9 @@ DeliveryOptions.Preview = ({ deliveryOptions, selectedOption }) => {
189
189
  );
190
190
  };
191
191
 
192
- DeliveryOptions.Form = ({
192
+ DeliveryOptions.Preview = DeliveryOptionsPreview;
193
+
194
+ const DeliveryOptionsForm = ({
193
195
  deliveryOptions,
194
196
  deliveryUnavailable,
195
197
  selectedOption,
@@ -240,4 +242,6 @@ DeliveryOptions.Form = ({
240
242
  );
241
243
  };
242
244
 
245
+ DeliveryOptions.Form = DeliveryOptionsForm;
246
+
243
247
  export default DeliveryOptions;
@@ -7,7 +7,7 @@ import {
7
7
  Span,
8
8
  } from "@evenicanpm/storefront-core/src/components/Typography";
9
9
  import useCurrencyFormatter from "@evenicanpm/storefront-core/src/lib/currency-formatter";
10
- import { createContext, type ReactNode, useContext } from "react";
10
+ import { createContext, type ReactNode, useContext, useMemo } from "react";
11
11
 
12
12
  interface CartItemContextValue {
13
13
  cartLine: CartLine;
@@ -20,8 +20,12 @@ interface CartItemProps {
20
20
  }
21
21
  const CartItem = ({ cartLine, children }: CartItemProps) => {
22
22
  const { formatCurrency } = useCurrencyFormatter();
23
+ const contextValue = useMemo(
24
+ () => ({ cartLine, formatCurrency }),
25
+ [cartLine, formatCurrency],
26
+ );
23
27
  return (
24
- <CartItemContext.Provider value={{ cartLine, formatCurrency }}>
28
+ <CartItemContext.Provider value={contextValue}>
25
29
  <FlexBetween mb={1.5}>
26
30
  {children ?? (
27
31
  <>
@@ -34,7 +38,7 @@ const CartItem = ({ cartLine, children }: CartItemProps) => {
34
38
  );
35
39
  };
36
40
 
37
- CartItem.Description = () => {
41
+ const CartItemDescription = () => {
38
42
  const ctx = useContext(CartItemContext);
39
43
  if (!ctx) return null;
40
44
  const { cartLine } = ctx;
@@ -46,7 +50,9 @@ CartItem.Description = () => {
46
50
  );
47
51
  };
48
52
 
49
- CartItem.Price = () => {
53
+ CartItem.Description = CartItemDescription;
54
+
55
+ const CartItemPrice = () => {
50
56
  const ctx = useContext(CartItemContext);
51
57
  if (!ctx) return null;
52
58
  const { cartLine, formatCurrency } = ctx;
@@ -69,4 +75,6 @@ CartItem.Price = () => {
69
75
  );
70
76
  };
71
77
 
78
+ CartItem.Price = CartItemPrice;
79
+
72
80
  export default CartItem;
@@ -13,7 +13,7 @@ import ListItem from "@evenicanpm/storefront-core/src/pages/checkout/checkout-al
13
13
  import Box from "@mui/material/Box";
14
14
  import Divider from "@mui/material/Divider";
15
15
  import { useTranslations } from "next-intl";
16
- import { createContext, type ReactNode, useContext } from "react";
16
+ import { createContext, type ReactNode, useContext, useMemo } from "react";
17
17
 
18
18
  interface Props {
19
19
  cart: Cart;
@@ -29,15 +29,16 @@ const CheckoutAltSummaryContext =
29
29
 
30
30
  const CheckoutAltSummary = ({ cart, children }: Props) => {
31
31
  const t = useTranslations("Checkout.Summary");
32
+ const contextValue = useMemo(() => ({ cart, t }), [cart, t]);
32
33
 
33
34
  return (
34
- <CheckoutAltSummaryContext.Provider value={{ cart, t }}>
35
+ <CheckoutAltSummaryContext.Provider value={contextValue}>
35
36
  <div>{children}</div>
36
37
  </CheckoutAltSummaryContext.Provider>
37
38
  );
38
39
  };
39
40
 
40
- CheckoutAltSummary.Title = () => {
41
+ const CheckoutAltSummaryTitle = () => {
41
42
  const ctx = useContext(CheckoutAltSummaryContext);
42
43
  if (!ctx) return null;
43
44
  const { t } = ctx;
@@ -48,7 +49,9 @@ CheckoutAltSummary.Title = () => {
48
49
  );
49
50
  };
50
51
 
51
- CheckoutAltSummary.CartItems = () => {
52
+ CheckoutAltSummary.Title = CheckoutAltSummaryTitle;
53
+
54
+ const CheckoutAltSummaryCartItems = () => {
52
55
  const ctx = useContext(CheckoutAltSummaryContext);
53
56
  if (!ctx) return null;
54
57
  const { cart } = ctx;
@@ -61,11 +64,13 @@ CheckoutAltSummary.CartItems = () => {
61
64
  );
62
65
  };
63
66
 
67
+ CheckoutAltSummary.CartItems = CheckoutAltSummaryCartItems;
68
+
64
69
  CheckoutAltSummary.Divider = (props: { sx?: object }) => {
65
70
  return <Box component={Divider} borderColor="grey.300" {...props} />;
66
71
  };
67
72
 
68
- CheckoutAltSummary.ListItem = ({
73
+ const CheckoutAltSummaryListItem = ({
69
74
  cartKey,
70
75
  titleKey,
71
76
  color,
@@ -89,4 +94,6 @@ CheckoutAltSummary.ListItem = ({
89
94
  );
90
95
  };
91
96
 
97
+ CheckoutAltSummary.ListItem = CheckoutAltSummaryListItem;
98
+
92
99
  export default CheckoutAltSummary;
@@ -1,7 +1,7 @@
1
1
  import { FlexBetween } from "@evenicanpm/storefront-core/src/components/flex-box/index";
2
2
  import { Paragraph } from "@evenicanpm/storefront-core/src/components/Typography";
3
3
  import useCurrencyFormatter from "@evenicanpm/storefront-core/src/lib/currency-formatter";
4
- import { createContext, type ReactNode, useContext } from "react";
4
+ import { createContext, type ReactNode, useContext, useMemo } from "react";
5
5
 
6
6
  interface ListItemProps {
7
7
  title: string;
@@ -30,13 +30,16 @@ const ListItem = ({
30
30
  }: ListItemProps) => {
31
31
  const { formatCurrency } = useCurrencyFormatter();
32
32
 
33
- const contextValue: ListItemContextValue = {
34
- title,
35
- value,
36
- mb,
37
- color,
38
- formatCurrency,
39
- };
33
+ const contextValue: ListItemContextValue = useMemo(
34
+ () => ({
35
+ title,
36
+ value,
37
+ mb,
38
+ color,
39
+ formatCurrency,
40
+ }),
41
+ [title, value, mb, color, formatCurrency],
42
+ );
40
43
 
41
44
  return (
42
45
  <ListItemContext.Provider value={contextValue}>
@@ -52,14 +55,16 @@ const ListItem = ({
52
55
  );
53
56
  };
54
57
 
55
- ListItem.Title = () => {
58
+ const ListItemTitle = () => {
56
59
  const ctx = useContext(ListItemContext);
57
60
  if (!ctx) return null;
58
61
  const { title, color } = ctx;
59
62
  return <Paragraph color={color}>{title}:</Paragraph>;
60
63
  };
61
64
 
62
- ListItem.Value = () => {
65
+ ListItem.Title = ListItemTitle;
66
+
67
+ const ListItemValue = () => {
63
68
  const ctx = useContext(ListItemContext);
64
69
  if (!ctx) return null;
65
70
  const { value, formatCurrency } = ctx;
@@ -70,4 +75,6 @@ ListItem.Value = () => {
70
75
  );
71
76
  };
72
77
 
78
+ ListItem.Value = ListItemValue;
79
+
73
80
  export default ListItem;
@@ -24,7 +24,7 @@ const OrderAddress = ({ address, children }: Props) => {
24
24
  );
25
25
  };
26
26
 
27
- OrderAddress.Heading = ({ children }: { children?: ReactNode }) => {
27
+ const OrderAddressHeading = ({ children }: { children?: ReactNode }) => {
28
28
  const t = useTranslations("Address");
29
29
  return (
30
30
  <H5 mt={0} mb={2}>
@@ -33,7 +33,9 @@ OrderAddress.Heading = ({ children }: { children?: ReactNode }) => {
33
33
  );
34
34
  };
35
35
 
36
- OrderAddress.Field = ({
36
+ OrderAddress.Heading = OrderAddressHeading;
37
+
38
+ const OrderAddressField = ({
37
39
  property,
38
40
  }: {
39
41
  property: Exclude<keyof Address, "ExtensionProperties">;
@@ -48,4 +50,6 @@ OrderAddress.Field = ({
48
50
  return <Paragraph>{value}</Paragraph>;
49
51
  };
50
52
 
53
+ OrderAddress.Field = OrderAddressField;
54
+
51
55
  export default OrderAddress;
@@ -12,7 +12,7 @@ import {
12
12
  import useCurrencyFormatter from "@evenicanpm/storefront-core/src/lib/currency-formatter";
13
13
  import { Card, Divider } from "@mui/material";
14
14
  import { useTranslations } from "next-intl";
15
- import { createContext, type ReactNode, useContext } from "react";
15
+ import { createContext, type ReactNode, useContext, useMemo } from "react";
16
16
 
17
17
  type SummaryValues = {
18
18
  order: SalesOrder;
@@ -26,9 +26,13 @@ type Props = SummaryValues & { children?: ReactNode };
26
26
 
27
27
  const OrderSummary = ({ order, children }: Props) => {
28
28
  const { formatCurrency } = useCurrencyFormatter();
29
+ const contextValue = useMemo(
30
+ () => ({ order, formatCurrency }),
31
+ [order, formatCurrency],
32
+ );
29
33
 
30
34
  return (
31
- <SummaryContext.Provider value={{ order, formatCurrency }}>
35
+ <SummaryContext.Provider value={contextValue}>
32
36
  <Card sx={{ px: 3, mb: "30px" }}>
33
37
  {children ?? (
34
38
  <>
@@ -51,7 +55,7 @@ type ItemProps = {
51
55
  field: keyof SalesOrder;
52
56
  };
53
57
 
54
- OrderSummary.Title = () => {
58
+ const OrderSummaryTitle = () => {
55
59
  const t = useTranslations("Confirmation");
56
60
  return (
57
61
  <FlexBox
@@ -66,7 +70,9 @@ OrderSummary.Title = () => {
66
70
  );
67
71
  };
68
72
 
69
- OrderSummary.Item = ({ label, field }: ItemProps) => {
73
+ OrderSummary.Title = OrderSummaryTitle;
74
+
75
+ const OrderSummaryItem = ({ label, field }: ItemProps) => {
70
76
  const ctx = useContext(SummaryContext);
71
77
  const t = useTranslations("Checkout.Summary");
72
78
  if (!ctx) return null;
@@ -88,6 +94,8 @@ OrderSummary.Item = ({ label, field }: ItemProps) => {
88
94
  );
89
95
  };
90
96
 
97
+ OrderSummary.Item = OrderSummaryItem;
98
+
91
99
  OrderSummary.Divider = (props: { sx?: object }) => {
92
100
  return <Divider {...props} />;
93
101
  };
@@ -33,7 +33,7 @@ export const OrderedProducts = ({ order, children }: Props) => {
33
33
  );
34
34
  };
35
35
 
36
- OrderedProducts.Header = () => {
36
+ const OrderedProductsHeader = () => {
37
37
  const ctx = useContext(OrderedProductsContext);
38
38
  const t = useTranslations("Confirmation");
39
39
  const { dateTime } = useFormatter();
@@ -68,6 +68,8 @@ OrderedProducts.Header = () => {
68
68
  );
69
69
  };
70
70
 
71
+ OrderedProducts.Header = OrderedProductsHeader;
72
+
71
73
  OrderedProducts.List = ({ children }: { children: React.ReactNode }) => {
72
74
  return (
73
75
  <Grid container spacing={2} alignItems="center" padding="15px 5px">
@@ -100,7 +102,7 @@ OrderedProducts.Info = ({ children }: { children: React.ReactNode }) => {
100
102
  return <div>{children}</div>;
101
103
  };
102
104
 
103
- OrderedProducts.Title = ({ line }: LineProps) => {
105
+ const OrderedProductsTitle = ({ line }: LineProps) => {
104
106
  const { formatCurrency } = useCurrencyFormatter();
105
107
  return (
106
108
  <>
@@ -112,7 +114,9 @@ OrderedProducts.Title = ({ line }: LineProps) => {
112
114
  );
113
115
  };
114
116
 
115
- OrderedProducts.Comment = ({ line }: LineProps) => {
117
+ OrderedProducts.Title = OrderedProductsTitle;
118
+
119
+ const OrderedProductsComment = ({ line }: LineProps) => {
116
120
  const t = useTranslations("Confirmation");
117
121
  if (!line.Comment) return null;
118
122
  return (
@@ -125,6 +129,8 @@ OrderedProducts.Comment = ({ line }: LineProps) => {
125
129
  );
126
130
  };
127
131
 
132
+ OrderedProducts.Comment = OrderedProductsComment;
133
+
128
134
  OrderedProducts.Dimensions = ({ line }: LineProps) => {
129
135
  if (!line.Product?.Dimensions?.length) return <Box />;
130
136
  return (
@@ -139,7 +145,7 @@ OrderedProducts.Dimensions = ({ line }: LineProps) => {
139
145
  );
140
146
  };
141
147
 
142
- OrderedProducts.Total = ({ line }: LineProps) => {
148
+ const OrderedProductsTotal = ({ line }: LineProps) => {
143
149
  const { formatCurrency } = useCurrencyFormatter();
144
150
  return (
145
151
  <Grid size={{ xs: 4, sm: 2 }}>
@@ -150,4 +156,6 @@ OrderedProducts.Total = ({ line }: LineProps) => {
150
156
  );
151
157
  };
152
158
 
159
+ OrderedProducts.Total = OrderedProductsTotal;
160
+
153
161
  export default OrderedProducts;
@@ -14,7 +14,7 @@ import {
14
14
  // STYLED COMPONENT
15
15
  import { ContentWrapper } from "@evenicanpm/storefront-core/src/pages/product-details/bopis/store-card/styles";
16
16
  import { convertSecondsToTime } from "@evenicanpm/storefront-core/src/pages/product-details/bopis/store-card/utils/convertSecondsToTime";
17
- import { getDaysResources } from "@evenicanpm/storefront-core/src/pages/product-details/bopis/store-card/utils/getDaysResources";
17
+ import { useDaysResources } from "@evenicanpm/storefront-core/src/pages/product-details/bopis/store-card/utils/getDaysResources";
18
18
  import type {
19
19
  ProductWarehouseInventoryAvailability,
20
20
  StoreHours,
@@ -39,7 +39,7 @@ export default function ShopCard(props: Readonly<Props>) {
39
39
  const { name, address, actionArea, openFrom, openTo, storeHours } =
40
40
  props || {};
41
41
 
42
- const resources = getDaysResources();
42
+ const resources = useDaysResources();
43
43
 
44
44
  const renderStoreHours = () => {
45
45
  if (storeHours?.RegularStoreHours) {
@@ -1,7 +1,7 @@
1
1
  import type { IStoreSelectorLocationLineItemResources } from "@evenicanpm/storefront-core/src/lib/store-hours-helper";
2
2
  import { useTranslations } from "next-intl";
3
3
 
4
- export const getDaysResources = (): IStoreSelectorLocationLineItemResources => {
4
+ export const useDaysResources = (): IStoreSelectorLocationLineItemResources => {
5
5
  const daysTrans = useTranslations("StoreHours");
6
6
  return {
7
7
  days: {
@@ -13,7 +13,7 @@ import Box from "@mui/material/Box";
13
13
  import Grid from "@mui/material/Grid2";
14
14
  import Rating from "@mui/material/Rating";
15
15
  import type React from "react";
16
- import { useContext, useState } from "react";
16
+ import { useContext, useMemo, useState } from "react";
17
17
 
18
18
  // D365
19
19
 
@@ -81,18 +81,27 @@ const ProductInfo = ({ children }: Props) => {
81
81
  !productPrices ||
82
82
  variantPending ||
83
83
  (allDimensionsSelected && inventoryPending);
84
+ const contextValue = useMemo(
85
+ () => ({
86
+ hasVariants,
87
+ isProductDetailsPending,
88
+ productPrices,
89
+ shippingInventoryData,
90
+ showDimensionError,
91
+ setShowDimensionError,
92
+ }),
93
+ [
94
+ hasVariants,
95
+ isProductDetailsPending,
96
+ productPrices,
97
+ shippingInventoryData,
98
+ showDimensionError,
99
+ setShowDimensionError,
100
+ ],
101
+ );
84
102
 
85
103
  return (
86
- <ProductInfoContext.Provider
87
- value={{
88
- hasVariants,
89
- isProductDetailsPending,
90
- productPrices,
91
- shippingInventoryData,
92
- showDimensionError,
93
- setShowDimensionError,
94
- }}
95
- >
104
+ <ProductInfoContext.Provider value={contextValue}>
96
105
  <Grid size={{ md: 6, xs: 12 }} alignItems="center">
97
106
  {children}
98
107
  </Grid>
@@ -103,7 +112,7 @@ const ProductInfo = ({ children }: Props) => {
103
112
  /**
104
113
  * @category Sub-Component
105
114
  */
106
- ProductInfo.Name = () => {
115
+ const ProductInfoName = () => {
107
116
  const IntroCtx = useProductIntro();
108
117
  if (!IntroCtx)
109
118
  throw new Error(
@@ -113,10 +122,12 @@ ProductInfo.Name = () => {
113
122
  return <H1 mb={1}>{masterProduct.Name}</H1>;
114
123
  };
115
124
 
125
+ ProductInfo.Name = ProductInfoName;
126
+
116
127
  /**
117
128
  * @category Sub-Component
118
129
  */
119
- ProductInfo.Sku = () => {
130
+ const ProductInfoSku = () => {
120
131
  const IntroCtx = useProductIntro();
121
132
  if (!IntroCtx)
122
133
  throw new Error(
@@ -130,10 +141,12 @@ ProductInfo.Sku = () => {
130
141
  );
131
142
  };
132
143
 
144
+ ProductInfo.Sku = ProductInfoSku;
145
+
133
146
  /**
134
147
  * @category Sub-Component
135
148
  */
136
- ProductInfo.Sku = () => {
149
+ const ProductInfoRating = () => {
137
150
  return (
138
151
  <FlexBox alignItems="center" gap={1} mb={2}>
139
152
  <Box lineHeight="1">Rated:</Box>
@@ -143,10 +156,12 @@ ProductInfo.Sku = () => {
143
156
  );
144
157
  };
145
158
 
159
+ ProductInfo.Rating = ProductInfoRating;
160
+
146
161
  /**
147
162
  * @category Sub-Component
148
163
  */
149
- ProductInfo.Dimensions = () => {
164
+ const ProductInfoDimensions = () => {
150
165
  const InfoCtx = useContext(ProductInfoContext);
151
166
  const IntroCtx = useProductIntro();
152
167
  if (!InfoCtx || !IntroCtx)
@@ -170,7 +185,9 @@ ProductInfo.Dimensions = () => {
170
185
  );
171
186
  };
172
187
 
173
- ProductInfo.Price = () => {
188
+ ProductInfo.Dimensions = ProductInfoDimensions;
189
+
190
+ const ProductInfoPrice = () => {
174
191
  const { formatCurrency } = useCurrencyFormatter();
175
192
  const { isProductDetailsPending, productPrices } = useProductInfo();
176
193
 
@@ -188,10 +205,12 @@ ProductInfo.Price = () => {
188
205
  );
189
206
  };
190
207
 
208
+ ProductInfo.Price = ProductInfoPrice;
209
+
191
210
  /**
192
211
  * @category Sub-Component
193
212
  */
194
- ProductInfo.Inventory = () => {
213
+ const ProductInfoInventory = () => {
195
214
  const InfoCtx = useContext(ProductInfoContext);
196
215
  const IntroCtx = useProductIntro();
197
216
  if (!InfoCtx || !IntroCtx)
@@ -227,12 +246,14 @@ ProductInfo.Inventory = () => {
227
246
  );
228
247
  };
229
248
 
249
+ ProductInfo.Inventory = ProductInfoInventory;
250
+
230
251
  /**
231
252
  * These are grouped together because they have similar functionality and callbacks
232
253
  * addToCart Etc
233
254
  * @category Sub-Component
234
255
  */
235
- ProductInfo.Buttons = () => {
256
+ const ProductInfoButtons = () => {
236
257
  const { masterProduct, selectedVariant, allDimensionsSelected } =
237
258
  useProductIntro();
238
259
  const { setShowDimensionError, shippingInventoryData, hasVariants } =
@@ -282,4 +303,6 @@ ProductInfo.Buttons = () => {
282
303
  );
283
304
  };
284
305
 
306
+ ProductInfo.Buttons = ProductInfoButtons;
307
+
285
308
  export default ProductInfo;
@@ -13,7 +13,7 @@ import ThumbnailWithSkeleton from "@evenicanpm/storefront-core/src/pages/product
13
13
  import Grid from "@mui/material/Grid2";
14
14
  import Skeleton from "@mui/material/Skeleton";
15
15
  import type React from "react";
16
- import { useContext, useState } from "react";
16
+ import { useContext, useMemo, useState } from "react";
17
17
 
18
18
  type Props = {
19
19
  children: React.ReactNode;
@@ -56,17 +56,19 @@ export default function ProductImages({ children }: Readonly<Props>) {
56
56
  const handleImageClick = (ind: number) => () => {
57
57
  setSelectedImageIndex(ind);
58
58
  };
59
+ const contextValue = useMemo(
60
+ () => ({
61
+ name: Name,
62
+ selectedImageIndex,
63
+ images,
64
+ handleImageClick,
65
+ primaryImageUrl,
66
+ }),
67
+ [Name, selectedImageIndex, images, handleImageClick, primaryImageUrl],
68
+ );
59
69
 
60
70
  return (
61
- <ProductImagesContext.Provider
62
- value={{
63
- name: Name,
64
- selectedImageIndex,
65
- images,
66
- handleImageClick,
67
- primaryImageUrl,
68
- }}
69
- >
71
+ <ProductImagesContext.Provider value={contextValue}>
70
72
  <Grid size={{ md: 6, xs: 12 }} alignItems="center">
71
73
  {children}
72
74
  </Grid>
@@ -78,7 +80,7 @@ export default function ProductImages({ children }: Readonly<Props>) {
78
80
  * The image gallery thumbnails, when clicked switches the main image.
79
81
  * @category Sub-Component
80
82
  */
81
- ProductImages.Thumbnails = () => {
83
+ const ProductImagesThumbnails = () => {
82
84
  const ctx = useContext(ProductImagesContext);
83
85
  if (!ctx)
84
86
  throw new Error(
@@ -113,12 +115,14 @@ ProductImages.Thumbnails = () => {
113
115
  );
114
116
  };
115
117
 
118
+ ProductImages.Thumbnails = ProductImagesThumbnails;
119
+
116
120
  /**
117
121
  * Main large image on PDP that displays currently selected image from
118
122
  * product details.
119
123
  * @category Sub-Component
120
124
  */
121
- ProductImages.Main = () => {
125
+ const ProductImagesMain = () => {
122
126
  const ctx = useContext(ProductImagesContext);
123
127
  if (!ctx)
124
128
  throw new Error(
@@ -162,3 +166,5 @@ ProductImages.Main = () => {
162
166
  </FlexBox>
163
167
  );
164
168
  };
169
+
170
+ ProductImages.Main = ProductImagesMain;
@@ -7,6 +7,7 @@ import ProductImages from "@evenicanpm/storefront-core/src/pages/product-details
7
7
  import Box from "@mui/material/Box";
8
8
  import Grid from "@mui/material/Grid2";
9
9
  import type React from "react";
10
+ import { useMemo } from "react";
10
11
 
11
12
  /**
12
13
  * Master product data comes in as prop, selected variant details are called
@@ -23,18 +24,27 @@ export default function ProductIntro({ product, children }: Readonly<Props>) {
23
24
  allDimensionsSelected,
24
25
  variantPending,
25
26
  } = useVariants(product);
27
+ const contextValue = useMemo(
28
+ () => ({
29
+ masterProduct: product,
30
+ allDimensionsSelected,
31
+ variantPending,
32
+ selectedVariant,
33
+ selectDimensions,
34
+ handleChangeVariant,
35
+ }),
36
+ [
37
+ product,
38
+ allDimensionsSelected,
39
+ variantPending,
40
+ selectedVariant,
41
+ selectDimensions,
42
+ handleChangeVariant,
43
+ ],
44
+ );
26
45
 
27
46
  return (
28
- <ProductIntroContext.Provider
29
- value={{
30
- masterProduct: product,
31
- allDimensionsSelected,
32
- variantPending,
33
- selectedVariant,
34
- selectDimensions,
35
- handleChangeVariant,
36
- }}
37
- >
47
+ <ProductIntroContext.Provider value={contextValue}>
38
48
  <Box width="100%">
39
49
  <Grid container spacing={3} justifyContent="space-around">
40
50
  {children}