@magento/peregrine 12.4.0-beta.1 → 12.5.0-alpha.2

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 (64) hide show
  1. package/lib/Apollo/links/authLink.js +19 -0
  2. package/lib/Apollo/links/errorLink.js +64 -0
  3. package/lib/Apollo/links/gqlCacheLink.js +63 -0
  4. package/lib/Apollo/links/index.js +66 -0
  5. package/lib/Apollo/links/mutationQueueLink.js +5 -0
  6. package/lib/Apollo/links/retryLink.js +17 -0
  7. package/lib/Apollo/links/storeLink.js +22 -0
  8. package/lib/Apollo/magentoGqlCacheLink.js +3 -59
  9. package/lib/PeregrineContextProvider/peregrineContextProvider.js +2 -0
  10. package/lib/context/eventing.js +57 -0
  11. package/lib/hooks/useDelayedTransition.js +1 -1
  12. package/lib/talons/AccountInformationPage/useAccountInformationPage.js +15 -1
  13. package/lib/talons/Adapter/useAdapter.js +23 -200
  14. package/lib/talons/AddToCartDialog/addToCartDialog.gql.js +8 -0
  15. package/lib/talons/AddToCartDialog/useAddToCartDialog.js +46 -2
  16. package/lib/talons/AddressBookPage/useAddressBookPage.js +52 -17
  17. package/lib/talons/AuthModal/useAuthModal.js +12 -2
  18. package/lib/talons/Breadcrumbs/useBreadcrumbs.js +7 -2
  19. package/lib/talons/CartPage/ProductListing/EditModal/__fixtures__/configurableProduct.js +39 -4
  20. package/lib/talons/CartPage/ProductListing/EditModal/useProductForm.js +47 -3
  21. package/lib/talons/CartPage/ProductListing/productListingFragments.gql.js +6 -0
  22. package/lib/talons/CartPage/ProductListing/useProduct.js +49 -3
  23. package/lib/talons/CartPage/useCartPage.js +15 -0
  24. package/lib/talons/CheckoutPage/ItemsReview/itemsReviewFragments.gql.js +12 -14
  25. package/lib/talons/CheckoutPage/OrderConfirmationPage/orderConfirmationPageFragments.gql.js +21 -1
  26. package/lib/talons/CheckoutPage/OrderConfirmationPage/useCreateAccount.js +15 -1
  27. package/lib/talons/CheckoutPage/PaymentInformation/useEditModal.js +10 -1
  28. package/lib/talons/CheckoutPage/PaymentInformation/usePaymentInformation.js +14 -0
  29. package/lib/talons/CheckoutPage/ShippingInformation/AddressForm/useCustomerForm.js +38 -1
  30. package/lib/talons/CheckoutPage/ShippingInformation/AddressForm/useGuestForm.js +15 -1
  31. package/lib/talons/CheckoutPage/ShippingInformation/useShippingInformation.js +13 -0
  32. package/lib/talons/CheckoutPage/ShippingMethod/useShippingMethod.js +28 -6
  33. package/lib/talons/CheckoutPage/checkoutPage.gql.js +3 -0
  34. package/lib/talons/CheckoutPage/useCheckoutPage.js +79 -3
  35. package/lib/talons/Cms/useCmsPage.js +14 -0
  36. package/lib/talons/CreateAccount/useCreateAccount.js +15 -1
  37. package/lib/talons/FilterModal/helpers.js +8 -2
  38. package/lib/talons/FilterModal/useFilterModal.js +1 -0
  39. package/lib/talons/FilterSidebar/useFilterSidebar.js +1 -0
  40. package/lib/talons/Gallery/useAddToCartButton.js +24 -12
  41. package/lib/talons/Gallery/useGalleryItem.js +85 -1
  42. package/lib/talons/Header/storeSwitcher.gql.js +1 -16
  43. package/lib/talons/Header/useAccountMenu.js +21 -2
  44. package/lib/talons/Header/useStoreSwitcher.js +40 -93
  45. package/lib/talons/MagentoRoute/useMagentoRoute.js +7 -7
  46. package/lib/talons/MiniCart/ProductList/productListFragments.gql.js +4 -0
  47. package/lib/talons/MiniCart/useMiniCart.js +46 -3
  48. package/lib/talons/ProductFullDetail/useProductFullDetail.js +37 -9
  49. package/lib/talons/RootComponents/Category/categoryContent.gql.js +2 -0
  50. package/lib/talons/RootComponents/Category/categoryFragments.gql.js +3 -0
  51. package/lib/talons/RootComponents/Category/useCategory.js +4 -1
  52. package/lib/talons/RootComponents/Category/useCategoryContent.js +35 -13
  53. package/lib/talons/RootComponents/Product/productDetailFragment.gql.js +6 -0
  54. package/lib/talons/RootComponents/Product/useProduct.js +27 -0
  55. package/lib/talons/SearchBar/index.js +1 -0
  56. package/lib/talons/SearchBar/useAutocomplete.js +18 -1
  57. package/lib/talons/SearchBar/useSuggestedProduct.js +99 -0
  58. package/lib/talons/SearchPage/searchPage.gql.js +3 -0
  59. package/lib/talons/SearchPage/useSearchPage.js +56 -28
  60. package/lib/talons/SignIn/useSignIn.js +19 -2
  61. package/lib/talons/WishlistPage/useWishlistItem.js +36 -1
  62. package/lib/talons/WishlistPage/wishlistItemFragments.gql.js +3 -0
  63. package/lib/util/makeUrl.js +1 -1
  64. package/package.json +5 -4
@@ -1,194 +1,41 @@
1
- import { ApolloLink, createHttpLink } from '@apollo/client';
1
+ import { ApolloLink } from '@apollo/client';
2
2
  import { InMemoryCache } from '@apollo/client/cache';
3
3
  import { ApolloClient } from '@apollo/client/core';
4
- import { setContext } from '@apollo/client/link/context';
5
- import { onError } from '@apollo/client/link/error';
6
- import { RetryLink } from '@apollo/client/link/retry';
7
4
  import { CachePersistor } from 'apollo-cache-persist';
8
- import getWithPath from 'lodash.get';
9
- import setWithPath from 'lodash.set';
10
5
  import { useCallback, useEffect, useMemo, useState } from 'react';
11
6
 
12
- import MutationQueueLink from '@adobe/apollo-link-mutation-queue';
13
7
  import attachClient from '@magento/peregrine/lib/Apollo/attachClientToStore';
14
8
  import { clearCartDataFromCache } from '@magento/peregrine/lib/Apollo/clearCartDataFromCache';
15
9
  import { clearCustomerDataFromCache } from '@magento/peregrine/lib/Apollo/clearCustomerDataFromCache';
16
10
  import { CACHE_PERSIST_PREFIX } from '@magento/peregrine/lib/Apollo/constants';
11
+ import getLinks from '@magento/peregrine/lib/Apollo/links';
17
12
  import typePolicies from '@magento/peregrine/lib/Apollo/policies';
18
- import MagentoGQLCacheLink from '@magento/peregrine/lib/Apollo/magentoGqlCacheLink';
19
13
  import { BrowserPersistence } from '@magento/peregrine/lib/util';
20
- import shrinkQuery from '@magento/peregrine/lib/util/shrinkQuery';
14
+
15
+ const isServer = !globalThis.document;
16
+ const storage = new BrowserPersistence();
17
+ const urlHasStoreCode = process.env.USE_STORE_CODE_IN_URL === 'true';
21
18
 
22
19
  export const useAdapter = props => {
23
- const { origin, store, styles } = props;
20
+ const { apiUrl, configureLinks, origin, store, styles } = props;
24
21
  const storeCode = storage.getItem('store_view_code') || STORE_VIEW_CODE;
25
22
  const basename = urlHasStoreCode ? `/${storeCode}` : null;
26
23
  const [initialized, setInitialized] = useState(false);
27
24
 
28
- const apiBase = useMemo(() => new URL('/graphql', origin).toString(), [
29
- origin
30
- ]);
31
-
32
- const authLink = useMemo(
33
- () =>
34
- setContext((_, { headers }) => {
35
- // get the authentication token from local storage if it exists.
36
- const token = storage.getItem('signin_token');
37
-
38
- // return the headers to the context so httpLink can read them
39
- return {
40
- headers: {
41
- ...headers,
42
- authorization: token ? `Bearer ${token}` : ''
43
- }
44
- };
45
- }),
46
- []
47
- );
48
-
49
- const errorLink = useMemo(
50
- () =>
51
- onError(handler => {
52
- const { graphQLErrors, networkError, response } = handler;
53
-
54
- if (graphQLErrors) {
55
- graphQLErrors.forEach(({ message, locations, path }) =>
56
- console.log(
57
- `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
58
- )
59
- );
60
- }
61
-
62
- if (networkError) {
63
- console.log(`[Network error]: ${networkError}`);
64
- }
65
-
66
- if (response) {
67
- const { data, errors } = response;
68
- let pathToCartItems;
69
-
70
- // It's within the GraphQL spec to receive data and errors, where
71
- // errors are merely informational and not intended to block. Almost
72
- // all existing components were not built with this in mind, so we
73
- // build special handling of this error message so we can deal with
74
- // it at the time we deem appropriate.
75
- errors.forEach(({ message, path }, index) => {
76
- if (
77
- message ===
78
- 'Some of the products are out of stock.' ||
79
- message ===
80
- 'There are no source items with the in stock status' ||
81
- message === 'The requested qty is not available'
82
- ) {
83
- if (!pathToCartItems) {
84
- pathToCartItems = path.slice(0, -1);
85
- }
86
-
87
- // Set the error to null to be cleaned up later
88
- response.errors[index] = null;
89
- }
90
- });
91
-
92
- // indicator that we have some cleanup to perform on the response
93
- if (pathToCartItems) {
94
- const cartItems = getWithPath(data, pathToCartItems);
95
- const filteredCartItems = cartItems.filter(
96
- cartItem => cartItem !== null
97
- );
98
- setWithPath(data, pathToCartItems, filteredCartItems);
99
-
100
- const filteredErrors = response.errors.filter(
101
- error => error !== null
102
- );
103
- // If all errors were stock related and set to null, reset the error response so it doesn't throw
104
- response.errors = filteredErrors.length
105
- ? filteredErrors
106
- : undefined;
107
- }
108
- }
109
- }),
110
- []
111
- );
112
-
113
- // Warning: `useGETForQueries` risks exceeding URL length limits.
114
- // These limits in practice are typically set at or behind where TLS
115
- // terminates. For Magento Cloud & Fastly, 8kb is the maximum by default.
116
- // https://docs.fastly.com/en/guides/resource-limits#request-and-response-limits
117
- const httpLink = useMemo(
118
- () =>
119
- createHttpLink({
120
- fetch: customFetchToShrinkQuery,
121
- useGETForQueries: true,
122
- uri: apiBase
123
- }),
124
- [apiBase]
125
- );
126
-
127
- const mutationQueueLink = useMemo(() => new MutationQueueLink(), []);
128
-
129
- const retryLink = useMemo(
130
- () =>
131
- new RetryLink({
132
- delay: {
133
- initial: 300,
134
- max: Infinity,
135
- jitter: true
136
- },
137
- attempts: {
138
- max: 5,
139
- retryIf: error => error && !isServer && navigator.onLine
140
- }
141
- }),
142
- []
25
+ const apiBase = useMemo(
26
+ () => apiUrl || new URL('/graphql', origin).toString(),
27
+ [apiUrl, origin]
143
28
  );
144
29
 
145
- const storeLink = useMemo(
146
- () =>
147
- setContext((_, { headers }) => {
148
- const storeCurrency =
149
- storage.getItem('store_view_currency') || null;
150
- const storeCode =
151
- storage.getItem('store_view_code') || STORE_VIEW_CODE;
30
+ const apolloLink = useMemo(() => {
31
+ let links = getLinks(apiBase);
152
32
 
153
- // return the headers to the context so httpLink can read them
154
- return {
155
- headers: {
156
- ...headers,
157
- store: storeCode,
158
- ...(storeCurrency && {
159
- 'Content-Currency': storeCurrency
160
- })
161
- }
162
- };
163
- }),
164
- []
165
- );
33
+ if (configureLinks) {
34
+ links = configureLinks(links, apiBase);
35
+ }
166
36
 
167
- const magentoGqlCacheLink = useMemo(() => new MagentoGQLCacheLink(), []);
168
-
169
- const apolloLink = useMemo(
170
- () =>
171
- ApolloLink.from([
172
- // preserve this array order, it's important
173
- // as the terminating link, `httpLink` must be last
174
- mutationQueueLink,
175
- retryLink,
176
- authLink,
177
- magentoGqlCacheLink,
178
- storeLink,
179
- errorLink,
180
- httpLink
181
- ]),
182
- [
183
- authLink,
184
- errorLink,
185
- httpLink,
186
- magentoGqlCacheLink,
187
- mutationQueueLink,
188
- retryLink,
189
- storeLink
190
- ]
191
- );
37
+ return ApolloLink.from(Array.from(links.values()));
38
+ }, [apiBase, configureLinks]);
192
39
 
193
40
  const createApolloClient = useCallback((cache, link) => {
194
41
  return new ApolloClient({
@@ -222,10 +69,10 @@ export const useAdapter = props => {
222
69
 
223
70
  // Clear other stores
224
71
  for (const store of AVAILABLE_STORE_VIEWS) {
225
- if (store.code !== storeCode) {
72
+ if (store.store_code !== storeCode) {
226
73
  // Get saved data directly from local storage
227
74
  const existingStorePersistor = globalThis.localStorage.getItem(
228
- `${CACHE_PERSIST_PREFIX}-${store.code}`
75
+ `${CACHE_PERSIST_PREFIX}-${store.store_code}`
229
76
  );
230
77
 
231
78
  // Make sure we have data available
@@ -245,7 +92,10 @@ export const useAdapter = props => {
245
92
 
246
93
  storeClient.persistor = isServer
247
94
  ? null
248
- : createCachePersistor(store.code, storeCache);
95
+ : createCachePersistor(
96
+ store.store_code,
97
+ storeCache
98
+ );
249
99
 
250
100
  // Clear other store
251
101
  if (cacheType === 'cart') {
@@ -320,10 +170,6 @@ export const useAdapter = props => {
320
170
  };
321
171
  };
322
172
 
323
- const isServer = !globalThis.document;
324
- const storage = new BrowserPersistence();
325
- const urlHasStoreCode = process.env.USE_STORE_CODE_IN_URL === 'true';
326
-
327
173
  /**
328
174
  * To improve initial load time, create an apollo cache object as soon as
329
175
  * this module is executed, since it doesn't depend on any component props.
@@ -334,26 +180,3 @@ const preInstantiatedCache = new InMemoryCache({
334
180
  possibleTypes: POSSIBLE_TYPES,
335
181
  typePolicies
336
182
  });
337
-
338
- /**
339
- * Intercept and shrink URLs from GET queries.
340
- *
341
- * Using GET makes it possible to use edge caching in Magento Cloud, but risks
342
- * exceeding URL limits with default usage of Apollo's http link.
343
- *
344
- * `shrinkQuery` encodes the URL in a more efficient way.
345
- *
346
- * @param {*} uri
347
- * @param {*} options
348
- */
349
- const customFetchToShrinkQuery = (uri, options) => {
350
- // TODO: add `ismorphic-fetch` or equivalent to avoid this error
351
- if (typeof globalThis.fetch !== 'function') {
352
- console.error('This environment does not define `fetch`.');
353
- return () => {};
354
- }
355
-
356
- const resource = options.method === 'GET' ? shrinkQuery(uri) : uri;
357
-
358
- return globalThis.fetch(resource, options);
359
- };
@@ -20,6 +20,10 @@ const GET_PRODUCT_DETAIL = gql`
20
20
  currency
21
21
  value
22
22
  }
23
+
24
+ discount {
25
+ amount_off
26
+ }
23
27
  }
24
28
  }
25
29
  ... on ConfigurableProduct {
@@ -50,6 +54,10 @@ const GET_PRODUCT_DETAIL = gql`
50
54
  currency
51
55
  value
52
56
  }
57
+
58
+ discount {
59
+ amount_off
60
+ }
53
61
  }
54
62
  }
55
63
  }
@@ -4,16 +4,20 @@ import { useMutation, useQuery } from '@apollo/client';
4
4
  import mergeOperations from '../../util/shallowMerge';
5
5
  import { useCartContext } from '../../context/cart';
6
6
  import defaultOperations from './addToCartDialog.gql';
7
+ import { useEventingContext } from '../../context/eventing';
7
8
 
8
9
  export const useAddToCartDialog = props => {
9
10
  const { item, onClose } = props;
10
11
  const sku = item && item.product.sku;
11
12
 
13
+ const [, { dispatch }] = useEventingContext();
14
+
12
15
  const operations = mergeOperations(defaultOperations, props.operations);
13
16
 
14
17
  const [userSelectedOptions, setUserSelectedOptions] = useState(new Map());
15
18
  const [currentImage, setCurrentImage] = useState();
16
19
  const [currentPrice, setCurrentPrice] = useState();
20
+ const [currentDiscount, setCurrentDiscount] = useState();
17
21
 
18
22
  const [{ cartId }] = useCartContext();
19
23
 
@@ -89,6 +93,11 @@ export const useAddToCartDialog = props => {
89
93
  ? selectedVariant.price_range.maximum_price.final_price
90
94
  : product.price_range.maximum_price.final_price;
91
95
 
96
+ const discount = selectedVariant
97
+ ? selectedVariant.price_range.maximum_price.discount
98
+ : product.price_range.maximum_price.discount;
99
+
100
+ setCurrentDiscount(discount);
92
101
  setCurrentPrice(finalPrice);
93
102
  }
94
103
  }, [data, selectedOptionsArray.length]);
@@ -108,22 +117,57 @@ export const useAddToCartDialog = props => {
108
117
 
109
118
  const handleAddToCart = useCallback(async () => {
110
119
  try {
120
+ const quantity = 1;
121
+
111
122
  await addProductToCart({
112
123
  variables: {
113
124
  cartId,
114
125
  cartItem: {
115
- quantity: 1,
126
+ quantity,
116
127
  selected_options: selectedOptionsArray,
117
128
  sku
118
129
  }
119
130
  }
120
131
  });
121
132
 
133
+ const selectedOptionsLabels =
134
+ selectedOptionsArray?.map((value, i) => ({
135
+ attribute: item.product.configurable_options[i].label,
136
+ value:
137
+ item.product.configurable_options[i].values.find(
138
+ x => x.uid === value
139
+ )?.label || null
140
+ })) || null;
141
+
142
+ dispatch({
143
+ type: 'CART_ADD_ITEM',
144
+ payload: {
145
+ cartId,
146
+ sku: item.product.sku,
147
+ name: item.product.name,
148
+ priceTotal: currentPrice.value,
149
+ currencyCode: currentPrice.currency,
150
+ discountAmount: currentDiscount.amount_off,
151
+ selectedOptions: selectedOptionsLabels,
152
+ quantity
153
+ }
154
+ });
155
+
122
156
  handleOnClose();
123
157
  } catch (error) {
124
158
  console.error(error);
125
159
  }
126
- }, [addProductToCart, cartId, handleOnClose, selectedOptionsArray, sku]);
160
+ }, [
161
+ addProductToCart,
162
+ cartId,
163
+ currentDiscount,
164
+ currentPrice,
165
+ dispatch,
166
+ handleOnClose,
167
+ item,
168
+ selectedOptionsArray,
169
+ sku
170
+ ]);
127
171
 
128
172
  const imageProps = useMemo(() => {
129
173
  if (currentImage) {
@@ -6,6 +6,7 @@ import { useUserContext } from '@magento/peregrine/lib/context/user';
6
6
  import mergeOperations from '@magento/peregrine/lib/util/shallowMerge';
7
7
 
8
8
  import defaultOperations from './addressBookPage.gql';
9
+ import { useEventingContext } from '../../context/eventing';
9
10
 
10
11
  /**
11
12
  * A talon to support the functionality of the Address Book page.
@@ -35,7 +36,9 @@ export const useAddressBookPage = (props = {}) => {
35
36
  actions: { setPageLoading }
36
37
  }
37
38
  ] = useAppContext();
38
- const [{ isSignedIn }] = useUserContext();
39
+ const [{ isSignedIn, currentUser }] = useUserContext();
40
+
41
+ const [, { dispatch }] = useEventingContext();
39
42
 
40
43
  const { data: customerAddressesData, loading } = useQuery(
41
44
  getCustomerAddressesQuery,
@@ -112,6 +115,14 @@ export const useAddressBookPage = (props = {}) => {
112
115
  awaitRefetchQueries: true
113
116
  });
114
117
 
118
+ dispatch({
119
+ type: 'USER_ADDRESS_DELETE',
120
+ payload: {
121
+ addressId: confirmDeleteAddressId,
122
+ user: currentUser
123
+ }
124
+ });
125
+
115
126
  setConfirmDeleteAddressId(null);
116
127
  } catch {
117
128
  return;
@@ -119,7 +130,9 @@ export const useAddressBookPage = (props = {}) => {
119
130
  }, [
120
131
  confirmDeleteAddressId,
121
132
  deleteCustomerAddress,
122
- getCustomerAddressesQuery
133
+ getCustomerAddressesQuery,
134
+ dispatch,
135
+ currentUser
123
136
  ]);
124
137
 
125
138
  const handleEditAddress = useCallback(address => {
@@ -139,21 +152,32 @@ export const useAddressBookPage = (props = {}) => {
139
152
  async formValues => {
140
153
  if (isDialogEditMode) {
141
154
  try {
155
+ const address = {
156
+ ...formValues,
157
+ // Sends value as empty if none are provided
158
+ middlename: formValues.middlename || '',
159
+ // Cleans up the street array when values are null or undefined
160
+ street: formValues.street.filter(e => e)
161
+ };
162
+
142
163
  await updateCustomerAddress({
143
164
  variables: {
144
165
  addressId: formAddress.id,
145
- updated_address: {
146
- ...formValues,
147
- // Sends value as empty if none are provided
148
- middlename: formValues.middlename || '',
149
- // Cleans up the street array when values are null or undefined
150
- street: formValues.street.filter(e => e)
151
- }
166
+ updated_address: address
152
167
  },
153
168
  refetchQueries: [{ query: getCustomerAddressesQuery }],
154
169
  awaitRefetchQueries: true
155
170
  });
156
171
 
172
+ dispatch({
173
+ type: 'USER_ADDRESS_EDIT',
174
+ payload: {
175
+ id: formAddress.id,
176
+ address: address,
177
+ user: currentUser
178
+ }
179
+ });
180
+
157
181
  setIsDialogOpen(false);
158
182
  } catch {
159
183
  // Make sure any errors from the mutations are displayed.
@@ -166,20 +190,29 @@ export const useAddressBookPage = (props = {}) => {
166
190
  }
167
191
  } else {
168
192
  try {
193
+ const address = {
194
+ ...formValues,
195
+ // Sends value as empty if none are provided
196
+ middlename: formValues.middlename || '',
197
+ // Cleans up the street array when values are null or undefined
198
+ street: formValues.street.filter(e => e)
199
+ };
169
200
  await createCustomerAddress({
170
201
  variables: {
171
- address: {
172
- ...formValues,
173
- // Sends value as empty if none are provided
174
- middlename: formValues.middlename || '',
175
- // Cleans up the street array when values are null or undefined
176
- street: formValues.street.filter(e => e)
177
- }
202
+ address
178
203
  },
179
204
  refetchQueries: [{ query: getCustomerAddressesQuery }],
180
205
  awaitRefetchQueries: true
181
206
  });
182
207
 
208
+ dispatch({
209
+ type: 'USER_ADDRESS_CREATE',
210
+ payload: {
211
+ address,
212
+ user: currentUser
213
+ }
214
+ });
215
+
183
216
  setIsDialogOpen(false);
184
217
  } catch {
185
218
  // Make sure any errors from the mutations are displayed.
@@ -197,7 +230,9 @@ export const useAddressBookPage = (props = {}) => {
197
230
  formAddress,
198
231
  getCustomerAddressesQuery,
199
232
  isDialogEditMode,
200
- updateCustomerAddress
233
+ updateCustomerAddress,
234
+ dispatch,
235
+ currentUser
201
236
  ]
202
237
  );
203
238
 
@@ -5,6 +5,7 @@ import { useMutation } from '@apollo/client';
5
5
  import mergeOperations from '../../util/shallowMerge';
6
6
  import { useUserContext } from '../../context/user';
7
7
  import DEFAULT_OPERATIONS from './authModal.gql';
8
+ import { useEventingContext } from '../../context/eventing';
8
9
 
9
10
  const UNAUTHED_ONLY = ['CREATE_ACCOUNT', 'FORGOT_PASSWORD', 'SIGN_IN'];
10
11
 
@@ -52,6 +53,8 @@ export const useAuthModal = props => {
52
53
  const [revokeToken] = useMutation(signOutMutation);
53
54
  const history = useHistory();
54
55
 
56
+ const [, { dispatch }] = useEventingContext();
57
+
55
58
  // If the user is authed, the only valid view is "MY_ACCOUNT".
56
59
  // view an also be `MENU` but in that case we don't want to act.
57
60
  useEffect(() => {
@@ -84,6 +87,11 @@ export const useAuthModal = props => {
84
87
  const handleSignOut = useCallback(async () => {
85
88
  setIsSigningOut(true);
86
89
 
90
+ dispatch({
91
+ type: 'USER_SIGN_OUT',
92
+ payload: currentUser
93
+ });
94
+
87
95
  // Delete cart/user data from the redux store.
88
96
  await signOut({ revokeToken });
89
97
 
@@ -91,7 +99,7 @@ export const useAuthModal = props => {
91
99
  // would be to call apolloClient.resetStore() but that would require
92
100
  // a large refactor.
93
101
  history.go(0);
94
- }, [history, revokeToken, signOut]);
102
+ }, [history, revokeToken, signOut, dispatch, currentUser]);
95
103
 
96
104
  return {
97
105
  handleCancel,
@@ -102,6 +110,8 @@ export const useAuthModal = props => {
102
110
  showCreateAccount,
103
111
  showForgotPassword,
104
112
  showMyAccount,
105
- username
113
+ username,
114
+ dispatch,
115
+ currentUser
106
116
  };
107
117
  };
@@ -57,7 +57,7 @@ export const useBreadcrumbs = props => {
57
57
 
58
58
  // When we have breadcrumb data sort and normalize it for easy rendering.
59
59
  const normalizedData = useMemo(() => {
60
- if (!loading && data) {
60
+ if (!loading && data && data.categories.items.length) {
61
61
  const breadcrumbData = data.categories.items[0].breadcrumbs;
62
62
 
63
63
  return (
@@ -79,9 +79,14 @@ export const useBreadcrumbs = props => {
79
79
  const { setShimmerType } = useInternalLink('category');
80
80
 
81
81
  return {
82
- currentCategory: (data && data.categories.items[0].name) || '',
82
+ currentCategory:
83
+ (data &&
84
+ data.categories.items.length &&
85
+ data.categories.items[0].name) ||
86
+ '',
83
87
  currentCategoryPath:
84
88
  (data &&
89
+ data.categories.items.length &&
85
90
  `${data.categories.items[0].url_path}${categoryUrlSuffix ||
86
91
  ''}`) ||
87
92
  '#',
@@ -6,11 +6,33 @@ export const configurableItemResponse = {
6
6
  configurable_options: [
7
7
  {
8
8
  attribute_id: '123',
9
- attribute_code: 'color'
9
+ attribute_code: 'color',
10
+ label: 'Color',
11
+ values: [
12
+ {
13
+ label: 'Lilac',
14
+ value_index: 1
15
+ },
16
+ {
17
+ label: 'Red',
18
+ value_index: 2
19
+ }
20
+ ]
10
21
  },
11
22
  {
12
23
  attribute_id: '456',
13
- attribute_code: 'size'
24
+ attribute_code: 'size',
25
+ label: 'Red',
26
+ values: [
27
+ {
28
+ label: 'XS',
29
+ value_index: 1
30
+ },
31
+ {
32
+ label: 'S',
33
+ value_index: 2
34
+ }
35
+ ]
14
36
  }
15
37
  ],
16
38
  variants: [
@@ -80,11 +102,24 @@ export const configurableItemResponse = {
80
102
  };
81
103
 
82
104
  export const cartItem = {
83
- configurable_options: [{ id: 123, value_id: 1 }, { id: 456, value_id: 1 }],
105
+ configurable_options: [
106
+ { id: 123, value_id: 1, option_label: 'Color', value_label: 'Pink' },
107
+ { id: 456, value_id: 1, option_label: 'Size', value_label: 'XS' }
108
+ ],
84
109
  id: 123,
85
110
  uid: 'NDA=',
86
111
  product: {
87
- sku: 'SP01'
112
+ sku: 'SP01',
113
+ name: 'Product Name'
114
+ },
115
+ prices: {
116
+ price: {
117
+ value: 9.99,
118
+ currency: 'USD'
119
+ },
120
+ total_item_discount: {
121
+ value: 5
122
+ }
88
123
  },
89
124
  quantity: 5
90
125
  };