@graphcommerce/magento-cart 10.0.0-canary.67 → 10.0.0-canary.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Change Log
2
2
 
3
+ ## 10.0.0-canary.72
4
+
5
+ ## 10.0.0-canary.71
6
+
7
+ ## 10.0.0-canary.70
8
+
9
+ ### Major Changes
10
+
11
+ - [#2565](https://github.com/graphcommerce-org/graphcommerce/pull/2565) [`c96dfcd`](https://github.com/graphcommerce-org/graphcommerce/commit/c96dfcdca981baca387c270ad9e2b9515cdd00cc) - Updated to Apollo Client 4 ([@paales](https://github.com/paales))
12
+
13
+ ## 10.0.0-canary.69
14
+
15
+ ## 10.0.0-canary.68
16
+
17
+ ### Major Changes
18
+
19
+ - [#2557](https://github.com/graphcommerce-org/graphcommerce/pull/2557) [`ceaadd8`](https://github.com/graphcommerce-org/graphcommerce/commit/ceaadd87f0648982a068a3b07b1fa149c9127f49) - ## Material UI v5 → v7 Migration
20
+
21
+ This release upgrades Material UI from v5 to v7 with full CSS variables support. ([@paales](https://github.com/paales))
22
+
3
23
  ## 10.0.0-canary.67
4
24
 
5
25
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  import { CheckboxElement } from '@graphcommerce/ecommerce-ui'
2
2
  import { useQuery } from '@graphcommerce/graphql'
3
- import { extendableComponent, FormDiv } from '@graphcommerce/next-ui'
3
+ import { extendableComponent, FormDiv, sxx } from '@graphcommerce/next-ui'
4
4
  import type { UseFormComposeOptions } from '@graphcommerce/react-hook-form'
5
5
  import { FormPersist, useForm, useFormCompose } from '@graphcommerce/react-hook-form'
6
6
  import { t } from '@lingui/core/macro'
@@ -50,10 +50,7 @@ export function CartAgreementsForm(props: CartAgreementsFormProps) {
50
50
  if (data?.checkoutAgreements?.length === 0) return null
51
51
 
52
52
  return (
53
- <FormDiv
54
- className={classes.form}
55
- sx={[(theme) => ({ pt: theme.spacings.md }), ...(Array.isArray(sx) ? sx : [sx])]}
56
- >
53
+ <FormDiv className={classes.form} sx={sxx((theme) => ({ pt: theme.spacings.md }), sx)}>
57
54
  <form noValidate onSubmit={submit} name='cartAgreements'>
58
55
  <Box className={classes.formInner} sx={{ typography: 'body1', display: 'inline-block' }}>
59
56
  {data?.checkoutAgreements &&
@@ -64,7 +61,6 @@ export function CartAgreementsForm(props: CartAgreementsFormProps) {
64
61
  // check if the agreement text contains an anchor tag
65
62
  const containsLink = containsAnchorTag(agreement.checkbox_text)
66
63
  let labelContent: React.ReactNode
67
-
68
64
  if (containsLink) {
69
65
  labelContent = (
70
66
  <Typography
@@ -3,12 +3,13 @@ import {
3
3
  extendableComponent,
4
4
  iconShoppingBag,
5
5
  IconSvg,
6
+ sxx,
6
7
  useFabSize,
7
8
  useScrollY,
8
9
  } from '@graphcommerce/next-ui'
9
10
  import { t } from '@lingui/core/macro'
10
11
  import type { BadgeProps, FabProps, SxProps, Theme } from '@mui/material'
11
- import { alpha, Box, Fab, styled, useTheme } from '@mui/material'
12
+ import { Box, Fab, styled, useTheme } from '@mui/material'
12
13
  import { m, useTransform } from 'framer-motion'
13
14
  import React from 'react'
14
15
  import { useCartEnabled, useCartShouldLoginToContinue } from '../../hooks'
@@ -42,8 +43,8 @@ function CartFabContent(props: CartFabContentProps) {
42
43
  const scrollY = useScrollY()
43
44
  const opacity = useTransform(scrollY, [50, 60], [0, 1])
44
45
 
45
- const paper0 = alpha(theme2.palette.background.paper, 0)
46
- const paper1 = alpha(theme2.palette.background.paper, 1)
46
+ const paper0 = theme2.alpha(theme2.palette.background.paper, 0)
47
+ const paper1 = theme2.alpha(theme2.palette.background.paper, 1)
47
48
  const backgroundColor = useTransform(scrollY, [0, 10], [paper0, paper1])
48
49
 
49
50
  const cartIcon = icon ?? <IconSvg src={iconShoppingBag} size='large' />
@@ -52,10 +53,7 @@ function CartFabContent(props: CartFabContentProps) {
52
53
  return (
53
54
  <Box
54
55
  className={classes.root}
55
- sx={[
56
- { position: 'relative', width: fabIconSize, height: fabIconSize },
57
- ...(Array.isArray(sx) ? sx : [sx]),
58
- ]}
56
+ sx={sxx({ position: 'relative', width: fabIconSize, height: fabIconSize }, sx)}
59
57
  >
60
58
  <MotionFab
61
59
  href='/cart'
@@ -66,7 +64,7 @@ function CartFabContent(props: CartFabContentProps) {
66
64
  style={{ backgroundColor }}
67
65
  sx={(theme) => ({
68
66
  [theme.breakpoints.down('md')]: {
69
- backgroundColor: `${theme.palette.background.paper} !important`,
67
+ backgroundColor: `${theme.vars.palette.background.paper} !important`,
70
68
  },
71
69
  })}
72
70
  {...fabProps}
@@ -7,6 +7,7 @@ import {
7
7
  extendableComponent,
8
8
  nonNullable,
9
9
  SectionContainer,
10
+ sxx,
10
11
  } from '@graphcommerce/next-ui'
11
12
  import { Trans } from '@lingui/react/macro'
12
13
  import type { SxProps, Theme } from '@mui/material'
@@ -38,10 +39,10 @@ export function CartItemSummary(props: OrderSummaryProps) {
38
39
  return (
39
40
  <Box
40
41
  className={classes.root}
41
- sx={[
42
+ sx={sxx(
42
43
  (theme) => ({
43
44
  padding: `${theme.spacings.sm} ${theme.spacings.sm}`,
44
- border: `1px ${theme.palette.divider} solid`,
45
+ border: `1px ${theme.vars.palette.divider} solid`,
45
46
  ...breakpointVal(
46
47
  'borderRadius',
47
48
  theme.shape.borderRadius * 2,
@@ -49,8 +50,8 @@ export function CartItemSummary(props: OrderSummaryProps) {
49
50
  theme.breakpoints.values,
50
51
  ),
51
52
  }),
52
- ...(Array.isArray(sx) ? sx : [sx]),
53
- ]}
53
+ sx,
54
+ )}
54
55
  >
55
56
  <SectionContainer
56
57
  sx={{ '& .SectionHeader-root': { mt: 0 } }}
@@ -1,6 +1,6 @@
1
1
  import { Money } from '@graphcommerce/magento-store'
2
2
  import type { ButtonProps } from '@graphcommerce/next-ui'
3
- import { Button, extendableComponent, iconChevronRight, IconSvg } from '@graphcommerce/next-ui'
3
+ import { Button, extendableComponent, iconChevronRight, IconSvg, sxx } from '@graphcommerce/next-ui'
4
4
  import { Trans } from '@lingui/react/macro'
5
5
  import type { SxProps, Theme } from '@mui/material'
6
6
  import { Box, Link } from '@mui/material'
@@ -49,13 +49,13 @@ export function CartStartCheckout(props: CartStartCheckoutProps) {
49
49
  return (
50
50
  <Box
51
51
  className={classes.checkoutButtonContainer}
52
- sx={[
52
+ sx={sxx(
53
53
  (theme) => ({
54
54
  textAlign: 'center',
55
55
  my: theme.spacings.md,
56
56
  }),
57
- ...(Array.isArray(sx) ? sx : [sx]),
58
- ]}
57
+ sx,
58
+ )}
59
59
  >
60
60
  {shouldLoginToContinue && (
61
61
  <Box sx={{ mb: 1 }} className={classes.loginContainer}>
@@ -1,9 +1,9 @@
1
1
  import { useHistoryLink } from '@graphcommerce/framer-next-pages'
2
2
  import { useBillingAddressPermission } from '@graphcommerce/magento-customer'
3
- import { breakpointVal, extendableComponent, SectionContainer } from '@graphcommerce/next-ui'
3
+ import { breakpointVal, extendableComponent, SectionContainer, sxx } from '@graphcommerce/next-ui'
4
4
  import { Trans } from '@lingui/react/macro'
5
5
  import type { SxProps, Theme } from '@mui/material'
6
- import { Box, lighten, Link, Typography } from '@mui/material'
6
+ import { Box, Link, Typography } from '@mui/material'
7
7
  import React from 'react'
8
8
  import { useCartQuery } from '../../hooks'
9
9
  import { CartAddressMultiLine } from '../CartAddressMultiLine/CartAddressMultiLine'
@@ -37,7 +37,7 @@ export function CartSummary(props: CartSummaryProps) {
37
37
  return (
38
38
  <Box
39
39
  className={classes.root}
40
- sx={[
40
+ sx={sxx(
41
41
  (theme) => ({
42
42
  margin: `${theme.spacings.sm} 0`,
43
43
  '& > div:last-of-type': {
@@ -56,8 +56,8 @@ export function CartSummary(props: CartSummaryProps) {
56
56
  ),
57
57
  },
58
58
  }),
59
- ...(Array.isArray(sx) ? sx : [sx]),
60
- ]}
59
+ sx,
60
+ )}
61
61
  >
62
62
  <Box
63
63
  className={classes.detailsContainer}
@@ -74,10 +74,7 @@ export function CartSummary(props: CartSummaryProps) {
74
74
  theme.shape.borderRadius * 3,
75
75
  theme.breakpoints.values,
76
76
  ),
77
- background:
78
- theme.palette.mode === 'light'
79
- ? theme.palette.background.default
80
- : lighten(theme.palette.background.default, 0.15),
77
+ background: theme.lighten(theme.vars.palette.background.default, 0.15),
81
78
  padding: theme.spacings.sm,
82
79
  gridColumnGap: theme.spacings.xxl,
83
80
  gridRowGap: theme.spacings.sm,
@@ -86,6 +83,9 @@ export function CartSummary(props: CartSummaryProps) {
86
83
  gridTemplateColumns: '1fr 1fr',
87
84
  marginTop: theme.spacings.xxs,
88
85
  },
86
+ ...theme.applyStyles('light', {
87
+ background: theme.vars.palette.background.default,
88
+ }),
89
89
  })}
90
90
  >
91
91
  <Box>
@@ -3,7 +3,7 @@ import { magentoVersion } from '@graphcommerce/next-config/config'
3
3
  import { breakpointVal, extendableComponent, sxx } from '@graphcommerce/next-ui'
4
4
  import { Trans } from '@lingui/react/macro'
5
5
  import type { SxProps, Theme } from '@mui/material'
6
- import { Box, Divider, lighten } from '@mui/material'
6
+ import { Box, Divider } from '@mui/material'
7
7
  import { useCartQuery, useDisplayInclTax } from '../../hooks'
8
8
  import { GetCartTotalsDocument } from './GetCartTotals.gql'
9
9
 
@@ -71,12 +71,11 @@ export function CartTotals(props: CartTotalsProps) {
71
71
  theme.shape.borderRadius * 5,
72
72
  theme.breakpoints.values,
73
73
  ),
74
- background:
75
- theme.palette.mode === 'light'
76
- ? theme.palette.background.default
77
- : lighten(theme.palette.background.default, 0.15),
74
+ background: theme.vars.palette.background.default,
75
+ ...theme.applyStyles('dark', {
76
+ background: theme.lighten(theme.vars.palette.background.default, 0.15),
77
+ }),
78
78
  padding: `${theme.spacings.xs} ${theme.spacings.sm}`,
79
-
80
79
  '&.containerMargin': {
81
80
  marginTop: theme.spacings.md,
82
81
  px: theme.spacings.xs,
@@ -132,7 +131,9 @@ export function CartTotals(props: CartTotalsProps) {
132
131
  sx={{ display: 'flex', justifyContent: 'space-between' }}
133
132
  >
134
133
  <Box>
135
- <Trans>Shipping ({shippingMethod.carrier_title} {shippingMethod.method_title})</Trans>
134
+ <Trans>
135
+ Shipping ({shippingMethod.carrier_title} {shippingMethod.method_title})
136
+ </Trans>
136
137
  </Box>
137
138
  <Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
138
139
  <Money
@@ -171,7 +172,7 @@ export function CartTotals(props: CartTotalsProps) {
171
172
  sx={(theme) => ({
172
173
  display: 'flex',
173
174
  justifyContent: 'space-between',
174
- color: theme.palette.primary.main,
175
+ color: theme.vars.palette.primary.main,
175
176
  })}
176
177
  >
177
178
  <Box>
@@ -74,7 +74,6 @@ export function EditBillingAddressForm(props: EditBillingAddressFormProps) {
74
74
  <CompanyFields<SetBillingAddressMutationVariables> form={form} />
75
75
  <NameFields form={form} prefix />
76
76
  <AddressFields<SetBillingAddressMutationVariables> form={form} />
77
-
78
77
  <FormRow>
79
78
  <TelephoneElement
80
79
  variant='outlined'
@@ -85,9 +84,7 @@ export function EditBillingAddressForm(props: EditBillingAddressFormProps) {
85
84
  showValid
86
85
  />
87
86
  </FormRow>
88
-
89
87
  <FormDivider />
90
-
91
88
  <FormActions sx={{ paddingBottom: 0 }}>
92
89
  <Button
93
90
  type='submit'
@@ -100,7 +97,6 @@ export function EditBillingAddressForm(props: EditBillingAddressFormProps) {
100
97
  </Button>
101
98
  </FormActions>
102
99
  </Form>
103
-
104
100
  <ApolloCustomerErrorAlert error={error} />
105
101
  </>
106
102
  )
@@ -30,11 +30,7 @@ export function EmptyCart(props: EmptyCartProps) {
30
30
  }
31
31
  {...rest}
32
32
  >
33
- {children ?? (
34
- <Trans>
35
- Discover our collection and add items to your cart!
36
- </Trans>
37
- )}
33
+ {children ?? <Trans>Discover our collection and add items to your cart!</Trans>}
38
34
  </FullPageMessage>
39
35
  )
40
36
  }
@@ -5,7 +5,7 @@ import {
5
5
  useCustomerSession,
6
6
  useGuestQuery,
7
7
  } from '@graphcommerce/magento-customer'
8
- import { Button, extendableComponent, FormRow } from '@graphcommerce/next-ui'
8
+ import { Button, extendableComponent, FormRow, sxx } from '@graphcommerce/next-ui'
9
9
  import { Trans } from '@lingui/react/macro'
10
10
  import type { SxProps, Theme } from '@mui/material'
11
11
  import { Box, TextField, Typography } from '@mui/material'
@@ -48,15 +48,15 @@ export function InlineAccount(props: InlineAccountProps) {
48
48
  <div>
49
49
  <Box
50
50
  className={classes.root}
51
- sx={[
51
+ sx={sxx(
52
52
  (theme) => ({
53
53
  borderRadius: '4px',
54
- border: `1px solid ${theme.palette.divider}`,
54
+ border: `1px solid ${theme.vars.palette.divider}`,
55
55
  padding: theme.spacings.md,
56
56
  marginTop: theme.spacings.sm,
57
57
  }),
58
- ...(Array.isArray(sx) ? sx : [sx]),
59
- ]}
58
+ sx,
59
+ )}
60
60
  >
61
61
  <Box
62
62
  className={classes.innerContainer}
@@ -101,8 +101,10 @@ export function InlineAccount(props: InlineAccountProps) {
101
101
  variant='outlined'
102
102
  label={<Trans>Email address</Trans>}
103
103
  value={cart?.email}
104
- InputProps={{
105
- readOnly: true,
104
+ slotProps={{
105
+ input: {
106
+ readOnly: true,
107
+ },
106
108
  }}
107
109
  />
108
110
  </FormRow>
@@ -6,7 +6,7 @@ import { CurrentCartIdDocument } from './CurrentCartId.gql'
6
6
 
7
7
  export const CART_ID_COOKIE = 'cart'
8
8
 
9
- export function writeCartId(cache: ApolloCache<object>, id: string | null = null) {
9
+ export function writeCartId(cache: ApolloCache, id: string | null = null) {
10
10
  cache.writeQuery({
11
11
  query: CurrentCartIdDocument,
12
12
  data: { currentCartId: { __typename: 'CurrentCartId', locked: false, id } },
@@ -14,11 +14,11 @@ export function writeCartId(cache: ApolloCache<object>, id: string | null = null
14
14
  })
15
15
  }
16
16
 
17
- export function readCartId(cache: ApolloCache<object>) {
17
+ export function readCartId(cache: ApolloCache) {
18
18
  return cache.readQuery({ query: CurrentCartIdDocument })?.currentCartId
19
19
  }
20
20
 
21
- export function cartLock(cache: ApolloCache<object>, locked: boolean) {
21
+ export function cartLock(cache: ApolloCache, locked: boolean) {
22
22
  const currentCartId = cache.readQuery({ query: CurrentCartIdDocument })?.currentCartId
23
23
  if (currentCartId?.id && currentCartId.locked !== locked) {
24
24
  cache.writeQuery({
@@ -1,10 +1,23 @@
1
- import type { QueryHookOptions, TypedDocumentNode } from '@graphcommerce/graphql'
2
- import { ApolloError, useQuery } from '@graphcommerce/graphql'
1
+ import type {
2
+ OperationVariables,
3
+ TypedDocumentNode,
4
+ WatchQueryFetchPolicy,
5
+ } from '@graphcommerce/graphql'
6
+ import { useQuery } from '@graphcommerce/graphql'
7
+ import { CombinedGraphQLErrors } from '@apollo/client/errors'
8
+ import type { useQuery as useQueryType } from '@apollo/client/react'
3
9
  import { GraphQLError } from 'graphql'
4
10
  import { useRouter } from 'next/router'
5
11
  import { useCartShouldLoginToContinue } from './useCartPermissions'
6
12
  import { useCurrentCartId } from './useCurrentCartId'
7
13
 
14
+ type CartQueryOptions<Q, V extends OperationVariables> = Omit<
15
+ useQueryType.Options<Q, V>,
16
+ 'variables'
17
+ > & {
18
+ variables?: Omit<V, 'cartId'>
19
+ }
20
+
8
21
  /**
9
22
  * Requires the query to have a `$cartId: String!` argument. It will automatically inject the
10
23
  * currently active cart_id.
@@ -15,12 +28,10 @@ import { useCurrentCartId } from './useCurrentCartId'
15
28
  * const { data } = useCartQuery(CartFabQueryDocument)
16
29
  * ```
17
30
  */
18
- export function useCartQuery<Q, V extends { cartId: string; [index: string]: unknown }>(
19
- document: TypedDocumentNode<Q, V>,
20
- options: QueryHookOptions<Q, Omit<V, 'cartId'>> = {},
21
- ) {
22
- const { ...queryOptions } = options
23
-
31
+ export function useCartQuery<
32
+ Q,
33
+ V extends OperationVariables & { cartId: string; [index: string]: unknown },
34
+ >(document: TypedDocumentNode<Q, V>, options?: CartQueryOptions<Q, V>) {
24
35
  const router = useRouter()
25
36
  const { currentCartId, locked } = useCurrentCartId()
26
37
 
@@ -29,23 +40,27 @@ export function useCartQuery<Q, V extends { cartId: string; [index: string]: unk
29
40
  const cartId = usingUrl ? urlCartId : currentCartId
30
41
  const shouldLoginToContinue = useCartShouldLoginToContinue()
31
42
 
32
- if (usingUrl || locked) queryOptions.fetchPolicy = 'cache-only'
43
+ let fetchPolicy: WatchQueryFetchPolicy | undefined = options?.fetchPolicy
44
+ let returnPartialData: boolean | undefined = options?.returnPartialData
33
45
 
34
- if (usingUrl && typeof queryOptions.returnPartialData === 'undefined')
35
- queryOptions.returnPartialData = true
46
+ if (usingUrl || locked) fetchPolicy = 'cache-only'
47
+ if (usingUrl && typeof returnPartialData === 'undefined') returnPartialData = true
36
48
 
37
- queryOptions.variables = { cartId, ...options?.variables } as V
49
+ const variables = { cartId, ...options?.variables } as V
38
50
 
39
51
  const query = useQuery(document, {
40
- ...(queryOptions as QueryHookOptions<Q, V>),
41
- skip: queryOptions.skip || !cartId || shouldLoginToContinue,
52
+ ...options,
53
+ fetchPolicy,
54
+ returnPartialData,
55
+ variables,
56
+ skip: options?.skip || !cartId || shouldLoginToContinue,
42
57
  })
43
58
 
44
- if (shouldLoginToContinue && !queryOptions?.skip) {
59
+ if (shouldLoginToContinue && !options?.skip) {
45
60
  return {
46
61
  ...query,
47
- error: new ApolloError({
48
- graphQLErrors: [
62
+ error: new CombinedGraphQLErrors({
63
+ errors: [
49
64
  new GraphQLError('Action can not be performed by the current user', {
50
65
  extensions: { category: 'graphql-authorization' },
51
66
  }),
@@ -1,4 +1,3 @@
1
- import type { QueryHookOptions } from '@graphcommerce/graphql'
2
1
  import { useQuery } from '@graphcommerce/graphql'
3
2
  import type { CurrentCartIdQuery, CurrentCartIdQueryVariables } from './CurrentCartId.gql'
4
3
  import { CurrentCartIdDocument } from './CurrentCartId.gql'
@@ -6,8 +5,8 @@ import { CurrentCartIdDocument } from './CurrentCartId.gql'
6
5
  export function useCurrentCartId<
7
6
  Q extends CurrentCartIdQuery,
8
7
  V extends CurrentCartIdQueryVariables,
9
- >(options: QueryHookOptions<Q, V> = {}) {
10
- const queryResults = useQuery<Q, V>(CurrentCartIdDocument, options)
8
+ >(options: Partial<useQuery.Options<Q, V>> = {}) {
9
+ const queryResults = useQuery<Q, V>(CurrentCartIdDocument, options as useQuery.Options<Q, V>)
11
10
 
12
11
  return {
13
12
  currentCartId: queryResults.data?.currentCartId?.id || '',
@@ -1,5 +1,9 @@
1
- import type { MutationHookOptions, TypedDocumentNode } from '@graphcommerce/graphql'
2
- import { ApolloError, useApolloClient } from '@graphcommerce/graphql'
1
+ import {
2
+ CombinedGraphQLErrors,
3
+ TypedDocumentNode,
4
+ useApolloClient,
5
+ useMutation,
6
+ } from '@graphcommerce/graphql'
3
7
  import type {
4
8
  UseFormGqlMutationReturn,
5
9
  UseFormGraphQlOptions,
@@ -18,7 +22,7 @@ export function useFormGqlMutationCart<
18
22
  >(
19
23
  document: TypedDocumentNode<Q, V>,
20
24
  options: UseFormGraphQlOptions<Q, V> & { submitWhileLocked?: boolean } = {},
21
- operationOptions?: MutationHookOptions<Q, V>,
25
+ operationOptions?: useMutation.Options<NoInfer<Q>, NoInfer<V>>,
22
26
  ): UseFormGqlMutationReturn<Q, V> {
23
27
  const cartIdCreate = useCartIdCreate()
24
28
  const cartIdFromContext = useCartIdContext()
@@ -63,8 +67,8 @@ export function useFormGqlMutationCart<
63
67
  if (shouldLoginToContinue && result.formState.isSubmitted && shouldBlockOperation) {
64
68
  return {
65
69
  ...result,
66
- error: new ApolloError({
67
- graphQLErrors: [
70
+ error: new CombinedGraphQLErrors({
71
+ errors: [
68
72
  new GraphQLError('Action can not be performed by the current user', {
69
73
  extensions: { category: 'graphql-authorization' },
70
74
  }),
package/link/cartLink.ts CHANGED
@@ -1,6 +1,6 @@
1
- import type { Operation } from '@graphcommerce/graphql'
2
- import { fromPromise, globalApolloClient } from '@graphcommerce/graphql'
3
- import { ApolloLink, Observable, onError } from '@graphcommerce/graphql/apollo'
1
+ import { globalApolloClient } from '@graphcommerce/graphql'
2
+ import { ApolloLink, CombinedGraphQLErrors, ErrorLink } from '@graphcommerce/graphql/apollo'
3
+ import { filter, from, of, switchMap } from '@graphcommerce/graphql/rxjs'
4
4
  import { CustomerTokenDocument, getCustomerAccountCanSignIn } from '@graphcommerce/magento-customer'
5
5
  import type { PushRouter } from '@graphcommerce/magento-customer/link/customerLink'
6
6
  import { pushWithPromise } from '@graphcommerce/magento-customer/link/customerLink'
@@ -14,8 +14,8 @@ import { CreateEmptyCartDocument } from '../hooks/CreateEmptyCart.gql'
14
14
  import { getCartEnabledForUser } from '../utils'
15
15
  import { isProtectedCartOperation } from './isProtectedCartOperation'
16
16
 
17
- type CartOperation = Operation & { variables: { cartId: string } }
18
- function isCartOperation(operation: Operation): operation is CartOperation {
17
+ type CartOperation = ApolloLink.Operation & { variables: { cartId: string } }
18
+ function isCartOperation(operation: ApolloLink.Operation): operation is CartOperation {
19
19
  return typeof operation.variables.cartId === 'string'
20
20
  }
21
21
 
@@ -24,13 +24,16 @@ function errorIsIncluded(errorPath: readonly (string | number)[] | undefined, ke
24
24
  return keys.some((value) => value === error)
25
25
  }
26
26
 
27
- const cartErrorLink = onError(({ graphQLErrors, operation, forward }) => {
27
+ const cartErrorLink = new ErrorLink(({ error, operation, forward }) => {
28
28
  if (!globalApolloClient.current) return undefined
29
29
 
30
30
  const client = globalApolloClient.current
31
31
  const { cache } = client
32
32
 
33
- if (!isCartOperation(operation) || !graphQLErrors) return undefined
33
+ // Check if this is a GraphQL error
34
+ if (!CombinedGraphQLErrors.is(error)) return undefined
35
+
36
+ if (!isCartOperation(operation)) return undefined
34
37
 
35
38
  const isErrorCategory = (err: GraphQLFormattedError, category: ErrorCategory) =>
36
39
  err.extensions?.category === category
@@ -54,7 +57,7 @@ const cartErrorLink = onError(({ graphQLErrors, operation, forward }) => {
54
57
  // 'applyCouponToCart',
55
58
  // 'removeCouponFromCart'
56
59
  ])
57
- const cartErr = graphQLErrors.find((err) => isNoSuchEntityError(err))
60
+ const cartErr = error.errors.find((err) => isNoSuchEntityError(err))
58
61
 
59
62
  if (!cartErr) return undefined
60
63
 
@@ -63,9 +66,9 @@ const cartErrorLink = onError(({ graphQLErrors, operation, forward }) => {
63
66
  if (urlParams.get('cart_id')) return forward(operation)
64
67
  }
65
68
 
66
- return fromPromise(client?.mutate({ mutation: CreateEmptyCartDocument }))
67
- .filter((value) => Boolean(value))
68
- .flatMap((cartData) => {
69
+ return from(client?.mutate({ mutation: CreateEmptyCartDocument })).pipe(
70
+ filter((value) => Boolean(value)),
71
+ switchMap((cartData) => {
69
72
  const cartId = cartData.data?.createEmptyCart
70
73
  if (!cartId) return forward(operation)
71
74
 
@@ -74,7 +77,8 @@ const cartErrorLink = onError(({ graphQLErrors, operation, forward }) => {
74
77
 
75
78
  // retry the request, returning the new observable
76
79
  return forward(operation)
77
- })
80
+ }),
81
+ )
78
82
  })
79
83
 
80
84
  const cartPermissionLink = (router: PushRouter) =>
@@ -82,7 +86,7 @@ const cartPermissionLink = (router: PushRouter) =>
82
86
  const { locale } = router
83
87
  const { cache } = operation.getContext()
84
88
 
85
- if (!isProtectedCartOperation(operation.operationName)) return forward(operation)
89
+ if (!isProtectedCartOperation(operation.operationName ?? '')) return forward(operation)
86
90
 
87
91
  const check = () => Boolean(cache?.readQuery({ query: CustomerTokenDocument }))
88
92
  if (getCartEnabledForUser(locale, check)) return forward(operation)
@@ -95,33 +99,35 @@ const cartPermissionLink = (router: PushRouter) =>
95
99
  const oldHeaders = operation.getContext().headers
96
100
  const signInAgainPromise = pushWithPromise(router, '/account/signin')
97
101
 
98
- return fromPromise(signInAgainPromise).flatMap(() => {
99
- const tokenQuery = cache?.readQuery({ query: CustomerTokenDocument })
100
-
101
- if (tokenQuery?.customerToken?.valid) {
102
- // Customer is authenticated, retrying request.
103
- operation.setContext({
104
- headers: {
105
- ...oldHeaders,
106
- authorization: `Bearer ${tokenQuery?.customerToken?.token}`,
107
- },
102
+ return from(signInAgainPromise).pipe(
103
+ switchMap(() => {
104
+ const tokenQuery = cache?.readQuery({ query: CustomerTokenDocument })
105
+
106
+ if (tokenQuery?.customerToken?.valid) {
107
+ // Customer is authenticated, retrying request.
108
+ operation.setContext({
109
+ headers: {
110
+ ...oldHeaders,
111
+ authorization: `Bearer ${tokenQuery?.customerToken?.token}`,
112
+ },
113
+ })
114
+ return forward(operation)
115
+ }
116
+
117
+ return of({
118
+ data: null,
119
+ errors: [
120
+ new GraphQLError(t`Please login to add products to your cart`, {
121
+ extensions: { category: 'graphql-authorization' },
122
+ }),
123
+ ],
108
124
  })
109
- return forward(operation)
110
- }
111
-
112
- return Observable.of({
113
- data: null,
114
- errors: [
115
- new GraphQLError(t`Please login to add products to your cart`, {
116
- extensions: { category: 'graphql-authorization' },
117
- }),
118
- ],
119
- })
120
- })
125
+ }),
126
+ )
121
127
  })
122
128
 
123
129
  export const cartLink = (router: PushRouter) => {
124
- const links = [cartErrorLink]
130
+ const links: ApolloLink[] = [cartErrorLink]
125
131
 
126
132
  if (!(permissions?.cart === 'ENABLED')) {
127
133
  links.push(cartPermissionLink(router))
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@graphcommerce/magento-cart",
3
3
  "homepage": "https://www.graphcommerce.org/",
4
4
  "repository": "github:graphcommerce-org/graphcommerce",
5
- "version": "10.0.0-canary.67",
5
+ "version": "10.0.0-canary.72",
6
6
  "sideEffects": false,
7
7
  "prettier": "@graphcommerce/prettier-config-pwa",
8
8
  "eslintConfig": {
@@ -20,24 +20,24 @@
20
20
  "./plugins/useSignInFormMergeCart": "./plugins/useSignInFormMergeCart.ts"
21
21
  },
22
22
  "peerDependencies": {
23
- "@graphcommerce/ecommerce-ui": "^10.0.0-canary.67",
24
- "@graphcommerce/eslint-config-pwa": "^10.0.0-canary.67",
25
- "@graphcommerce/framer-next-pages": "^10.0.0-canary.67",
26
- "@graphcommerce/framer-scroller": "^10.0.0-canary.67",
27
- "@graphcommerce/framer-utils": "^10.0.0-canary.67",
28
- "@graphcommerce/graphql": "^10.0.0-canary.67",
29
- "@graphcommerce/image": "^10.0.0-canary.67",
30
- "@graphcommerce/magento-customer": "^10.0.0-canary.67",
31
- "@graphcommerce/magento-graphql": "^10.0.0-canary.67",
32
- "@graphcommerce/magento-store": "^10.0.0-canary.67",
33
- "@graphcommerce/next-ui": "^10.0.0-canary.67",
34
- "@graphcommerce/prettier-config-pwa": "^10.0.0-canary.67",
35
- "@graphcommerce/react-hook-form": "^10.0.0-canary.67",
36
- "@graphcommerce/typescript-config-pwa": "^10.0.0-canary.67",
23
+ "@graphcommerce/ecommerce-ui": "^10.0.0-canary.72",
24
+ "@graphcommerce/eslint-config-pwa": "^10.0.0-canary.72",
25
+ "@graphcommerce/framer-next-pages": "^10.0.0-canary.72",
26
+ "@graphcommerce/framer-scroller": "^10.0.0-canary.72",
27
+ "@graphcommerce/framer-utils": "^10.0.0-canary.72",
28
+ "@graphcommerce/graphql": "^10.0.0-canary.72",
29
+ "@graphcommerce/image": "^10.0.0-canary.72",
30
+ "@graphcommerce/magento-customer": "^10.0.0-canary.72",
31
+ "@graphcommerce/magento-graphql": "^10.0.0-canary.72",
32
+ "@graphcommerce/magento-store": "^10.0.0-canary.72",
33
+ "@graphcommerce/next-ui": "^10.0.0-canary.72",
34
+ "@graphcommerce/prettier-config-pwa": "^10.0.0-canary.72",
35
+ "@graphcommerce/react-hook-form": "^10.0.0-canary.72",
36
+ "@graphcommerce/typescript-config-pwa": "^10.0.0-canary.72",
37
37
  "@lingui/core": "^5",
38
38
  "@lingui/macro": "^5",
39
39
  "@lingui/react": "^5",
40
- "@mui/material": "^5.10.16",
40
+ "@mui/material": "^7.0.0",
41
41
  "framer-motion": "^11.0.0",
42
42
  "graphql": "^16.0.0",
43
43
  "next": "*",
@@ -22,12 +22,12 @@ export const useSignInForm: FunctionPlugin<typeof useSignInFormType> = (useSignI
22
22
 
23
23
  cartLock(client.cache, true)
24
24
 
25
- const destinationCartId = (
26
- await client.query({
27
- query: CustomerCartDocument,
28
- fetchPolicy: 'network-only',
29
- })
30
- ).data.customerCart.id
25
+ const customerCartResult = await client.query({
26
+ query: CustomerCartDocument,
27
+ fetchPolicy: 'network-only',
28
+ })
29
+ const destinationCartId = customerCartResult.data?.customerCart?.id
30
+ if (!destinationCartId) return
31
31
 
32
32
  try {
33
33
  const sourceCartId = readCartId(client.cache)?.id
@@ -3,10 +3,7 @@ import type { ApolloClient, NormalizedCacheObject } from '@graphcommerce/graphql
3
3
  import type { Page } from '@playwright/test'
4
4
  import { CartAgreementsDocument } from '../components/CartAgreementsForm/CartAgreements.gql'
5
5
 
6
- export async function fillCartAgreementsForm(
7
- page: Page,
8
- client: ApolloClient<NormalizedCacheObject>,
9
- ) {
6
+ export async function fillCartAgreementsForm(page: Page, client: ApolloClient) {
10
7
  const res = (await client.query({ query: CartAgreementsDocument })).data
11
8
 
12
9
  for await (const agreement of res.checkoutAgreements ?? []) {
package/typePolicies.ts CHANGED
@@ -1,8 +1,4 @@
1
- import type {
2
- ApolloCache,
3
- NormalizedCacheObject,
4
- StrictTypedTypePolicies,
5
- } from '@graphcommerce/graphql'
1
+ import type { ApolloCache, StrictTypedTypePolicies } from '@graphcommerce/graphql'
6
2
  import type { CartPrices, QuerycartArgs, ShippingCartAddress } from '@graphcommerce/graphql-mesh'
7
3
  import { CartFabDocument } from './components/CartFab/CartFab.gql'
8
4
  import { readCartId, writeCartId } from './hooks'
@@ -57,10 +53,7 @@ export const cartTypePolicies: StrictTypedTypePolicies = {
57
53
  },
58
54
  }
59
55
 
60
- export const migrateCart = (
61
- oldCache: ApolloCache<NormalizedCacheObject>,
62
- newCache: ApolloCache<NormalizedCacheObject>,
63
- ) => {
56
+ export const migrateCart = (oldCache: ApolloCache, newCache: ApolloCache) => {
64
57
  const cartId = readCartId(oldCache)?.id
65
58
 
66
59
  if (cartId) {