@graphcommerce/magento-product 4.7.3 → 4.8.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.
- package/CHANGELOG.md +34 -0
- package/components/AddProductsToCart/AddProductsToCartButton.tsx +21 -38
- package/components/AddProductsToCart/AddProductsToCartError.tsx +1 -1
- package/components/AddProductsToCart/AddProductsToCartFab.tsx +18 -0
- package/components/AddProductsToCart/AddProductsToCartForm.tsx +75 -68
- package/components/AddProductsToCart/AddProductsToCartQuantity.tsx +1 -1
- package/components/AddProductsToCart/AddProductsToCartSnackbar.tsx +59 -44
- package/components/AddProductsToCart/index.ts +2 -1
- package/components/AddProductsToCart/useAddProductsToCartAction.ts +40 -0
- package/components/AddProductsToCart/useFormAddProductsToCart.ts +30 -0
- package/components/ProductPageBreadcrumb/ProductPageBreadcrumb.tsx +22 -24
- package/components/ProductPageGallery/ProductPageGallery.tsx +17 -9
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 4.8.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#1684](https://github.com/graphcommerce-org/graphcommerce/pull/1684) [`aab6b4fa5`](https://github.com/graphcommerce-org/graphcommerce/commit/aab6b4fa5b4708003cfb5bf673a617dc5dbf3078) Thanks [@paales](https://github.com/paales)! - Make sure the images are sorted according to their position field
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`ae28fb14c`](https://github.com/graphcommerce-org/graphcommerce/commit/ae28fb14cec298c52970260a4fc2c2551b5f175e), [`98d6a9cce`](https://github.com/graphcommerce-org/graphcommerce/commit/98d6a9cce1bb9514088be0af2736721b3edda467)]:
|
|
10
|
+
- @graphcommerce/magento-cart@4.9.2
|
|
11
|
+
- @graphcommerce/next-ui@4.29.1
|
|
12
|
+
- @graphcommerce/ecommerce-ui@1.5.6
|
|
13
|
+
- @graphcommerce/framer-scroller@2.1.43
|
|
14
|
+
- @graphcommerce/magento-store@4.3.4
|
|
15
|
+
|
|
16
|
+
## 4.8.0
|
|
17
|
+
|
|
18
|
+
### Minor Changes
|
|
19
|
+
|
|
20
|
+
- [#1679](https://github.com/graphcommerce-org/graphcommerce/pull/1679) [`2b5451395`](https://github.com/graphcommerce-org/graphcommerce/commit/2b5451395dc1173de55d18d08968866e561f90ab) Thanks [@paales](https://github.com/paales)! - Move AddProductsToCartSnackbar to inside AddProductsToCartForm and make AddProductsToCartForm configrable via theme.ts
|
|
21
|
+
|
|
22
|
+
- [#1679](https://github.com/graphcommerce-org/graphcommerce/pull/1679) [`e76df6dc3`](https://github.com/graphcommerce-org/graphcommerce/commit/e76df6dc37c11c793a5d008ba36932d17dc23855) Thanks [@paales](https://github.com/paales)! - Added AddProductsToCartFab for a smaller add to cart button
|
|
23
|
+
|
|
24
|
+
- [#1678](https://github.com/graphcommerce-org/graphcommerce/pull/1678) [`78d7d51cb`](https://github.com/graphcommerce-org/graphcommerce/commit/78d7d51cb1551601d3a4756cd1f2157a49ff93b9) Thanks [@Jessevdpoel](https://github.com/Jessevdpoel)! - Changed styling and forwarded breadcrumbprops
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- [#1679](https://github.com/graphcommerce-org/graphcommerce/pull/1679) [`c4ed376e2`](https://github.com/graphcommerce-org/graphcommerce/commit/c4ed376e2c72b16b34704d7d1ca69c074de172ba) Thanks [@paales](https://github.com/paales)! - Support passing children to AddProductsToCartButton instead of Add To Cart
|
|
29
|
+
|
|
30
|
+
- Updated dependencies [[`e76df6dc3`](https://github.com/graphcommerce-org/graphcommerce/commit/e76df6dc37c11c793a5d008ba36932d17dc23855), [`0bd9ea582`](https://github.com/graphcommerce-org/graphcommerce/commit/0bd9ea58230dde79c5fe2cdb07e9860151460270)]:
|
|
31
|
+
- @graphcommerce/next-ui@4.29.0
|
|
32
|
+
- @graphcommerce/ecommerce-ui@1.5.5
|
|
33
|
+
- @graphcommerce/framer-scroller@2.1.42
|
|
34
|
+
- @graphcommerce/magento-cart@4.9.1
|
|
35
|
+
- @graphcommerce/magento-store@4.3.3
|
|
36
|
+
|
|
3
37
|
## 4.7.3
|
|
4
38
|
|
|
5
39
|
### Patch Changes
|
|
@@ -1,48 +1,31 @@
|
|
|
1
1
|
import { Button, ButtonProps } from '@graphcommerce/next-ui'
|
|
2
2
|
import { Trans } from '@lingui/react'
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
useAddProductsToCartAction,
|
|
5
|
+
UseAddProductsToCartActionProps,
|
|
6
|
+
} from './useAddProductsToCartAction'
|
|
5
7
|
|
|
6
|
-
export type AddProductsToCartButtonProps =
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
| 'onClick'
|
|
20
|
-
| 'loading'
|
|
21
|
-
>
|
|
8
|
+
export type AddProductsToCartButtonProps = UseAddProductsToCartActionProps &
|
|
9
|
+
Pick<
|
|
10
|
+
ButtonProps<'button'>,
|
|
11
|
+
| 'variant'
|
|
12
|
+
| 'color'
|
|
13
|
+
| 'size'
|
|
14
|
+
| 'fullWidth'
|
|
15
|
+
| 'startIcon'
|
|
16
|
+
| 'endIcon'
|
|
17
|
+
| 'onClick'
|
|
18
|
+
| 'sx'
|
|
19
|
+
| 'children'
|
|
20
|
+
>
|
|
22
21
|
|
|
23
22
|
export function AddProductsToCartButton(props: AddProductsToCartButtonProps) {
|
|
24
|
-
const {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
const clickHandler: NonNullable<AddProductsToCartButtonProps['onClick']> = useEventCallback(
|
|
28
|
-
(e) => {
|
|
29
|
-
setValue(`cartItems.${index}.sku`, sku)
|
|
30
|
-
onClick?.(e)
|
|
31
|
-
},
|
|
32
|
-
)
|
|
23
|
+
const { children } = props
|
|
24
|
+
const action = useAddProductsToCartAction(props)
|
|
33
25
|
|
|
34
26
|
return (
|
|
35
|
-
<Button
|
|
36
|
-
|
|
37
|
-
color='primary'
|
|
38
|
-
variant='pill'
|
|
39
|
-
size='large'
|
|
40
|
-
{...props}
|
|
41
|
-
disabled={Boolean(formState.errors.cartItems?.[index].sku?.message) || disabled}
|
|
42
|
-
loading={formState.isSubmitting || loading}
|
|
43
|
-
onClick={clickHandler}
|
|
44
|
-
>
|
|
45
|
-
<Trans id='Add to Cart' />
|
|
27
|
+
<Button type='submit' color='primary' variant='pill' size='large' {...props} {...action}>
|
|
28
|
+
{children || <Trans id='Add to Cart' />}
|
|
46
29
|
</Button>
|
|
47
30
|
)
|
|
48
31
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Fab, FabProps, iconShoppingBag } from '@graphcommerce/next-ui'
|
|
2
|
+
import { SxProps, Theme } from '@mui/material'
|
|
3
|
+
import {
|
|
4
|
+
useAddProductsToCartAction,
|
|
5
|
+
UseAddProductsToCartActionProps,
|
|
6
|
+
} from './useAddProductsToCartAction'
|
|
7
|
+
|
|
8
|
+
export type AddProductsToCartFabProps = {
|
|
9
|
+
sx?: SxProps<Theme>
|
|
10
|
+
icon?: FabProps['icon']
|
|
11
|
+
} & Pick<FabProps, 'color' | 'size'> &
|
|
12
|
+
UseAddProductsToCartActionProps
|
|
13
|
+
|
|
14
|
+
export function AddProductsToCartFab(props: AddProductsToCartFabProps) {
|
|
15
|
+
const { icon = iconShoppingBag } = props
|
|
16
|
+
const action = useAddProductsToCartAction(props)
|
|
17
|
+
return <Fab type='submit' {...props} {...action} icon={icon} />
|
|
18
|
+
}
|
|
@@ -1,104 +1,111 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { UseFormGraphQlOptions } from '@graphcommerce/ecommerce-ui'
|
|
2
2
|
import { useFormGqlMutationCart } from '@graphcommerce/magento-cart'
|
|
3
|
-
import {
|
|
3
|
+
import { ExtendableComponent } from '@graphcommerce/next-ui'
|
|
4
|
+
import { Box, SxProps, Theme, useThemeProps } from '@mui/material'
|
|
4
5
|
import { useRouter } from 'next/router'
|
|
5
|
-
import {
|
|
6
|
+
import { useMemo } from 'react'
|
|
6
7
|
import {
|
|
7
8
|
AddProductsToCartDocument,
|
|
8
9
|
AddProductsToCartMutation,
|
|
9
10
|
AddProductsToCartMutationVariables,
|
|
10
11
|
} from './AddProductsToCart.gql'
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
type AddProductsToCartContextType = UseFormGqlMutationReturn<
|
|
19
|
-
AddProductsToCartMutation,
|
|
20
|
-
AddProductsToCartFormState
|
|
21
|
-
>
|
|
22
|
-
|
|
23
|
-
export const addProductsToCartContext = createContext(
|
|
24
|
-
undefined as AddProductsToCartContextType | undefined,
|
|
25
|
-
)
|
|
12
|
+
import {
|
|
13
|
+
AddProductsToCartSnackbar,
|
|
14
|
+
AddProductsToCartSnackbarProps,
|
|
15
|
+
} from './AddProductsToCartSnackbar'
|
|
16
|
+
import { AddProductsToCartContext, RedirectType } from './useFormAddProductsToCart'
|
|
26
17
|
|
|
27
18
|
type AddProductsToCartFormProps = {
|
|
19
|
+
// The props are actually used, but are passed through useThemeProps and that breaks react/no-unused-prop-types
|
|
20
|
+
// eslint-disable-next-line react/no-unused-prop-types
|
|
28
21
|
children: React.ReactNode
|
|
22
|
+
// eslint-disable-next-line react/no-unused-prop-types
|
|
29
23
|
sx?: SxProps<Theme>
|
|
24
|
+
// eslint-disable-next-line react/no-unused-prop-types
|
|
30
25
|
redirect?: RedirectType
|
|
31
|
-
} &
|
|
32
|
-
|
|
33
|
-
'onBeforeSubmit'
|
|
34
|
-
>
|
|
26
|
+
} & UseFormGraphQlOptions<AddProductsToCartMutation, AddProductsToCartMutationVariables> &
|
|
27
|
+
AddProductsToCartSnackbarProps
|
|
35
28
|
|
|
29
|
+
const name = 'AddProductsToCartForm'
|
|
30
|
+
|
|
31
|
+
/** Expose the component to be exendable in your theme.components */
|
|
32
|
+
declare module '@mui/material/styles/components' {
|
|
33
|
+
interface Components {
|
|
34
|
+
AddProductsToCartForm?: Pick<
|
|
35
|
+
ExtendableComponent<Omit<AddProductsToCartFormProps, 'children'>>,
|
|
36
|
+
'defaultProps'
|
|
37
|
+
>
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Component that handles adding products to the cart. Used on the product page, but can be used for
|
|
43
|
+
* any product listing.
|
|
44
|
+
*
|
|
45
|
+
* Can be configured globally in your theme.ts;
|
|
46
|
+
*
|
|
47
|
+
* - Uses react-hook-form's useForm hook under the hood and exposes the form as a context which can be
|
|
48
|
+
* consumed with `useFormAddProductsToCart` hook.
|
|
49
|
+
* - Cleans up the submitted data.
|
|
50
|
+
* - Redirects the user to the cart/checkout/added page after successful submission.
|
|
51
|
+
*/
|
|
36
52
|
export function AddProductsToCartForm(props: AddProductsToCartFormProps) {
|
|
37
|
-
const {
|
|
53
|
+
const {
|
|
54
|
+
children,
|
|
55
|
+
redirect = 'cart',
|
|
56
|
+
onComplete,
|
|
57
|
+
sx,
|
|
58
|
+
errorSnackbar,
|
|
59
|
+
successSnackbar,
|
|
60
|
+
...formProps
|
|
61
|
+
} = useThemeProps({ name, props })
|
|
38
62
|
const router = useRouter()
|
|
39
63
|
|
|
40
|
-
const form = useFormGqlMutationCart<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
64
|
+
const form = useFormGqlMutationCart<
|
|
65
|
+
AddProductsToCartMutation,
|
|
66
|
+
AddProductsToCartMutationVariables
|
|
67
|
+
>(AddProductsToCartDocument, {
|
|
68
|
+
...formProps,
|
|
69
|
+
// We're stripping out incomplete entered options.
|
|
70
|
+
onBeforeSubmit: async (variables) => {
|
|
71
|
+
const variables2 = (await formProps.onBeforeSubmit?.(variables)) ?? variables
|
|
72
|
+
if (variables2 === false) return false
|
|
47
73
|
|
|
48
|
-
|
|
49
|
-
|
|
74
|
+
const { cartId, cartItems } = variables2
|
|
75
|
+
return {
|
|
50
76
|
cartId,
|
|
51
77
|
cartItems: cartItems
|
|
52
78
|
.filter((cartItem) => cartItem.sku)
|
|
53
79
|
.map((cartItem) => ({
|
|
54
80
|
...cartItem,
|
|
81
|
+
quantity: cartItem.quantity || 1,
|
|
55
82
|
selected_options: cartItem.selected_options?.filter(Boolean),
|
|
56
83
|
entered_options: cartItem.entered_options?.filter((option) => option?.value),
|
|
57
84
|
})),
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
onComplete: async (result, variables) => {
|
|
88
|
+
await onComplete?.(result, variables)
|
|
61
89
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
result.errors?.length ||
|
|
65
|
-
!redirect
|
|
66
|
-
)
|
|
67
|
-
return
|
|
90
|
+
if (result.data?.addProductsToCart?.user_errors?.length || result.errors?.length || !redirect)
|
|
91
|
+
return
|
|
68
92
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
},
|
|
73
|
-
...formProps,
|
|
93
|
+
if (redirect === 'checkout') await router.push('/checkout')
|
|
94
|
+
if (redirect === 'added') await router.push({ pathname: '/checkout/added' })
|
|
95
|
+
if (redirect === 'cart') await router.push({ pathname: '/cart' })
|
|
74
96
|
},
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
// When the value changes, update the value in the form
|
|
78
|
-
useEffect(() => {
|
|
79
|
-
if (form.getValues('redirect') !== redirect) form.setValue('redirect', redirect)
|
|
80
|
-
}, [form, redirect])
|
|
97
|
+
})
|
|
81
98
|
|
|
82
99
|
const submit = form.handleSubmit(() => {})
|
|
83
100
|
|
|
84
101
|
return (
|
|
85
|
-
<
|
|
86
|
-
|
|
102
|
+
<AddProductsToCartContext.Provider
|
|
103
|
+
value={useMemo(() => ({ ...form, redirect }), [form, redirect])}
|
|
104
|
+
>
|
|
105
|
+
<Box component='form' onSubmit={submit} noValidate sx={sx} className={name}>
|
|
87
106
|
{children}
|
|
88
107
|
</Box>
|
|
89
|
-
|
|
108
|
+
<AddProductsToCartSnackbar errorSnackbar={errorSnackbar} successSnackbar={successSnackbar} />
|
|
109
|
+
</AddProductsToCartContext.Provider>
|
|
90
110
|
)
|
|
91
111
|
}
|
|
92
|
-
|
|
93
|
-
export function useFormAddProductsToCart(optional: true): AddProductsToCartContextType | undefined
|
|
94
|
-
export function useFormAddProductsToCart(optional?: false): AddProductsToCartContextType
|
|
95
|
-
export function useFormAddProductsToCart(optional = false) {
|
|
96
|
-
const context = useContext(addProductsToCartContext)
|
|
97
|
-
|
|
98
|
-
if (!optional && typeof context === 'undefined') {
|
|
99
|
-
throw Error(
|
|
100
|
-
'useFormAddProductsToCart must be used within a AddProductsToCartForm or provide the optional=true argument',
|
|
101
|
-
)
|
|
102
|
-
}
|
|
103
|
-
return context
|
|
104
|
-
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NumberFieldElement, NumberFieldElementProps } from '@graphcommerce/ecommerce-ui'
|
|
2
|
-
import { useFormAddProductsToCart } from './
|
|
2
|
+
import { useFormAddProductsToCart } from './useFormAddProductsToCart'
|
|
3
3
|
|
|
4
4
|
type AddToCartQuantityProps = Omit<
|
|
5
5
|
NumberFieldElementProps,
|
|
@@ -1,69 +1,84 @@
|
|
|
1
1
|
import { ApolloCartErrorSnackbar } from '@graphcommerce/magento-cart'
|
|
2
2
|
import {
|
|
3
3
|
Button,
|
|
4
|
+
ErrorSnackbar,
|
|
5
|
+
ErrorSnackbarProps,
|
|
6
|
+
filterNonNullableKeys,
|
|
4
7
|
iconChevronRight,
|
|
5
8
|
IconSvg,
|
|
6
9
|
MessageSnackbar,
|
|
7
|
-
|
|
10
|
+
MessageSnackbarProps,
|
|
8
11
|
} from '@graphcommerce/next-ui'
|
|
9
12
|
import { Trans } from '@lingui/react'
|
|
10
13
|
import PageLink from 'next/link'
|
|
11
|
-
import { useFormAddProductsToCart } from './
|
|
14
|
+
import { useFormAddProductsToCart } from './useFormAddProductsToCart'
|
|
12
15
|
|
|
13
|
-
type
|
|
16
|
+
export type AddProductsToCartSnackbarProps = {
|
|
17
|
+
errorSnackbar?: Omit<ErrorSnackbarProps, 'open'>
|
|
18
|
+
successSnackbar?: Omit<MessageSnackbarProps, 'open' | 'action'>
|
|
19
|
+
}
|
|
14
20
|
|
|
15
|
-
export function AddProductsToCartSnackbar(props:
|
|
16
|
-
const {
|
|
17
|
-
const { formState, error, data,
|
|
21
|
+
export function AddProductsToCartSnackbar(props: AddProductsToCartSnackbarProps) {
|
|
22
|
+
const { errorSnackbar, successSnackbar } = props
|
|
23
|
+
const { formState, error, data, redirect } = useFormAddProductsToCart()
|
|
18
24
|
|
|
19
25
|
const showSuccess =
|
|
20
26
|
!formState.isSubmitting &&
|
|
21
27
|
formState.isSubmitSuccessful &&
|
|
22
28
|
!error?.message &&
|
|
23
29
|
!data?.addProductsToCart?.user_errors?.length &&
|
|
24
|
-
!
|
|
30
|
+
!redirect
|
|
25
31
|
|
|
32
|
+
const items = filterNonNullableKeys(data?.addProductsToCart?.cart.items)
|
|
33
|
+
|
|
34
|
+
const showErrorSnackbar = (data?.addProductsToCart?.user_errors?.length ?? 0) > 0
|
|
26
35
|
return (
|
|
27
36
|
<>
|
|
28
|
-
<ApolloCartErrorSnackbar error={error} />
|
|
29
|
-
|
|
30
|
-
<ErrorSnackbar
|
|
31
|
-
variant='pill'
|
|
32
|
-
severity='error'
|
|
33
|
-
open={(data?.addProductsToCart?.user_errors?.length ?? 0) > 0}
|
|
34
|
-
action={
|
|
35
|
-
<Button size='medium' variant='pill' color='secondary'>
|
|
36
|
-
<Trans id='Ok' />
|
|
37
|
-
</Button>
|
|
38
|
-
}
|
|
39
|
-
>
|
|
40
|
-
<>{data?.addProductsToCart?.user_errors?.map((e) => e?.message).join(', ')}</>
|
|
41
|
-
</ErrorSnackbar>
|
|
37
|
+
{error && <ApolloCartErrorSnackbar error={error} />}
|
|
42
38
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
<Button
|
|
49
|
-
id='
|
|
50
|
-
size='medium'
|
|
51
|
-
variant='pill'
|
|
52
|
-
color='secondary'
|
|
53
|
-
endIcon={<IconSvg src={iconChevronRight} />}
|
|
54
|
-
sx={{ display: 'flex' }}
|
|
55
|
-
>
|
|
56
|
-
<Trans id='View shopping cart' />
|
|
39
|
+
{showErrorSnackbar && (
|
|
40
|
+
<ErrorSnackbar
|
|
41
|
+
variant='pill'
|
|
42
|
+
severity='error'
|
|
43
|
+
action={
|
|
44
|
+
<Button size='medium' variant='pill' color='secondary'>
|
|
45
|
+
<Trans id='Ok' />
|
|
57
46
|
</Button>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
47
|
+
}
|
|
48
|
+
{...errorSnackbar}
|
|
49
|
+
open={showErrorSnackbar}
|
|
50
|
+
>
|
|
51
|
+
<>{data?.addProductsToCart?.user_errors?.map((e) => e?.message).join(', ')}</>
|
|
52
|
+
</ErrorSnackbar>
|
|
53
|
+
)}
|
|
54
|
+
|
|
55
|
+
{showSuccess && (
|
|
56
|
+
<MessageSnackbar
|
|
57
|
+
variant='pill'
|
|
58
|
+
{...successSnackbar}
|
|
59
|
+
open={showSuccess}
|
|
60
|
+
action={
|
|
61
|
+
<PageLink href='/cart' passHref>
|
|
62
|
+
<Button
|
|
63
|
+
id='view-shopping-cart-button'
|
|
64
|
+
size='medium'
|
|
65
|
+
variant='pill'
|
|
66
|
+
color='secondary'
|
|
67
|
+
endIcon={<IconSvg src={iconChevronRight} />}
|
|
68
|
+
sx={{ display: 'flex' }}
|
|
69
|
+
>
|
|
70
|
+
<Trans id='View shopping cart' />
|
|
71
|
+
</Button>
|
|
72
|
+
</PageLink>
|
|
73
|
+
}
|
|
74
|
+
>
|
|
75
|
+
<Trans
|
|
76
|
+
id='<0>{name}</0> has been added to your shopping cart!'
|
|
77
|
+
components={{ 0: <strong /> }}
|
|
78
|
+
values={{ name: items[items.length - 1]?.product.name }}
|
|
79
|
+
/>
|
|
80
|
+
</MessageSnackbar>
|
|
81
|
+
)}
|
|
67
82
|
</>
|
|
68
83
|
)
|
|
69
84
|
}
|
|
@@ -3,4 +3,5 @@ export * from './AddProductsToCartButton'
|
|
|
3
3
|
export * from './AddProductsToCartError'
|
|
4
4
|
export * from './AddProductsToCartForm'
|
|
5
5
|
export * from './AddProductsToCartQuantity'
|
|
6
|
-
export * from './
|
|
6
|
+
export * from './useFormAddProductsToCart'
|
|
7
|
+
export * from './AddProductsToCartFab'
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useEventCallback } from '@mui/material'
|
|
2
|
+
import { useMemo } from 'react'
|
|
3
|
+
import { useFormAddProductsToCart } from './useFormAddProductsToCart'
|
|
4
|
+
|
|
5
|
+
export type UseAddProductsToCartActionProps = {
|
|
6
|
+
sku: string | null | undefined
|
|
7
|
+
index?: number
|
|
8
|
+
disabled?: boolean
|
|
9
|
+
loading?: boolean
|
|
10
|
+
onClick?: React.MouseEventHandler<HTMLButtonElement>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type UseAddProductsToCartActionReturn = {
|
|
14
|
+
disabled: boolean
|
|
15
|
+
loading: boolean
|
|
16
|
+
onClick: React.MouseEventHandler<HTMLButtonElement>
|
|
17
|
+
onMouseDown: React.MouseEventHandler<HTMLButtonElement>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function useAddProductsToCartAction(
|
|
21
|
+
props: UseAddProductsToCartActionProps,
|
|
22
|
+
): UseAddProductsToCartActionReturn {
|
|
23
|
+
const { formState, setValue, getValues } = useFormAddProductsToCart()
|
|
24
|
+
const { sku, index = 0, onClick: onClickIncomming, disabled, loading } = props
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
disabled: Boolean(formState.errors.cartItems?.[index].sku?.message || disabled),
|
|
28
|
+
loading: loading || (formState.isSubmitting && getValues(`cartItems.${index}.sku`) === sku),
|
|
29
|
+
onClick: useEventCallback((e) => {
|
|
30
|
+
e.stopPropagation()
|
|
31
|
+
if (formState.isSubmitting) return
|
|
32
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
33
|
+
if (!sku) console.warn(`You must provide a 'sku' to useAddProductsToCartAction`)
|
|
34
|
+
}
|
|
35
|
+
setValue(`cartItems.${index}.sku`, sku ?? '')
|
|
36
|
+
onClickIncomming?.(e)
|
|
37
|
+
}),
|
|
38
|
+
onMouseDown: useEventCallback((e) => e.stopPropagation()),
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { UseFormGqlMutationReturn } from '@graphcommerce/ecommerce-ui'
|
|
2
|
+
import { createContext, useContext } from 'react'
|
|
3
|
+
import {
|
|
4
|
+
AddProductsToCartMutation,
|
|
5
|
+
AddProductsToCartMutationVariables,
|
|
6
|
+
} from './AddProductsToCart.gql'
|
|
7
|
+
|
|
8
|
+
export type RedirectType = 'added' | 'cart' | 'checkout' | undefined
|
|
9
|
+
|
|
10
|
+
export type AddProductsToCartContextType = { redirect: RedirectType } & UseFormGqlMutationReturn<
|
|
11
|
+
AddProductsToCartMutation,
|
|
12
|
+
AddProductsToCartMutationVariables
|
|
13
|
+
>
|
|
14
|
+
|
|
15
|
+
export const AddProductsToCartContext = createContext(
|
|
16
|
+
undefined as AddProductsToCartContextType | undefined,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
export function useFormAddProductsToCart(optional: true): AddProductsToCartContextType | undefined
|
|
20
|
+
export function useFormAddProductsToCart(optional?: false): AddProductsToCartContextType
|
|
21
|
+
export function useFormAddProductsToCart(optional = false) {
|
|
22
|
+
const context = useContext(AddProductsToCartContext)
|
|
23
|
+
|
|
24
|
+
if (!optional && typeof context === 'undefined') {
|
|
25
|
+
throw Error(
|
|
26
|
+
'useFormAddProductsToCart must be used within a AddProductsToCartForm or provide the optional=true argument',
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
return context
|
|
30
|
+
}
|
|
@@ -5,38 +5,36 @@ import PageLink from 'next/link'
|
|
|
5
5
|
import { productPageCategory } from '../ProductPageCategory/productPageCategory'
|
|
6
6
|
import { ProductPageBreadcrumbFragment } from './ProductPageBreadcrumb.gql'
|
|
7
7
|
|
|
8
|
-
type ProductPageBreadcrumbsProps = ProductPageBreadcrumbFragment & BreadcrumbsProps
|
|
8
|
+
type ProductPageBreadcrumbsProps = ProductPageBreadcrumbFragment & Omit<BreadcrumbsProps, 'children'>
|
|
9
9
|
|
|
10
10
|
export function ProductPageBreadcrumb(props: ProductPageBreadcrumbsProps) {
|
|
11
|
-
const { categories, name } = props
|
|
11
|
+
const { categories, name, ...breadcrumbProps } = props
|
|
12
12
|
const prev = usePrevPageRouter()
|
|
13
13
|
|
|
14
14
|
const category =
|
|
15
15
|
categories?.find((c) => `/${c?.url_path}` === prev?.asPath) ?? productPageCategory(props)
|
|
16
16
|
|
|
17
17
|
return (
|
|
18
|
-
<
|
|
19
|
-
<
|
|
20
|
-
<
|
|
21
|
-
<
|
|
22
|
-
<Trans id='Home' />
|
|
23
|
-
</Link>
|
|
24
|
-
</PageLink>
|
|
25
|
-
{category?.breadcrumbs?.map((mapped_category, i) => (
|
|
26
|
-
<Link
|
|
27
|
-
underline='hover'
|
|
28
|
-
key={mapped_category?.category_uid}
|
|
29
|
-
color='inherit'
|
|
30
|
-
href={`/${mapped_category?.category_url_path}`}
|
|
31
|
-
>
|
|
32
|
-
{mapped_category?.category_name}
|
|
33
|
-
</Link>
|
|
34
|
-
))}
|
|
35
|
-
<Link underline='hover' color='inherit' href={`/${category?.url_path}`}>
|
|
36
|
-
{category?.name}
|
|
18
|
+
<Breadcrumbs {...breadcrumbProps}>
|
|
19
|
+
<PageLink href='/' passHref>
|
|
20
|
+
<Link underline='hover' color='inherit'>
|
|
21
|
+
<Trans id='Home' />
|
|
37
22
|
</Link>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
23
|
+
</PageLink>
|
|
24
|
+
{category?.breadcrumbs?.map((mapped_category, i) => (
|
|
25
|
+
<Link
|
|
26
|
+
underline='hover'
|
|
27
|
+
key={mapped_category?.category_uid}
|
|
28
|
+
color='inherit'
|
|
29
|
+
href={`/${mapped_category?.category_url_path}`}
|
|
30
|
+
>
|
|
31
|
+
{mapped_category?.category_name}
|
|
32
|
+
</Link>
|
|
33
|
+
))}
|
|
34
|
+
<Link underline='hover' color='inherit' href={`/${category?.url_path}`}>
|
|
35
|
+
{category?.name}
|
|
36
|
+
</Link>
|
|
37
|
+
<Typography color='text.primary'>{name}</Typography>
|
|
38
|
+
</Breadcrumbs>
|
|
41
39
|
)
|
|
42
40
|
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
nonNullable,
|
|
3
|
+
SidebarGallery,
|
|
4
|
+
SidebarGalleryProps,
|
|
5
|
+
TypeRenderer,
|
|
6
|
+
} from '@graphcommerce/next-ui'
|
|
2
7
|
import { PropsWithChildren } from 'react'
|
|
3
8
|
import { ProductPageGalleryFragment } from './ProductPageGallery.gql'
|
|
4
9
|
|
|
@@ -23,14 +28,17 @@ export function ProductPageGallery(props: ProductPageGalleryProps) {
|
|
|
23
28
|
sidebar={children}
|
|
24
29
|
aspectRatio={[width, height]}
|
|
25
30
|
images={
|
|
26
|
-
media_gallery
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
media_gallery
|
|
32
|
+
?.filter(nonNullable)
|
|
33
|
+
.sort((a, b) => (a.position ?? 0) - (b.position ?? 0))
|
|
34
|
+
.map((item) => {
|
|
35
|
+
if (item.__typename === 'ProductImage')
|
|
36
|
+
return { src: item.url ?? '', alt: item.label || undefined, width, height }
|
|
37
|
+
return {
|
|
38
|
+
src: '',
|
|
39
|
+
alt: `{${item.__typename} not yet supported}`,
|
|
40
|
+
}
|
|
41
|
+
}) ?? []
|
|
34
42
|
}
|
|
35
43
|
/>
|
|
36
44
|
)
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@graphcommerce/magento-product",
|
|
3
3
|
"homepage": "https://www.graphcommerce.org/",
|
|
4
4
|
"repository": "github:graphcommerce-org/graphcommerce",
|
|
5
|
-
"version": "4.
|
|
5
|
+
"version": "4.8.1",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"prettier": "@graphcommerce/prettier-config-pwa",
|
|
8
8
|
"eslintConfig": {
|
|
@@ -19,15 +19,15 @@
|
|
|
19
19
|
"type-fest": "^2.12.2"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@graphcommerce/ecommerce-ui": "1.5.
|
|
23
|
-
"@graphcommerce/framer-scroller": "2.1.
|
|
22
|
+
"@graphcommerce/ecommerce-ui": "1.5.6",
|
|
23
|
+
"@graphcommerce/framer-scroller": "2.1.43",
|
|
24
24
|
"@graphcommerce/framer-next-pages": "3.3.2",
|
|
25
25
|
"@graphcommerce/graphql": "3.5.0",
|
|
26
26
|
"@graphcommerce/graphql-mesh": "4.2.0",
|
|
27
27
|
"@graphcommerce/image": "3.1.10",
|
|
28
|
-
"@graphcommerce/magento-cart": "4.9.
|
|
29
|
-
"@graphcommerce/magento-store": "4.3.
|
|
30
|
-
"@graphcommerce/next-ui": "4.
|
|
28
|
+
"@graphcommerce/magento-cart": "4.9.2",
|
|
29
|
+
"@graphcommerce/magento-store": "4.3.4",
|
|
30
|
+
"@graphcommerce/next-ui": "4.29.1",
|
|
31
31
|
"schema-dts": "^1.1.0"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|