@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.
Files changed (142) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/package.json +90 -90
  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/data/index.ts +0 -0
  21. package/src/cms/blocks/components/footer/index.tsx +0 -0
  22. package/src/cms/blocks/components/footer/sections/footer-app-store.tsx +0 -0
  23. package/src/cms/blocks/components/footer/sections/footer-contact.tsx +0 -2
  24. package/src/cms/blocks/components/footer/sections/footer-links.tsx +0 -0
  25. package/src/cms/blocks/components/footer/sections/footer-logo.tsx +2 -1
  26. package/src/cms/blocks/components/footer/sections/footer-social-links.tsx +0 -0
  27. package/src/cms/blocks/components/footer/styles/index.ts +0 -0
  28. package/src/cms/blocks/components/hero-carousel/index.tsx +0 -0
  29. package/src/cms/blocks/components/product-carousel/index.tsx +0 -0
  30. package/src/cms/blocks/components/shared/featured-product-card.tsx +0 -0
  31. package/src/cms/blocks/components/shared/product-category-item.tsx +0 -0
  32. package/src/cms/blocks/components/shared/product-grid.tsx +0 -0
  33. package/src/cms/blocks/components/shared/top-categories-card.tsx +0 -0
  34. package/src/cms/blocks/components/shared/top-rating-product-card.tsx +0 -0
  35. package/src/cms/blocks/icons/components/category.tsx +0 -0
  36. package/src/cms/blocks/icons/components/dotted-star.tsx +0 -0
  37. package/src/cms/blocks/icons/components/gift-box.tsx +0 -0
  38. package/src/cms/blocks/icons/components/light.tsx +0 -0
  39. package/src/cms/blocks/icons/components/new-arrival.tsx +0 -0
  40. package/src/cms/blocks/icons/components/rank-badge.tsx +0 -0
  41. package/src/components/SearchInput.stories.tsx +12 -10
  42. package/src/components/base-layout.tsx +42 -0
  43. package/src/components/categories/category-list/category-list.tsx +32 -12
  44. package/src/components/header/__tests__/header.test.tsx +0 -24
  45. package/src/components/header/components/mobile-header.tsx +16 -8
  46. package/src/components/header/header.tsx +6 -3
  47. package/src/components/mini-cart/components/bottom-actions.tsx +7 -2
  48. package/src/components/mini-cart/components/cart-item.tsx +16 -13
  49. package/src/components/mini-cart/components/empty-view.tsx +12 -6
  50. package/src/components/mini-cart/components/top-header.tsx +9 -8
  51. package/src/components/mini-cart/mini-cart-trigger.tsx +11 -5
  52. package/src/components/mini-cart/mini-cart.tsx +30 -15
  53. package/src/components/mobile-navigation/mobile-navigation-bar.tsx +6 -2
  54. package/src/components/navbar/categories.tsx +8 -2
  55. package/src/components/navbar/category-based-menu/category-based-menu.tsx +6 -2
  56. package/src/components/navbar/mega-menu/mega-menu.tsx +22 -14
  57. package/src/components/navbar/mobile-menu/mobile-menu.test.tsx +8 -11
  58. package/src/components/navbar/mobile-menu/mobile-menu.tsx +13 -11
  59. package/src/components/navbar/nav-list/nav-list.tsx +3 -2
  60. package/src/components/navbar/navbar.tsx +4 -2
  61. package/src/components/product-cards/product-card/product-card.tsx +50 -26
  62. package/src/components/product-cards/product-card-plp/product-card.tsx +37 -16
  63. package/src/components/product-cards/product-card-plp-list/product-card.tsx +38 -16
  64. package/src/components/product-cards/product-card-search/product-card.tsx +34 -13
  65. package/src/components/product-dialog/ProductDialog.stories.tsx +20 -18
  66. package/src/components/product-dialog/compound/product-dialog.tsx +7 -4
  67. package/src/components/product-dimensions/compound/dimensions-group.tsx +4 -2
  68. package/src/components/product-dimensions/compound/dimensions.tsx +15 -6
  69. package/src/components/product-quantity-buttons/compound/quantity-buttons-root.tsx +21 -13
  70. package/src/components/products-view/compound/products-grid-view.tsx +11 -4
  71. package/src/components/products-view/compound/products-list-view.tsx +11 -4
  72. package/src/components/search-bar/compound/search-bar-root.tsx +6 -4
  73. package/src/components/search-bar/compound/textfield-adornment-category.tsx +25 -8
  74. package/src/components/search-bar/hooks/use-click-outside.ts +7 -1
  75. package/src/components/search-bar/hooks/use-search.ts +3 -3
  76. package/src/components/user/index.ts +3 -0
  77. package/src/components/user/use-user-auth.ts +163 -0
  78. package/src/components/user/user.tsx +215 -0
  79. package/src/components/wishlist-dialogs/add-to-wishlist/AddToWishlist.stories.tsx +20 -15
  80. package/src/components/wishlist-dialogs/add-to-wishlist/compound/add-to-wishlist-dialog.tsx +11 -8
  81. package/src/components/wishlist-dialogs/add-to-wishlist/compound/add-to-wishlist-list.tsx +18 -3
  82. package/src/components/wishlist-dialogs/add-to-wishlist/index.tsx +0 -1
  83. package/src/components/wishlist-dialogs/create-wishlist/compound/create-wishlist-dialog.tsx +6 -2
  84. package/src/hooks/use-overflow-detect.ts +4 -4
  85. package/src/lib/graphqlRequestSdk.ts +0 -0
  86. package/src/lib/page-slugs.ts +16 -0
  87. package/src/pages/account/account-navigation.tsx +7 -14
  88. package/src/pages/account/account-routes.ts +4 -9
  89. package/src/pages/account/addresses/address-form.tsx +38 -16
  90. package/src/pages/account/addresses/address-item.tsx +18 -8
  91. package/src/pages/account/dashboard-header.tsx +20 -13
  92. package/src/pages/account/orders/order-history-filters-panel.tsx +21 -20
  93. package/src/pages/account/orders/order-history-header.tsx +14 -4
  94. package/src/pages/account/orders/order-history-root.tsx +3 -1
  95. package/src/pages/account/orders/order-history-row.tsx +12 -4
  96. package/src/pages/account/orders/order-history-search-and-filter.tsx +88 -59
  97. package/src/pages/account/orders/order-history-search-dropdown.tsx +26 -9
  98. package/src/pages/account/orders/order-history-sort.tsx +3 -1
  99. package/src/pages/account/orders/order-history-table.tsx +27 -5
  100. package/src/pages/account/orders/order-progress.tsx +12 -6
  101. package/src/pages/account/orders/order-row.tsx +18 -6
  102. package/src/pages/account/orders/order-summary.tsx +24 -8
  103. package/src/pages/account/orders/ordered-products.tsx +15 -5
  104. package/src/pages/account/profile/user-info.tsx +11 -3
  105. package/src/pages/account/table-row-skeleton.tsx +1 -1
  106. package/src/pages/account/wishlist/create-new-list.tsx +33 -9
  107. package/src/pages/account/wishlist/wishlist-item.tsx +18 -6
  108. package/src/pages/cart/cart-item.tsx +21 -7
  109. package/src/pages/cart/checkout-form.tsx +55 -22
  110. package/src/pages/cart/coupon-entry.tsx +143 -36
  111. package/src/pages/cart/estimate-shipping.tsx +45 -21
  112. package/src/pages/checkout/checkout-alt-form/checkout-form.tsx +22 -14
  113. package/src/pages/checkout/checkout-alt-form/checkout-step.tsx +37 -16
  114. package/src/pages/checkout/checkout-alt-form/steps/address/address-card.tsx +9 -3
  115. package/src/pages/checkout/checkout-alt-form/steps/address/delivery-address.tsx +84 -51
  116. package/src/pages/checkout/checkout-alt-form/steps/address/new-address-form.tsx +41 -19
  117. package/src/pages/checkout/checkout-alt-form/steps/customer-info/customer-information.tsx +17 -10
  118. package/src/pages/checkout/checkout-alt-form/steps/payment/payment-details.tsx +15 -6
  119. package/src/pages/checkout/checkout-alt-form/steps/shipping/delivery-options.tsx +6 -2
  120. package/src/pages/checkout/checkout-alt-summary/cart-item.tsx +12 -4
  121. package/src/pages/checkout/checkout-alt-summary/checkout-alt-summary.tsx +12 -5
  122. package/src/pages/checkout/checkout-alt-summary/list-item.tsx +17 -10
  123. package/src/pages/confirmation/address.tsx +6 -2
  124. package/src/pages/confirmation/confirmation-summary.tsx +12 -4
  125. package/src/pages/confirmation/ordered-products.tsx +12 -4
  126. package/src/pages/product-details/bopis/store-card/shop-card.tsx +2 -2
  127. package/src/pages/product-details/bopis/store-card/utils/getDaysResources.ts +1 -1
  128. package/src/pages/product-details/product-intro/compound/product-info.tsx +41 -18
  129. package/src/pages/product-details/product-intro/compound/product-intro-images.tsx +18 -12
  130. package/src/pages/product-details/product-intro/compound/product-intro.tsx +20 -10
  131. package/src/pages/product-details/product-intro/compound/thumbnail-with-skeleton.tsx +1 -1
  132. package/src/pages/product-details/product-tabs.tsx +20 -12
  133. package/src/pages/product-list/product-list-view.tsx +4 -2
  134. package/src/pages/product-list/search-bar.tsx +7 -7
  135. package/src/pages/product-list/side-nav.tsx +5 -3
  136. package/src/pages/quickorder/order-upload.tsx +1 -1
  137. package/src/pages/quickorder/provider.tsx +26 -14
  138. package/src/pages/quickorder/quick-order.tsx +49 -22
  139. package/src/providers/nav-provider/index.tsx +16 -11
  140. package/tsconfig.json +0 -5
  141. package/src/components/header/components/user.tsx +0 -322
  142. /package/src/components/{header → user}/__tests__/user.test.tsx +0 -0
@@ -20,7 +20,7 @@ import {
20
20
  } from "@mui/material";
21
21
  import { useTranslations } from "next-intl";
22
22
  import type React from "react";
23
- import { createContext, useContext, useEffect, useState } from "react";
23
+ import { createContext, useContext, useEffect, useMemo, useState } from "react";
24
24
 
25
25
  interface EstimateShippingContextValue {
26
26
  selectedCountry: CountryRegionInfo | null;
@@ -102,23 +102,37 @@ const EstimateShipping = ({
102
102
  setNoOptionsFound(true);
103
103
  }
104
104
  };
105
+ const contextValue = useMemo(
106
+ () => ({
107
+ selectedCountry,
108
+ setSelectedCountry,
109
+ selectedState,
110
+ setSelectedState,
111
+ zipCode,
112
+ setZipCode,
113
+ deliveryOptions,
114
+ noOptionsFound,
115
+ doDeliveryMethodLookup,
116
+ countries,
117
+ states,
118
+ }),
119
+ [
120
+ selectedCountry,
121
+ setSelectedCountry,
122
+ selectedState,
123
+ setSelectedState,
124
+ zipCode,
125
+ setZipCode,
126
+ deliveryOptions,
127
+ noOptionsFound,
128
+ doDeliveryMethodLookup,
129
+ countries,
130
+ states,
131
+ ],
132
+ );
105
133
 
106
134
  return (
107
- <EstimateShippingContext.Provider
108
- value={{
109
- selectedCountry,
110
- setSelectedCountry,
111
- selectedState,
112
- setSelectedState,
113
- zipCode,
114
- setZipCode,
115
- deliveryOptions,
116
- noOptionsFound,
117
- doDeliveryMethodLookup,
118
- countries,
119
- states,
120
- }}
121
- >
135
+ <EstimateShippingContext.Provider value={contextValue}>
122
136
  <Box>
123
137
  <Span fontWeight={600} mb={2} display="block">
124
138
  {t("estimatesLabel")}
@@ -130,7 +144,7 @@ const EstimateShipping = ({
130
144
  };
131
145
 
132
146
  // Subcomponents
133
- EstimateShipping.Country = () => {
147
+ const EstimateShippingCountry = () => {
134
148
  const ctx = useContext(EstimateShippingContext);
135
149
  const t = useTranslations("Cart.Sidebar");
136
150
  if (!ctx) return null;
@@ -147,7 +161,9 @@ EstimateShipping.Country = () => {
147
161
  );
148
162
  };
149
163
 
150
- EstimateShipping.State = () => {
164
+ EstimateShipping.Country = EstimateShippingCountry;
165
+
166
+ const EstimateShippingState = () => {
151
167
  const ctx = useContext(EstimateShippingContext);
152
168
  const t = useTranslations("Cart.Sidebar");
153
169
  if (!ctx) return null;
@@ -172,7 +188,9 @@ EstimateShipping.State = () => {
172
188
  );
173
189
  };
174
190
 
175
- EstimateShipping.ZipCode = () => {
191
+ EstimateShipping.State = EstimateShippingState;
192
+
193
+ const EstimateShippingZipCode = () => {
176
194
  const ctx = useContext(EstimateShippingContext);
177
195
  const t = useTranslations("Cart.Sidebar");
178
196
  if (!ctx) return null;
@@ -189,7 +207,9 @@ EstimateShipping.ZipCode = () => {
189
207
  );
190
208
  };
191
209
 
192
- EstimateShipping.Submit = () => {
210
+ EstimateShipping.ZipCode = EstimateShippingZipCode;
211
+
212
+ const EstimateShippingSubmit = () => {
193
213
  const ctx = useContext(EstimateShippingContext);
194
214
  const t = useTranslations("Cart.Sidebar");
195
215
  if (!ctx) return null;
@@ -207,7 +227,9 @@ EstimateShipping.Submit = () => {
207
227
  );
208
228
  };
209
229
 
210
- EstimateShipping.Options = () => {
230
+ EstimateShipping.Submit = EstimateShippingSubmit;
231
+
232
+ const EstimateShippingOptions = () => {
211
233
  const ctx = useContext(EstimateShippingContext);
212
234
  const { formatCurrency } = useCurrencyFormatter();
213
235
  const t = useTranslations("Cart.Sidebar");
@@ -241,4 +263,6 @@ EstimateShipping.Options = () => {
241
263
  );
242
264
  };
243
265
 
266
+ EstimateShipping.Options = EstimateShippingOptions;
267
+
244
268
  export default EstimateShipping;
@@ -18,6 +18,7 @@ import React, {
18
18
  type ReactNode,
19
19
  useContext,
20
20
  useEffect,
21
+ useMemo,
21
22
  useRef,
22
23
  useState,
23
24
  } from "react";
@@ -51,7 +52,6 @@ interface StepProps<T extends object> {
51
52
  component: React.FC<CheckoutStepComponentProps & T>;
52
53
  componentProps?: T;
53
54
  stepNumber?: number;
54
- isLastStep?: boolean;
55
55
  }
56
56
  const CheckoutForm = ({ cart, children }: CheckoutFormProps) => {
57
57
  const t = useTranslations("Checkout");
@@ -131,18 +131,20 @@ const CheckoutForm = ({ cart, children }: CheckoutFormProps) => {
131
131
  setIsSubmitting(false);
132
132
  }
133
133
  };
134
+ const contextValue = useMemo(
135
+ () => ({
136
+ cart,
137
+ activeStep,
138
+ setActiveStep,
139
+ isSubmitting,
140
+ placeOrder,
141
+ totalSteps,
142
+ }),
143
+ [cart, activeStep, setActiveStep, isSubmitting, placeOrder, totalSteps],
144
+ );
134
145
 
135
146
  return (
136
- <CheckoutContext.Provider
137
- value={{
138
- cart,
139
- activeStep,
140
- setActiveStep,
141
- isSubmitting,
142
- placeOrder,
143
- totalSteps,
144
- }}
145
- >
147
+ <CheckoutContext.Provider value={contextValue}>
146
148
  <Box>
147
149
  {React.Children.map(children, (child) =>
148
150
  React.isValidElement(child) && child.type === CheckoutForm.Steps
@@ -159,7 +161,7 @@ const CheckoutForm = ({ cart, children }: CheckoutFormProps) => {
159
161
  );
160
162
  };
161
163
 
162
- CheckoutForm.Steps = ({
164
+ const CheckoutFormSteps = ({
163
165
  children,
164
166
  setTotalSteps,
165
167
  }: {
@@ -185,7 +187,9 @@ CheckoutForm.Steps = ({
185
187
  );
186
188
  };
187
189
 
188
- CheckoutForm.Step = <T extends object>({
190
+ CheckoutForm.Steps = CheckoutFormSteps;
191
+
192
+ const CheckoutFormStep = <T extends object>({
189
193
  title,
190
194
  stepNumber,
191
195
  component: Component,
@@ -218,7 +222,9 @@ CheckoutForm.Step = <T extends object>({
218
222
  );
219
223
  };
220
224
 
221
- CheckoutForm.Footer = () => {
225
+ CheckoutForm.Step = CheckoutFormStep;
226
+
227
+ const CheckoutFormFooter = () => {
222
228
  const ctx = useContext(CheckoutContext);
223
229
  const t = useTranslations("Checkout");
224
230
  if (!ctx) return null;
@@ -244,4 +250,6 @@ CheckoutForm.Footer = () => {
244
250
  );
245
251
  };
246
252
 
253
+ CheckoutForm.Footer = CheckoutFormFooter;
254
+
247
255
  export default CheckoutForm;
@@ -6,7 +6,7 @@ import StepHeading from "@evenicanpm/storefront-core/src/pages/checkout/checkout
6
6
  import { Button, useTheme } from "@mui/material";
7
7
  import Grid from "@mui/material/Grid2";
8
8
  import { useTranslations } from "next-intl";
9
- import React, { createContext, useContext } from "react";
9
+ import React, { createContext, useContext, useMemo } from "react";
10
10
 
11
11
  export type StepViewState = "active" | "preview" | "pending";
12
12
  export type StepSubmissionState = "idle" | "submitting" | "error";
@@ -61,18 +61,33 @@ const CheckoutStep = ({
61
61
  return "pending";
62
62
  };
63
63
 
64
- const ctx: StepContextType = {
65
- title,
66
- stepNumber,
67
- activeStep,
68
- setActiveStep,
69
- cart,
70
- hasCompleted,
71
- stepSubmissionState,
72
- setStepSubmissionState,
73
- stepViewState: getStepViewState(),
74
- handleStepCompletion,
75
- };
64
+ const stepViewState = getStepViewState();
65
+ const ctx: StepContextType = useMemo(
66
+ () => ({
67
+ title,
68
+ stepNumber,
69
+ activeStep,
70
+ setActiveStep,
71
+ cart,
72
+ hasCompleted,
73
+ stepSubmissionState,
74
+ setStepSubmissionState,
75
+ stepViewState,
76
+ handleStepCompletion,
77
+ }),
78
+ [
79
+ title,
80
+ stepNumber,
81
+ activeStep,
82
+ setActiveStep,
83
+ cart,
84
+ hasCompleted,
85
+ stepSubmissionState,
86
+ setStepSubmissionState,
87
+ stepViewState,
88
+ handleStepCompletion,
89
+ ],
90
+ );
76
91
 
77
92
  return (
78
93
  <StepContext.Provider value={ctx}>
@@ -90,7 +105,7 @@ const CheckoutStep = ({
90
105
  );
91
106
  };
92
107
 
93
- CheckoutStep.Header = () => {
108
+ const CheckoutStepHeader = () => {
94
109
  const ctx = useContext(StepContext);
95
110
  const t = useTranslations("Checkout.Steps");
96
111
  if (!ctx) return null;
@@ -117,7 +132,9 @@ CheckoutStep.Header = () => {
117
132
  );
118
133
  };
119
134
 
120
- CheckoutStep.Body = <P extends object = object>({
135
+ CheckoutStep.Header = CheckoutStepHeader;
136
+
137
+ const CheckoutStepBody = <P extends object = object>({
121
138
  component: Component,
122
139
  componentProps,
123
140
  }: {
@@ -147,7 +164,9 @@ CheckoutStep.Body = <P extends object = object>({
147
164
  );
148
165
  };
149
166
 
150
- CheckoutStep.Actions = () => {
167
+ CheckoutStep.Body = CheckoutStepBody;
168
+
169
+ const CheckoutStepActions = () => {
151
170
  const ctx = useContext(StepContext);
152
171
  const t = useTranslations("Checkout.Steps");
153
172
  if (!ctx) return null;
@@ -168,4 +187,6 @@ CheckoutStep.Actions = () => {
168
187
  );
169
188
  };
170
189
 
190
+ CheckoutStep.Actions = CheckoutStepActions;
191
+
171
192
  export default CheckoutStep;
@@ -64,7 +64,7 @@ const AddressCard = ({
64
64
  );
65
65
  };
66
66
 
67
- AddressCard.Controls = () => {
67
+ const AddressCardControls = () => {
68
68
  const ctx = React.useContext(AddressCardContext);
69
69
  if (!ctx) return null;
70
70
 
@@ -102,7 +102,9 @@ AddressCard.Controls = () => {
102
102
  );
103
103
  };
104
104
 
105
- AddressCard.Header = () => {
105
+ AddressCard.Controls = AddressCardControls;
106
+
107
+ const AddressCardHeader = () => {
106
108
  const ctx = React.useContext(AddressCardContext);
107
109
  if (!ctx) return null;
108
110
 
@@ -110,7 +112,9 @@ AddressCard.Header = () => {
110
112
  return <H6 mb={0.5}>{address.Name}</H6>;
111
113
  };
112
114
 
113
- AddressCard.Body = () => {
115
+ AddressCard.Header = AddressCardHeader;
116
+
117
+ const AddressCardBody = () => {
114
118
  const ctx = React.useContext(AddressCardContext);
115
119
  if (!ctx) return null;
116
120
 
@@ -130,4 +134,6 @@ AddressCard.Body = () => {
130
134
  );
131
135
  };
132
136
 
137
+ AddressCard.Body = AddressCardBody;
138
+
133
139
  export default AddressCard;
@@ -27,6 +27,7 @@ import React, {
27
27
  useCallback,
28
28
  useContext,
29
29
  useEffect,
30
+ useMemo,
30
31
  useState,
31
32
  } from "react";
32
33
 
@@ -246,6 +247,74 @@ const DeliveryAddress: DeliveryAddressCompound = ({
246
247
  }
247
248
  }, [stepSubmissionState, handleStepCompletion, setStepSubmissionState]);
248
249
 
250
+ // modal helpers (moved before early returns)
251
+ const openNew = () => {
252
+ setEditAddress(undefined);
253
+ setModalOpen(true);
254
+ };
255
+ const openEdit = (addr: Address) => {
256
+ setEditAddress({ ...addr });
257
+ setModalOpen(true);
258
+ };
259
+
260
+ const closeModal = () => {
261
+ setModalOpen(false);
262
+ setEditAddress(undefined);
263
+ };
264
+
265
+ // context value (moved before early returns)
266
+ const cartShippingAddressId = cart.ShippingAddress?.Id;
267
+ let visibleAddresses = addressList;
268
+
269
+ if (useDropDown) {
270
+ if (!isGuest) {
271
+ visibleAddresses = addressList.filter(
272
+ (a) => a.RecordId === selectedAddressID,
273
+ );
274
+ }
275
+ }
276
+
277
+ const ctx: DeliveryAddressContextValue = useMemo(
278
+ () => ({
279
+ t,
280
+ countries,
281
+ addressList,
282
+ visibleAddresses,
283
+ isGuest,
284
+ cartShippingAddressId,
285
+ showDropDown,
286
+ title,
287
+ selectedAddressID,
288
+ modalOpen,
289
+ editAddress,
290
+ openNew,
291
+ openEdit,
292
+ closeModal,
293
+ pickAddress,
294
+ handleSaveAddress,
295
+ handleDelete,
296
+ }),
297
+ [
298
+ t,
299
+ countries,
300
+ addressList,
301
+ visibleAddresses,
302
+ isGuest,
303
+ cartShippingAddressId,
304
+ showDropDown,
305
+ title,
306
+ selectedAddressID,
307
+ modalOpen,
308
+ editAddress,
309
+ openNew,
310
+ openEdit,
311
+ closeModal,
312
+ pickAddress,
313
+ handleSaveAddress,
314
+ handleDelete,
315
+ ],
316
+ );
317
+
249
318
  // step state guards
250
319
  if (stepViewState === "pending" || !countries) return null;
251
320
 
@@ -267,52 +336,6 @@ const DeliveryAddress: DeliveryAddressCompound = ({
267
336
  );
268
337
  }
269
338
 
270
- let visibleAddresses = addressList;
271
-
272
- if (useDropDown) {
273
- if (!isGuest) {
274
- visibleAddresses = addressList.filter(
275
- (a) => a.RecordId === selectedAddressID,
276
- );
277
- }
278
- }
279
-
280
- // modal helpers
281
- const openNew = () => {
282
- setEditAddress(undefined);
283
- setModalOpen(true);
284
- };
285
- const openEdit = (addr: Address) => {
286
- setEditAddress({ ...addr });
287
- setModalOpen(true);
288
- };
289
-
290
- const closeModal = () => {
291
- setModalOpen(false);
292
- setEditAddress(undefined);
293
- };
294
-
295
- // context value
296
- const ctx: DeliveryAddressContextValue = {
297
- t,
298
- countries,
299
- addressList,
300
- visibleAddresses,
301
- isGuest,
302
- cartShippingAddressId: cart.ShippingAddress?.Id,
303
- showDropDown,
304
- title,
305
- selectedAddressID,
306
- modalOpen,
307
- editAddress,
308
- openNew,
309
- openEdit,
310
- closeModal,
311
- pickAddress,
312
- handleSaveAddress,
313
- handleDelete,
314
- };
315
-
316
339
  // default layout (identical behavior)
317
340
  const defaultLayout = (
318
341
  <>
@@ -334,7 +357,7 @@ const DeliveryAddress: DeliveryAddressCompound = ({
334
357
 
335
358
  // ---------- Subcomponents ----------
336
359
 
337
- DeliveryAddress.Modal = () => {
360
+ const DeliveryAddressModal = () => {
338
361
  const ctx = useContext(DeliveryAddressCtx);
339
362
  if (!ctx) return null;
340
363
  const {
@@ -359,8 +382,10 @@ DeliveryAddress.Modal = () => {
359
382
  );
360
383
  };
361
384
 
385
+ DeliveryAddress.Modal = DeliveryAddressModal;
386
+
362
387
  // (configurable via `useDropDown`/`showDropDown`)
363
- DeliveryAddress.Dropdown = () => {
388
+ const DeliveryAddressDropdown = () => {
364
389
  const ctx = useContext(DeliveryAddressCtx);
365
390
  if (!ctx) return null;
366
391
  const { showDropDown, title, addressList, pickAddress, handleSaveAddress } =
@@ -398,8 +423,10 @@ DeliveryAddress.Dropdown = () => {
398
423
  );
399
424
  };
400
425
 
426
+ DeliveryAddress.Dropdown = DeliveryAddressDropdown;
427
+
401
428
  // List (can accept a renderItem override)
402
- DeliveryAddress.List = ({ renderItem }: ListProps) => {
429
+ const DeliveryAddressList = ({ renderItem }: ListProps) => {
403
430
  const ctx = useContext(DeliveryAddressCtx);
404
431
  if (!ctx) return null;
405
432
  const { visibleAddresses } = ctx;
@@ -419,8 +446,10 @@ DeliveryAddress.List = ({ renderItem }: ListProps) => {
419
446
  );
420
447
  };
421
448
 
449
+ DeliveryAddress.List = DeliveryAddressList;
450
+
422
451
  // Item → one address card (default uses AddressCard with Controls/Header/Body)
423
- DeliveryAddress.Card = ({ address, children }: ItemProps) => {
452
+ const DeliveryAddressCard = ({ address, children }: ItemProps) => {
424
453
  const ctx = useContext(DeliveryAddressCtx);
425
454
  if (!ctx) return null;
426
455
  const { cartShippingAddressId, openEdit, handleSaveAddress, handleDelete } =
@@ -445,7 +474,9 @@ DeliveryAddress.Card = ({ address, children }: ItemProps) => {
445
474
  );
446
475
  };
447
476
 
448
- DeliveryAddress.NewCard = () => {
477
+ DeliveryAddress.Card = DeliveryAddressCard;
478
+
479
+ const DeliveryAddressNewCard = () => {
449
480
  const ctx = useContext(DeliveryAddressCtx);
450
481
  if (!ctx) return null;
451
482
  const { t, openNew } = ctx;
@@ -474,4 +505,6 @@ DeliveryAddress.NewCard = () => {
474
505
  );
475
506
  };
476
507
 
508
+ DeliveryAddress.NewCard = DeliveryAddressNewCard;
509
+
477
510
  export default DeliveryAddress;
@@ -176,21 +176,35 @@ const NewAddressForm = ({
176
176
  handleClose();
177
177
  };
178
178
 
179
- const ctx: NewAddressFormCtx = {
180
- form,
181
- countries,
182
- states: Array.isArray(states)
183
- ? states.map((s) => ({
184
- StateName: s.StateName ?? "",
185
- StateId: s.StateId ?? "",
186
- }))
187
- : [],
188
- selectedCountry,
189
- setSelectedCountry,
190
- errors,
191
- t,
192
- onCancel: handleClose,
193
- };
179
+ const mappedStates = Array.isArray(states)
180
+ ? states.map((s) => ({
181
+ StateName: s.StateName ?? "",
182
+ StateId: s.StateId ?? "",
183
+ }))
184
+ : [];
185
+
186
+ const ctx: NewAddressFormCtx = React.useMemo(
187
+ () => ({
188
+ form,
189
+ countries,
190
+ states: mappedStates,
191
+ selectedCountry,
192
+ setSelectedCountry,
193
+ errors,
194
+ t,
195
+ onCancel: handleClose,
196
+ }),
197
+ [
198
+ form,
199
+ countries,
200
+ mappedStates,
201
+ selectedCountry,
202
+ setSelectedCountry,
203
+ errors,
204
+ t,
205
+ handleClose,
206
+ ],
207
+ );
194
208
 
195
209
  const hasCustomChildren = React.Children.count(children) > 0;
196
210
 
@@ -231,7 +245,7 @@ const NewAddressForm = ({
231
245
  };
232
246
 
233
247
  // Generic text field
234
- NewAddressForm.Field = ({
248
+ const NewAddressFormField = ({
235
249
  name,
236
250
  label,
237
251
  }: {
@@ -257,7 +271,9 @@ NewAddressForm.Field = ({
257
271
  );
258
272
  };
259
273
 
260
- NewAddressForm.CountrySelect = () => {
274
+ NewAddressForm.Field = NewAddressFormField;
275
+
276
+ const NewAddressFormCountrySelect = () => {
261
277
  const ctx = React.useContext(FormContext);
262
278
  if (!ctx) return null;
263
279
  const { countries, form, setSelectedCountry, t, errors } = ctx;
@@ -297,7 +313,9 @@ NewAddressForm.CountrySelect = () => {
297
313
  );
298
314
  };
299
315
 
300
- NewAddressForm.StateSelect = () => {
316
+ NewAddressForm.CountrySelect = NewAddressFormCountrySelect;
317
+
318
+ const NewAddressFormStateSelect = () => {
301
319
  const ctx = React.useContext(FormContext);
302
320
  if (!ctx) return null;
303
321
  const { states, form, t, errors } = ctx;
@@ -326,7 +344,9 @@ NewAddressForm.StateSelect = () => {
326
344
  );
327
345
  };
328
346
 
329
- NewAddressForm.Actions = () => {
347
+ NewAddressForm.StateSelect = NewAddressFormStateSelect;
348
+
349
+ const NewAddressFormActions = () => {
330
350
  const ctx = React.useContext(FormContext);
331
351
  if (!ctx) return null;
332
352
  const { t, onCancel } = ctx;
@@ -343,4 +363,6 @@ NewAddressForm.Actions = () => {
343
363
  );
344
364
  };
345
365
 
366
+ NewAddressForm.Actions = NewAddressFormActions;
367
+
346
368
  export default NewAddressForm;
@@ -7,7 +7,7 @@ import Box from "@mui/material/Box";
7
7
  import Grid from "@mui/material/Grid2";
8
8
  import TextField from "@mui/material/TextField";
9
9
  import { useTranslations } from "next-intl";
10
- import React, { createContext, useContext } from "react";
10
+ import React, { createContext, useContext, useMemo } from "react";
11
11
  import type { SubmitHandler, UseFormReturn } from "react-hook-form";
12
12
  import { useForm } from "react-hook-form";
13
13
  import * as yup from "yup";
@@ -97,13 +97,16 @@ const CustomerInformation = (
97
97
  }
98
98
  }, [formState.errors, formState.isSubmitted, setStepSubmissionState]);
99
99
 
100
- const ctx: CustomerCtx = {
101
- ...props,
102
- email: email ?? null,
103
- cartId,
104
- form,
105
- handleSave,
106
- };
100
+ const ctx: CustomerCtx = useMemo(
101
+ () => ({
102
+ ...props,
103
+ email: email ?? null,
104
+ cartId,
105
+ form,
106
+ handleSave,
107
+ }),
108
+ [props, email, cartId, form, handleSave],
109
+ );
107
110
 
108
111
  return (
109
112
  <CustomerContext.Provider value={ctx}>
@@ -117,7 +120,7 @@ const CustomerInformation = (
117
120
  );
118
121
  };
119
122
 
120
- CustomerInformation.Preview = () => {
123
+ const CustomerInformationPreview = () => {
121
124
  const ctx = useContext(CustomerContext);
122
125
  const t = useTranslations("Checkout.CustomerInformation");
123
126
  if (!ctx) return null;
@@ -131,7 +134,9 @@ CustomerInformation.Preview = () => {
131
134
  );
132
135
  };
133
136
 
134
- CustomerInformation.Form = () => {
137
+ CustomerInformation.Preview = CustomerInformationPreview;
138
+
139
+ const CustomerInformationForm = () => {
135
140
  const ctx = useContext(CustomerContext);
136
141
  const t = useTranslations("Checkout.CustomerInformation");
137
142
  if (!ctx) return null;
@@ -155,4 +160,6 @@ CustomerInformation.Form = () => {
155
160
  );
156
161
  };
157
162
 
163
+ CustomerInformation.Form = CustomerInformationForm;
164
+
158
165
  export default CustomerInformation;