@graphcommerce/magento-cart 4.6.7 → 4.7.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,62 @@
1
1
  # Change Log
2
2
 
3
+ ## 4.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#1602](https://github.com/graphcommerce-org/graphcommerce/pull/1602) [`5f781a217`](https://github.com/graphcommerce-org/graphcommerce/commit/5f781a217ce63ed56bc1a9983487b04400a8a315) Thanks [@ErwinOtten](https://github.com/ErwinOtten)! - Default styles and layout fixes
8
+
9
+ - Scaled icons and fonts down. Size in typography is now more gradual: https://graphcommerce.vercel.app/test/typography
10
+ - Multiple accessibility fixes. Missing button/input labels, and fixed spacing issues resulting in high % appropriately sized tap targets
11
+ - Replaced responsiveVal usage with better performaning breakpointVal where possible
12
+ - All buttons are now Pill by default.
13
+ - Cleaned up checkout styles
14
+
15
+ * [#1608](https://github.com/graphcommerce-org/graphcommerce/pull/1608) [`ac6eedbb1`](https://github.com/graphcommerce-org/graphcommerce/commit/ac6eedbb14d3abd8cf1231a98dc2a8b7f4659f1f) Thanks [@mikekeehnen](https://github.com/mikekeehnen)! - Added global errorhandling on cart errors. When a cart query return an error, the currentCartId wil be renewed with the actual cartId when the user is authenticated. When there is an error in a guest cart, a new cartId will be generated and the cart will be empty
16
+
17
+ ### Patch Changes
18
+
19
+ - [#1609](https://github.com/graphcommerce-org/graphcommerce/pull/1609) [`e573278e4`](https://github.com/graphcommerce-org/graphcommerce/commit/e573278e43506a6b17a2981e61d0e9fad41eb2eb) Thanks [@ErwinOtten](https://github.com/ErwinOtten)! - Remove Cart error message when there isn’t a cart present yet
20
+
21
+ - Updated dependencies [[`04708dacc`](https://github.com/graphcommerce-org/graphcommerce/commit/04708daccc213c6ea927bc67fa3bd0d5b1fad619), [`bb94e7045`](https://github.com/graphcommerce-org/graphcommerce/commit/bb94e7045460cb671c45d612a0833731d7c20c30), [`b0dc4e2e1`](https://github.com/graphcommerce-org/graphcommerce/commit/b0dc4e2e1982d502d38dd50a0f493396360a7a15), [`4a5286dfe`](https://github.com/graphcommerce-org/graphcommerce/commit/4a5286dfeaa1719e594a0078f274fbab53969c4e), [`0ad5159eb`](https://github.com/graphcommerce-org/graphcommerce/commit/0ad5159ebef54b4ce7fee6f71b4bf710dba9ef8e), [`d46d5ed0c`](https://github.com/graphcommerce-org/graphcommerce/commit/d46d5ed0cc5794391b7527fc17bbb68ec2212e33), [`5f781a217`](https://github.com/graphcommerce-org/graphcommerce/commit/5f781a217ce63ed56bc1a9983487b04400a8a315), [`01372b918`](https://github.com/graphcommerce-org/graphcommerce/commit/01372b918a291e01cbf5db40edcb40fb1c2af313)]:
22
+ - @graphcommerce/next-ui@4.22.0
23
+ - @graphcommerce/ecommerce-ui@1.2.0
24
+ - @graphcommerce/magento-customer@4.10.0
25
+ - @graphcommerce/framer-next-pages@3.3.0
26
+ - @graphcommerce/framer-utils@3.2.0
27
+ - @graphcommerce/magento-store@4.2.28
28
+ - @graphcommerce/framer-scroller@2.1.33
29
+ - @graphcommerce/graphql@3.4.7
30
+ - @graphcommerce/image@3.1.9
31
+ - @graphcommerce/magento-graphql@3.1.7
32
+
33
+ ## 4.6.9
34
+
35
+ ### Patch Changes
36
+
37
+ - Updated dependencies [[`1f7ee6f6c`](https://github.com/graphcommerce-org/graphcommerce/commit/1f7ee6f6cfb28544439ed36e10929ac530d1b2b7), [`707dbc73d`](https://github.com/graphcommerce-org/graphcommerce/commit/707dbc73d181204d88fdbbd2e09340e25b2b5f7b), [`5c5645e6e`](https://github.com/graphcommerce-org/graphcommerce/commit/5c5645e6eaf5314c063f05547707fcd4b34a8717)]:
38
+ - @graphcommerce/next-ui@4.21.0
39
+ - @graphcommerce/framer-utils@3.1.5
40
+ - @graphcommerce/graphql@3.4.6
41
+ - @graphcommerce/ecommerce-ui@1.1.12
42
+ - @graphcommerce/framer-scroller@2.1.32
43
+ - @graphcommerce/magento-customer@4.9.5
44
+ - @graphcommerce/magento-store@4.2.27
45
+ - @graphcommerce/framer-next-pages@3.2.5
46
+ - @graphcommerce/image@3.1.8
47
+ - @graphcommerce/magento-graphql@3.1.6
48
+
49
+ ## 4.6.8
50
+
51
+ ### Patch Changes
52
+
53
+ - Updated dependencies [[`43822fd61`](https://github.com/graphcommerce-org/graphcommerce/commit/43822fd61c949215b8ddce9fb37d09f29b638426), [`3a619b70d`](https://github.com/graphcommerce-org/graphcommerce/commit/3a619b70d082804b8de46a8e8232f9431479a8b7), [`3a619b70d`](https://github.com/graphcommerce-org/graphcommerce/commit/3a619b70d082804b8de46a8e8232f9431479a8b7)]:
54
+ - @graphcommerce/next-ui@4.20.0
55
+ - @graphcommerce/magento-customer@4.9.4
56
+ - @graphcommerce/ecommerce-ui@1.1.11
57
+ - @graphcommerce/framer-scroller@2.1.31
58
+ - @graphcommerce/magento-store@4.2.26
59
+
3
60
  ## 4.6.7
4
61
 
5
62
  ### Patch Changes
@@ -2,31 +2,9 @@ import {
2
2
  ApolloCustomerErrorAlert,
3
3
  ApolloCustomerErrorAlertProps,
4
4
  } from '@graphcommerce/magento-customer'
5
- import { graphqlErrorByCategory } from '@graphcommerce/magento-graphql'
6
- import { Trans } from '@lingui/react'
7
- import { Button } from '@mui/material'
8
- import { useClearCurrentCartId } from '../../hooks/useClearCurrentCartId'
9
5
 
10
6
  export type ApolloCartErrorAlertProps = ApolloCustomerErrorAlertProps
11
7
 
12
8
  export function ApolloCartErrorAlert(props: ApolloCartErrorAlertProps) {
13
- const { error, graphqlErrorAlertProps } = props
14
- const clear = useClearCurrentCartId()
15
-
16
- const [, noSuchEntity] = graphqlErrorByCategory({ category: 'graphql-no-such-entity', error })
17
-
18
- return (
19
- <ApolloCustomerErrorAlert
20
- {...props}
21
- graphqlErrorAlertProps={{
22
- action: noSuchEntity ? (
23
- <Button onClick={clear}>
24
- <Trans id='Reset Cart' />
25
- </Button>
26
- ) : (
27
- graphqlErrorAlertProps?.action
28
- ),
29
- }}
30
- />
31
- )
9
+ return <ApolloCustomerErrorAlert {...props} />
32
10
  }
@@ -2,49 +2,12 @@ import {
2
2
  ApolloCustomerErrorFullPage,
3
3
  ApolloCustomerErrorFullPageProps,
4
4
  } from '@graphcommerce/magento-customer'
5
- import { graphqlErrorByCategory } from '@graphcommerce/magento-graphql'
6
5
  import { iconShoppingBag, IconSvg } from '@graphcommerce/next-ui'
7
- import { Trans } from '@lingui/react'
8
- import { Button } from '@mui/material'
9
- import { useCurrentCartId } from '../../hooks'
10
- import { useClearCurrentCartId } from '../../hooks/useClearCurrentCartId'
11
- import { EmptyCart } from '../EmptyCart/EmptyCart'
12
6
 
13
7
  export type ApolloCartErrorFullPageProps = Omit<ApolloCustomerErrorFullPageProps, 'icon'>
14
8
 
15
9
  export function ApolloCartErrorFullPage(props: ApolloCartErrorFullPageProps) {
16
- const { error, button } = props
17
- const clear = useClearCurrentCartId()
18
- const { currentCartId } = useCurrentCartId()
19
-
20
- const [, noSuchEntity] = graphqlErrorByCategory({ category: 'graphql-no-such-entity', error })
21
-
22
- if (noSuchEntity)
23
- return (
24
- <EmptyCart
25
- button={
26
- currentCartId ? (
27
- <Button onClick={clear}>
28
- <Trans id='Reset Cart' />
29
- </Button>
30
- ) : undefined
31
- }
32
- />
33
- )
34
-
35
10
  return (
36
- <ApolloCustomerErrorFullPage
37
- {...props}
38
- icon={<IconSvg src={iconShoppingBag} size='xxl' />}
39
- button={
40
- noSuchEntity ? (
41
- <Button onClick={clear}>
42
- <Trans id='Reset Cart' />
43
- </Button>
44
- ) : (
45
- button
46
- )
47
- }
48
- />
11
+ <ApolloCustomerErrorFullPage {...props} icon={<IconSvg src={iconShoppingBag} size='xxl' />} />
49
12
  )
50
13
  }
@@ -10,23 +10,5 @@ import { useClearCurrentCartId } from '../../hooks/useClearCurrentCartId'
10
10
  export type ApolloCartErrorSnackbarProps = ApolloCustomerErrorSnackbarProps
11
11
 
12
12
  export function ApolloCartErrorSnackbar(props: ApolloCartErrorSnackbarProps) {
13
- const { error, action } = props
14
- const clear = useClearCurrentCartId()
15
-
16
- const [, noSuchEntity] = graphqlErrorByCategory({ category: 'graphql-no-such-entity', error })
17
-
18
- return (
19
- <ApolloCustomerErrorSnackbar
20
- {...props}
21
- action={
22
- noSuchEntity ? (
23
- <Button onClick={clear} variant='pill' color='secondary'>
24
- <Trans id='Reset Cart' />
25
- </Button>
26
- ) : (
27
- action
28
- )
29
- }
30
- />
31
- )
13
+ return <ApolloCustomerErrorSnackbar {...props} />
32
14
  }
@@ -0,0 +1,38 @@
1
+ import { useApolloClient } from '@graphcommerce/graphql'
2
+ import { Button } from '@mui/material'
3
+ import { CurrentCartIdDocument } from '../../hooks/CurrentCartId.gql'
4
+
5
+ export function CartDebugger() {
6
+ const client = useApolloClient()
7
+
8
+ return (
9
+ <div style={{ position: 'fixed', bottom: 0, right: 0, zIndex: 1, opacity: 0.3 }}>
10
+ <Button
11
+ type='button'
12
+ variant='text'
13
+ size='small'
14
+ onClick={() => {
15
+ const currentCardId = client.readQuery({ query: CurrentCartIdDocument })
16
+ if (!currentCardId?.currentCartId) {
17
+ console.log('No customerToken available, nothing to break')
18
+ } else {
19
+ console.log(`Changing current token to a random one)`)
20
+
21
+ client.writeQuery({
22
+ query: CurrentCartIdDocument,
23
+ data: {
24
+ currentCartId: {
25
+ ...currentCardId.currentCartId,
26
+ id: `${Math.random().toString(36).slice(2)}random-cardId`,
27
+ },
28
+ },
29
+ broadcast: true,
30
+ })
31
+ }
32
+ }}
33
+ >
34
+ break cart
35
+ </Button>
36
+ </div>
37
+ )
38
+ }
@@ -7,6 +7,7 @@ import {
7
7
  SectionContainer,
8
8
  IconSvg,
9
9
  extendableComponent,
10
+ breakpointVal,
10
11
  } from '@graphcommerce/next-ui'
11
12
  import { Trans } from '@lingui/react'
12
13
  import { Box, Divider, SxProps, Theme } from '@mui/material'
@@ -47,7 +48,12 @@ export function CartItemSummary(props: OrderSummaryProps) {
47
48
  (theme) => ({
48
49
  padding: `${theme.spacings.sm} ${theme.spacings.sm}`,
49
50
  border: `1px ${theme.palette.divider} solid`,
50
- borderRadius: '4px',
51
+ ...breakpointVal(
52
+ 'borderRadius',
53
+ theme.shape.borderRadius * 2,
54
+ theme.shape.borderRadius * 3,
55
+ theme.breakpoints.values,
56
+ ),
51
57
  }),
52
58
  ...(Array.isArray(sx) ? sx : [sx]),
53
59
  ]}
@@ -65,9 +71,6 @@ export function CartItemSummary(props: OrderSummaryProps) {
65
71
  <Box
66
72
  className={classes.imageScrollerContainer}
67
73
  sx={(theme) => ({
68
- display: 'flex',
69
- alignItems: 'center',
70
- gap: theme.spacings.sm,
71
74
  position: 'relative',
72
75
  })}
73
76
  >
@@ -137,7 +140,7 @@ export function CartItemSummary(props: OrderSummaryProps) {
137
140
  />
138
141
  <CartTotals
139
142
  sx={(theme) => ({
140
- background: theme.palette.background.default,
143
+ background: 'none',
141
144
  padding: 0,
142
145
  })}
143
146
  />
@@ -1,7 +1,7 @@
1
1
  import { useHistoryLink } from '@graphcommerce/framer-next-pages'
2
- import { SectionContainer, extendableComponent } from '@graphcommerce/next-ui'
2
+ import { SectionContainer, extendableComponent, breakpointVal } from '@graphcommerce/next-ui'
3
3
  import { Trans } from '@lingui/react'
4
- import { Box, Link, SxProps, Theme, Typography } from '@mui/material'
4
+ import { Box, Link, SxProps, Theme, Typography, lighten } from '@mui/material'
5
5
  import PageLink from 'next/link'
6
6
  import React from 'react'
7
7
  import { useCartQuery } from '../../hooks'
@@ -37,8 +37,20 @@ export function CartSummary(props: CartSummaryProps) {
37
37
  sx={[
38
38
  (theme) => ({
39
39
  margin: `${theme.spacings.sm} 0`,
40
- '& div:last-of-type': {
41
- borderRadius: '0 0 4px 4px',
40
+ '& > div:last-of-type': {
41
+ borderRadius: '0',
42
+ ...breakpointVal(
43
+ 'borderBottomLeftRadius',
44
+ theme.shape.borderRadius * 2,
45
+ theme.shape.borderRadius * 3,
46
+ theme.breakpoints.values,
47
+ ),
48
+ ...breakpointVal(
49
+ 'borderBottomRightRadius',
50
+ theme.shape.borderRadius * 2,
51
+ theme.shape.borderRadius * 3,
52
+ theme.breakpoints.values,
53
+ ),
42
54
  },
43
55
  }),
44
56
  ...(Array.isArray(sx) ? sx : [sx]),
@@ -47,8 +59,22 @@ export function CartSummary(props: CartSummaryProps) {
47
59
  <Box
48
60
  className={classes.detailsContainer}
49
61
  sx={(theme) => ({
50
- borderRadius: '4px 4px 0 0',
51
- background: theme.palette.mode === 'light' ? '#FFE10820' : theme.palette.background.paper,
62
+ ...breakpointVal(
63
+ 'borderTopLeftRadius',
64
+ theme.shape.borderRadius * 2,
65
+ theme.shape.borderRadius * 3,
66
+ theme.breakpoints.values,
67
+ ),
68
+ ...breakpointVal(
69
+ 'borderTopRightRadius',
70
+ theme.shape.borderRadius * 2,
71
+ theme.shape.borderRadius * 3,
72
+ theme.breakpoints.values,
73
+ ),
74
+ background:
75
+ theme.palette.mode === 'light'
76
+ ? theme.palette.background.default
77
+ : lighten(theme.palette.background.default, 0.15),
52
78
  padding: theme.spacings.sm,
53
79
  gridColumnGap: theme.spacings.xxl,
54
80
  gridRowGap: theme.spacings.sm,
@@ -1,8 +1,8 @@
1
1
  import { Money } from '@graphcommerce/magento-store'
2
- import { AnimatedRow, extendableComponent, responsiveVal } from '@graphcommerce/next-ui'
2
+ import { extendableComponent, breakpointVal } from '@graphcommerce/next-ui'
3
3
  import { Trans } from '@lingui/react'
4
4
  import { Box, Divider, lighten, SxProps, Theme } from '@mui/material'
5
- import { AnimatePresence } from 'framer-motion'
5
+ import { useRouter } from 'next/router'
6
6
  import { useCartQuery, useDisplayInclTax } from '../../hooks'
7
7
  import { GetCartTotalsDocument } from './GetCartTotals.gql'
8
8
 
@@ -30,6 +30,8 @@ const { withState } = extendableComponent<OwnerProps, typeof name, typeof parts>
30
30
  export function CartTotals(props: CartTotalsProps) {
31
31
  const { data } = useCartQuery(GetCartTotalsDocument, { allowUrl: true })
32
32
  const { containerMargin, sx = [] } = props
33
+ const { asPath } = useRouter()
34
+ const animateLayout = asPath === '/checkout/payment' ? undefined : true
33
35
 
34
36
  const classes = withState({ containerMargin })
35
37
  const inclTax = useDisplayInclTax()
@@ -47,14 +49,19 @@ export function CartTotals(props: CartTotalsProps) {
47
49
  )
48
50
 
49
51
  return (
50
- <AnimatedRow
52
+ <Box
51
53
  className={classes.root}
52
54
  sx={[
53
55
  (theme) => ({
54
- borderRadius: responsiveVal(theme.shape.borderRadius * 3, theme.shape.borderRadius * 4),
56
+ ...breakpointVal(
57
+ 'borderRadius',
58
+ theme.shape.borderRadius * 3,
59
+ theme.shape.borderRadius * 5,
60
+ theme.breakpoints.values,
61
+ ),
55
62
  background:
56
63
  theme.palette.mode === 'light'
57
- ? '#FFE10820'
64
+ ? theme.palette.background.default
58
65
  : lighten(theme.palette.background.default, 0.15),
59
66
  padding: `${theme.spacings.xs} ${theme.spacings.sm}`,
60
67
 
@@ -66,131 +73,124 @@ export function CartTotals(props: CartTotalsProps) {
66
73
  ]}
67
74
  key='total-costs'
68
75
  >
69
- <AnimatePresence initial={false}>
70
- {prices?.subtotal_including_tax && (
71
- <AnimatedRow
72
- className={classes.costsRow}
73
- key='subtotal'
74
- sx={{ display: 'flex', justifyContent: 'space-between', typography: 'subtitle1' }}
76
+ {prices?.subtotal_including_tax && (
77
+ <Box
78
+ className={classes.costsRow}
79
+ key='subtotal'
80
+ sx={{ display: 'flex', justifyContent: 'space-between' }}
81
+ >
82
+ <Box>
83
+ <Trans id='Products' />
84
+ </Box>
85
+ <Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
86
+ <Money {...(inclTax ? prices.subtotal_including_tax : prices.subtotal_excluding_tax)} />
87
+ </Box>
88
+ </Box>
89
+ )}
90
+
91
+ {prices?.discounts?.map((discount) => {
92
+ const value = inclTax
93
+ ? (discount?.amount.value ?? 0) * -1
94
+ : (discount?.amount.value ?? 0) *
95
+ ((prices.subtotal_excluding_tax?.value ?? 1) /
96
+ (prices.subtotal_including_tax?.value ?? 1)) *
97
+ -1
98
+
99
+ return (
100
+ <Box
101
+ key={discount?.label}
102
+ sx={{
103
+ display: 'flex',
104
+ justifyContent: 'space-between',
105
+ }}
75
106
  >
76
- <Box>
77
- <Trans id='Products' />
78
- </Box>
107
+ <Box>{discount?.label}</Box>
79
108
  <Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
80
- <Money
81
- {...(inclTax ? prices.subtotal_including_tax : prices.subtotal_excluding_tax)}
82
- />
109
+ {discount?.amount && <Money {...discount.amount} value={value} />}
83
110
  </Box>
84
- </AnimatedRow>
85
- )}
86
-
87
- {prices?.discounts?.map((discount) => {
88
- const value = inclTax
89
- ? (discount?.amount.value ?? 0) * -1
90
- : (discount?.amount.value ?? 0) *
91
- ((prices.subtotal_excluding_tax?.value ?? 1) /
92
- (prices.subtotal_including_tax?.value ?? 1)) *
93
- -1
94
-
95
- return (
96
- <AnimatedRow
97
- key={discount?.label}
98
- sx={{
99
- display: 'flex',
100
- justifyContent: 'space-between',
101
- typography: 'subtitle1',
102
- }}
103
- >
104
- <Box>{discount?.label}</Box>
105
- <Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
106
- {discount?.amount && <Money {...discount.amount} value={value} />}
107
- </Box>
108
- </AnimatedRow>
109
- )
110
- })}
111
-
112
- {shippingMethod && (
113
- <AnimatedRow
111
+ </Box>
112
+ )
113
+ })}
114
+
115
+ {shippingMethod && (
116
+ <Box
117
+ className={classes.costsRow}
118
+ key='shippingMethod'
119
+ sx={{ display: 'flex', justifyContent: 'space-between' }}
120
+ >
121
+ <Box>
122
+ <Trans
123
+ id='Shipping ({0} {1})'
124
+ values={{ 0: shippingMethod.carrier_title, 1: shippingMethod.method_title }}
125
+ />
126
+ </Box>
127
+ <Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
128
+ <Money
129
+ {...(inclTax
130
+ ? shippingMethodPrices?.price_incl_tax
131
+ : shippingMethodPrices?.price_excl_tax)}
132
+ />
133
+ </Box>
134
+ </Box>
135
+ )}
136
+
137
+ {!inclTax &&
138
+ prices?.applied_taxes?.map((tax) => (
139
+ <Box
114
140
  className={classes.costsRow}
115
- key='shippingMethod'
116
- sx={{ display: 'flex', justifyContent: 'space-between', typography: 'subtitle1' }}
141
+ key={`excl${tax?.label}`}
142
+ sx={{ display: 'flex', justifyContent: 'space-between' }}
117
143
  >
118
- <Box>
119
- <Trans
120
- id='Shipping ({0} {1})'
121
- values={{ 0: shippingMethod.carrier_title, 1: shippingMethod.method_title }}
122
- />
123
- </Box>
144
+ <Box>{tax?.label}</Box>
124
145
  <Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
125
- <Money
126
- {...(inclTax
127
- ? shippingMethodPrices?.price_incl_tax
128
- : shippingMethodPrices?.price_excl_tax)}
129
- />
146
+ <Money {...tax?.amount} />
130
147
  </Box>
131
- </AnimatedRow>
132
- )}
133
-
134
- {!inclTax &&
135
- prices?.applied_taxes?.map((tax) => (
136
- <AnimatedRow
137
- className={classes.costsRow}
138
- key={`excl${tax?.label}`}
139
- sx={{ display: 'flex', justifyContent: 'space-between', typography: 'subtitle1' }}
140
- >
141
- <Box>{tax?.label}</Box>
142
- <Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
143
- <Money {...tax?.amount} />
144
- </Box>
145
- </AnimatedRow>
146
- ))}
147
-
148
- <AnimatedRow key='divider'>
149
- <Divider className={classes.costsDivider} sx={{ margin: `1em 0` }} />
150
- </AnimatedRow>
151
-
152
- {prices?.grand_total && (
153
- <AnimatedRow
154
- className={`${classes.costsRow} ${classes.costsGrandTotal}`}
155
- key='grand_total'
156
- sx={(theme) => ({
148
+ </Box>
149
+ ))}
150
+
151
+ <Box key='divider'>
152
+ <Divider className={classes.costsDivider} sx={{ margin: `1em 0` }} />
153
+ </Box>
154
+
155
+ {prices?.grand_total && (
156
+ <Box
157
+ className={`${classes.costsRow} ${classes.costsGrandTotal}`}
158
+ key='grand_total'
159
+ sx={(theme) => ({
160
+ display: 'flex',
161
+ justifyContent: 'space-between',
162
+ color: theme.palette.primary.main,
163
+ })}
164
+ >
165
+ <Box>
166
+ <Trans id='Grand total' />
167
+ </Box>
168
+ <Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
169
+ <Money {...prices.grand_total} />
170
+ </Box>
171
+ </Box>
172
+ )}
173
+
174
+ {inclTax &&
175
+ prices?.applied_taxes?.map((tax) => (
176
+ <Box
177
+ className={`${classes.costsRow} ${classes.costsTax}`}
178
+ key={`incl${tax?.label}`}
179
+ sx={{
157
180
  display: 'flex',
158
181
  justifyContent: 'space-between',
159
- typography: 'subtitle1',
160
- color: theme.palette.primary.main,
161
- })}
182
+ color: 'text.disabled',
183
+ paddingTop: 0,
184
+ }}
162
185
  >
163
186
  <Box>
164
- <Trans id='Grand total' />
187
+ <Trans id='Including {0}' values={{ 0: tax?.label }} />
165
188
  </Box>
166
189
  <Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
167
- <Money {...prices.grand_total} />
190
+ <Money {...tax?.amount} />
168
191
  </Box>
169
- </AnimatedRow>
170
- )}
171
-
172
- {inclTax &&
173
- prices?.applied_taxes?.map((tax) => (
174
- <AnimatedRow
175
- className={`${classes.costsRow} ${classes.costsTax}`}
176
- key={`incl${tax?.label}`}
177
- sx={{
178
- display: 'flex',
179
- justifyContent: 'space-between',
180
- typography: 'body1',
181
- color: 'text.disabled',
182
- paddingTop: 0,
183
- }}
184
- >
185
- <Box>
186
- <Trans id='Including {0}' values={{ 0: tax?.label }} />
187
- </Box>
188
- <Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
189
- <Money {...tax?.amount} />
190
- </Box>
191
- </AnimatedRow>
192
- ))}
193
- </AnimatePresence>
194
- </AnimatedRow>
192
+ </Box>
193
+ ))}
194
+ </Box>
195
195
  )
196
196
  }
@@ -1,16 +1,7 @@
1
- import { useQuery, TypedDocumentNode, QueryHookOptions, ApolloError } from '@graphcommerce/graphql'
2
- import { GraphQLError } from 'graphql'
1
+ import { useQuery, TypedDocumentNode, QueryHookOptions } from '@graphcommerce/graphql'
3
2
  import { useRouter } from 'next/router'
4
3
  import { useCurrentCartId } from './useCurrentCartId'
5
4
 
6
- const noCartError = new ApolloError({
7
- graphQLErrors: [
8
- new GraphQLError('No cart found', {
9
- extensions: { category: 'graphql-no-such-entity' },
10
- }),
11
- ],
12
- })
13
-
14
5
  /**
15
6
  * Requires the query to have a `$cartId: String!` argument. It will automatically inject the
16
7
  * currently active cart_id.
@@ -30,7 +21,7 @@ export function useCartQuery<Q, V extends { cartId: string; [index: string]: unk
30
21
  ) {
31
22
  const { allowUrl = true, hydration, ...queryOptions } = options
32
23
  const router = useRouter()
33
- const { currentCartId, called } = useCurrentCartId({ hydration })
24
+ const { currentCartId } = useCurrentCartId({ hydration })
34
25
 
35
26
  const urlCartId = router.query.cart_id
36
27
  const usingUrl = allowUrl && typeof urlCartId === 'string'
@@ -49,8 +40,7 @@ export function useCartQuery<Q, V extends { cartId: string; [index: string]: unk
49
40
  const result = useQuery(document, queryOptions)
50
41
 
51
42
  return {
52
- ...useQuery(document, queryOptions),
53
43
  ...result,
54
- error: called && !currentCartId ? noCartError : result.error,
44
+ // error: called && !currentCartId ? noCartError : result.error,
55
45
  }
56
46
  }
package/index.ts CHANGED
@@ -2,3 +2,5 @@ export * from './Api/CartItemCountChanged.gql'
2
2
  export * from './hooks'
3
3
  export * from './components'
4
4
  export * from './typePolicies'
5
+ export * from './link/createCartErrorLink'
6
+ export * from './components/CartDebugger/CartDebugger'
@@ -0,0 +1,71 @@
1
+ import {
2
+ ApolloClient,
3
+ FetchResult,
4
+ fromPromise,
5
+ InMemoryCache,
6
+ NormalizedCacheObject,
7
+ onError,
8
+ Operation,
9
+ } from '@graphcommerce/graphql'
10
+ import { RefObject } from 'react'
11
+ import { useAssignCurrentCartId, writeCartId } from '../hooks'
12
+ import { CreateEmptyCartDocument } from '../hooks/CreateEmptyCart.gql'
13
+ import { CurrentCartIdDocument } from '../hooks/CurrentCartId.gql'
14
+
15
+ type CartOperation = Operation & { variables: { cartId: string } }
16
+ function isCartOperation(operation: Operation): operation is CartOperation {
17
+ return typeof operation.variables.cartId === 'string'
18
+ }
19
+
20
+ function errorIsIncluded(errorPath: readonly (string | number)[] | undefined, keys: string[]) {
21
+ const error = errorPath?.join()
22
+ return keys.some((value) => value === error)
23
+ }
24
+
25
+ export const createCartErrorLink = (clientRef: RefObject<ApolloClient<NormalizedCacheObject>>) =>
26
+ onError(({ graphQLErrors, operation, forward }) => {
27
+ if (!clientRef.current) return undefined
28
+
29
+ const client = clientRef.current
30
+ const { cache } = client
31
+
32
+ if (!isCartOperation(operation) || !graphQLErrors) return undefined
33
+
34
+ const cartErr = graphQLErrors.find(
35
+ (err) =>
36
+ err.extensions?.category === 'graphql-no-such-entity' &&
37
+ errorIsIncluded(err.path, [
38
+ 'cart',
39
+ 'addProductsToCart',
40
+
41
+ /**
42
+ * These mutations can also throw the graphql-no-such-entity exception, however, we're not
43
+ * sure if it also throws for other types of entities.
44
+ */
45
+ // 'removeItemFromCart',
46
+ // 'setBillingAddressOnCart',
47
+ // 'setGuestEmailOnCart',
48
+ // 'setPaymentMethodOnCart',
49
+ // 'setShippingAddressesOnCart',
50
+ // 'setShippingMethodsOnCart',
51
+ // 'updateCartItems',
52
+ // 'applyCouponToCart',
53
+ // 'removeCouponFromCart'
54
+ ]),
55
+ )
56
+
57
+ if (!cartErr) return undefined
58
+
59
+ return fromPromise(client?.mutate({ mutation: CreateEmptyCartDocument }))
60
+ .filter((value) => Boolean(value))
61
+ .flatMap((cartData) => {
62
+ const cartId = cartData.data?.createEmptyCart
63
+ if (!cartId) return forward(operation)
64
+
65
+ writeCartId(cache, cartId)
66
+ operation.variables = { ...operation.variables, cartId }
67
+
68
+ // retry the request, returning the new observable
69
+ return forward(operation)
70
+ })
71
+ })
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": "4.6.7",
5
+ "version": "4.7.0",
6
6
  "sideEffects": false,
7
7
  "prettier": "@graphcommerce/prettier-config-pwa",
8
8
  "eslintConfig": {
@@ -12,22 +12,22 @@
12
12
  }
13
13
  },
14
14
  "devDependencies": {
15
- "@graphcommerce/eslint-config-pwa": "^4.1.9",
15
+ "@graphcommerce/eslint-config-pwa": "^4.1.10",
16
16
  "@graphcommerce/prettier-config-pwa": "^4.0.6",
17
17
  "@graphcommerce/typescript-config-pwa": "^4.0.4",
18
18
  "@playwright/test": "^1.21.1"
19
19
  },
20
20
  "dependencies": {
21
- "@graphcommerce/ecommerce-ui": "1.1.10",
22
- "@graphcommerce/framer-utils": "3.1.4",
23
- "@graphcommerce/framer-next-pages": "3.2.4",
24
- "@graphcommerce/framer-scroller": "2.1.30",
25
- "@graphcommerce/graphql": "3.4.5",
26
- "@graphcommerce/image": "3.1.7",
27
- "@graphcommerce/magento-customer": "4.9.3",
28
- "@graphcommerce/magento-graphql": "3.1.5",
29
- "@graphcommerce/magento-store": "4.2.25",
30
- "@graphcommerce/next-ui": "4.19.0",
21
+ "@graphcommerce/ecommerce-ui": "1.2.0",
22
+ "@graphcommerce/framer-utils": "3.2.0",
23
+ "@graphcommerce/framer-next-pages": "3.3.0",
24
+ "@graphcommerce/framer-scroller": "2.1.33",
25
+ "@graphcommerce/graphql": "3.4.7",
26
+ "@graphcommerce/image": "3.1.9",
27
+ "@graphcommerce/magento-customer": "4.10.0",
28
+ "@graphcommerce/magento-graphql": "3.1.7",
29
+ "@graphcommerce/magento-store": "4.2.28",
30
+ "@graphcommerce/next-ui": "4.22.0",
31
31
  "@graphcommerce/react-hook-form": "3.3.2"
32
32
  },
33
33
  "peerDependencies": {