@graphcommerce/magento-wishlist 1.0.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.
Files changed (36) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +62 -0
  3. package/components/ProductWishlistChip/ProductWishlistChip.graphql +4 -0
  4. package/components/ProductWishlistChip/ProductWishlistChip.tsx +5 -0
  5. package/components/ProductWishlistChip/ProductWishlistChipBase.tsx +180 -0
  6. package/components/ProductWishlistChip/ProductWishlistChipDetail.tsx +12 -0
  7. package/components/ProductWishlistChip/ProductWishlistChipDetailConfigurable.tsx +23 -0
  8. package/components/WishlistFab/WishlistFab.tsx +91 -0
  9. package/components/WishlistItem/ProductAddToCart.tsx +135 -0
  10. package/components/WishlistItem/WishlistItem.graphql +3 -0
  11. package/components/WishlistItem/WishlistItem.tsx +30 -0
  12. package/components/WishlistItem/WishlistItemBase.tsx +303 -0
  13. package/components/WishlistItem/WishlistItemConfigurable.graphql +8 -0
  14. package/components/WishlistItem/WishlistItemConfigurable.tsx +11 -0
  15. package/components/WishlistItem/WishlistItemProduct.graphql +11 -0
  16. package/components/WishlistItems/WishlistItems.graphql +12 -0
  17. package/components/WishlistItems/WishlistItems.tsx +33 -0
  18. package/components/WishlistMenuFabItem/WishlistMenuFabItem.tsx +81 -0
  19. package/components/index.ts +10 -0
  20. package/hooks/index.ts +3 -0
  21. package/hooks/useMergeGuestWishlistWithCustomer.tsx +64 -0
  22. package/hooks/useWishlistEnabled.tsx +9 -0
  23. package/hooks/useWishlistItems.tsx +53 -0
  24. package/index.ts +12 -0
  25. package/package.json +39 -0
  26. package/queries/AddProductToWishlist.graphql +13 -0
  27. package/queries/GetGuestWishlistProducts.graphql +14 -0
  28. package/queries/GetIsInWishlists.graphql +7 -0
  29. package/queries/GetWishlistProducts.graphql +19 -0
  30. package/queries/GuestWishlist.graphql +11 -0
  31. package/queries/GuestWishlist.graphqls +13 -0
  32. package/queries/RemoveProductFromWishlist.graphql +7 -0
  33. package/queries/WishlistStoreConfigFragment.graphql +3 -0
  34. package/queries/WishlistSummaryFragment.graphql +13 -0
  35. package/tsconfig.json +5 -0
  36. package/typePolicies.ts +21 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # @graphcommerce/magento-wishlist
2
+
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#1256](https://github.com/graphcommerce-org/graphcommerce/pull/1256) [`669a17a97`](https://github.com/graphcommerce-org/graphcommerce/commit/669a17a973c47c00fed4a649a9da0bfc5670c5da) Thanks [@timhofman](https://github.com/timhofman)! - Wishlist
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [[`669a17a97`](https://github.com/graphcommerce-org/graphcommerce/commit/669a17a973c47c00fed4a649a9da0bfc5670c5da)]:
12
+ - @graphcommerce/magento-product@4.3.0
13
+ - @graphcommerce/magento-product-configurable@4.1.0
package/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # Wishlist
2
+
3
+ This package enables visitors to manage their wishlist with or without an
4
+ account.
5
+
6
+ ## Features
7
+
8
+ - Manage wishlist without an account
9
+ - Automatically migrate wishlist from guest session to account upon login
10
+ - Add products to wishlist based on SKU (all producttypes)
11
+ - Add products with selected variants, like size and color, to wishlist
12
+ (configurable product)
13
+ - Add products from wishlist to cart (simple, downloadable, virtual)
14
+ - Enable/disable through Magento configuration item
15
+ 'magento_wishlist_general_is_enabled'
16
+
17
+ ## Additional settings
18
+
19
+ - Enable wishlist for logged in users only (for instance for B2B)
20
+
21
+ Add the following configuration to your env file
22
+
23
+ ```
24
+ NEXT_PUBLIC_WISHLIST_HIDE_FOR_GUEST="1"
25
+ ```
26
+
27
+ - Force add products to wishlist (only availble for logged in users)
28
+
29
+ In B2B cases you might want to add multiple configured variants of the same
30
+ product to the wishlist, like a quick order list.
31
+
32
+ Use this setting to force adding products to the wishlist, even when there
33
+ already is a product with the same SKU in the wishlist.
34
+
35
+ ```
36
+ NEXT_PUBLIC_WISHLIST_IGNORE_PRODUCT_WISHLIST_STATUS="1"
37
+ ```
38
+
39
+ ## Customizing wishlist styling
40
+
41
+ - The ProductWishlistChip accepts SX props (see ProductWishlistChip and
42
+ ProductWishlistChipDetail)
43
+ - Fancy a different icon? See:
44
+ https://www.graphcommerce.org/docs/framework/icons
45
+ - Styleable through theme provider overMuiCssBaseline.styleOverrides (although
46
+ we do recommend to use the SX approach)
47
+
48
+ ## Roadmap
49
+
50
+ - Wishlist overview: add to cart with configurable options (see 'Remarks')
51
+
52
+ ## Remarks
53
+
54
+ Magento (<= 2.4.4) throws errors when retrieving product configuration for
55
+ wishlist items. Currently selected products variants are stored, but not yet
56
+ displayed in the wishlist overview
57
+
58
+ Fetching configurable_options throws internal servers due to a mismatch in
59
+ schema and implemented resolvers on Magento's side.
60
+
61
+ Related issue:
62
+ https://github.com/magento/magento2/commit/5c884244cd7f1bd55bb0b908943caaed3e3e762b
@@ -0,0 +1,4 @@
1
+ fragment ProductWishlistChip on ProductInterface {
2
+ __typename
3
+ sku
4
+ }
@@ -0,0 +1,5 @@
1
+ import { ProductWishlistChipBase, ProductWishlistChipProps } from './ProductWishlistChipBase'
2
+
3
+ export function ProductWishlistChip(props: ProductWishlistChipProps) {
4
+ return <ProductWishlistChipBase {...props} />
5
+ }
@@ -0,0 +1,180 @@
1
+ /* eslint-disable @typescript-eslint/no-floating-promises */
2
+ /* eslint-disable react-hooks/rules-of-hooks */
3
+ import { useQuery, useMutation, useApolloClient } from '@graphcommerce/graphql'
4
+ import { CustomerTokenDocument } from '@graphcommerce/magento-customer'
5
+ import { IconSvg, iconHeart, extendableComponent } from '@graphcommerce/next-ui'
6
+ import { t } from '@lingui/macro'
7
+ import { SxProps, Theme, IconButton } from '@mui/material'
8
+ import { useState, useEffect } from 'react'
9
+ import { useWishlistEnabled } from '../../hooks'
10
+ import { AddProductToWishlistDocument } from '../../queries/AddProductToWishlist.gql'
11
+ import { GetIsInWishlistsDocument } from '../../queries/GetIsInWishlists.gql'
12
+ import { GuestWishlistDocument } from '../../queries/GuestWishlist.gql'
13
+ import { RemoveProductFromWishlistDocument } from '../../queries/RemoveProductFromWishlist.gql'
14
+ import { ProductWishlistChipFragment } from './ProductWishlistChip.gql'
15
+
16
+ const hideForGuest = process.env.NEXT_PUBLIC_WISHLIST_HIDE_FOR_GUEST === '1'
17
+ const ignoreProductWishlistStatus =
18
+ process.env.NEXT_PUBLIC_WISHLIST_IGNORE_PRODUCT_WISHLIST_STATUS === '1'
19
+
20
+ export type ProductWishlistChipProps = ProductWishlistChipFragment & { sx?: SxProps<Theme> } & {
21
+ selectedOptions?: string[]
22
+ }
23
+
24
+ const name = 'ProductWishlistChipBase' as const
25
+ const parts = ['root', 'wishlistIcon', 'wishlistIconActive', 'wishlistButton'] as const
26
+ const { classes } = extendableComponent(name, parts)
27
+
28
+ export function ProductWishlistChipBase(props: ProductWishlistChipProps) {
29
+ const { sku, selectedOptions = [], sx = [] } = props
30
+
31
+ const [inWishlist, setInWishlist] = useState(false)
32
+
33
+ const { data: token } = useQuery(CustomerTokenDocument)
34
+ const isLoggedIn = token?.customerToken && token?.customerToken.valid
35
+
36
+ const { cache } = useApolloClient()
37
+
38
+ const isWishlistEnabled = useWishlistEnabled()
39
+
40
+ if (!isWishlistEnabled) {
41
+ return null
42
+ }
43
+
44
+ const heart = (
45
+ <IconSvg
46
+ src={iconHeart}
47
+ size='medium'
48
+ className={classes.wishlistIcon}
49
+ sx={(theme) => ({ color: theme.palette.primary.main })}
50
+ />
51
+ )
52
+
53
+ const activeHeart = (
54
+ <IconSvg
55
+ src={iconHeart}
56
+ size='medium'
57
+ className={classes.wishlistIconActive}
58
+ sx={(theme) => ({ color: theme.palette.primary.main, fill: 'currentcolor' })}
59
+ />
60
+ )
61
+
62
+ const { data: GetCustomerWishlistData, loading } = useQuery(GetIsInWishlistsDocument, {
63
+ skip: !isLoggedIn,
64
+ })
65
+
66
+ const { data: guestWishlistData, loading: loadingGuestWishlistData } = useQuery(
67
+ GuestWishlistDocument,
68
+ {
69
+ ssr: false,
70
+ skip: isLoggedIn === true,
71
+ },
72
+ )
73
+
74
+ useEffect(() => {
75
+ // Do not display wishlist UI to guests when configured as customer only
76
+ if (hideForGuest && !isLoggedIn) {
77
+ return
78
+ }
79
+
80
+ if (!sku) {
81
+ return
82
+ }
83
+
84
+ // Mark as active when product is available in either customer or guest wishlist
85
+ if (isLoggedIn && !loading) {
86
+ const inWishlistTest =
87
+ GetCustomerWishlistData?.customer?.wishlists[0]?.items_v2?.items.map(
88
+ (item) => item?.product?.sku,
89
+ ) || []
90
+ setInWishlist(inWishlistTest.includes(sku))
91
+ } else if (!isLoggedIn) {
92
+ const inWishlistTest = guestWishlistData?.guestWishlist?.items.map((item) => item?.sku) || []
93
+ setInWishlist(inWishlistTest.includes(sku))
94
+ }
95
+ }, [isLoggedIn, sku, loading, GetCustomerWishlistData, guestWishlistData])
96
+
97
+ const [addWishlistItem] = useMutation(AddProductToWishlistDocument)
98
+ const [removeWishlistItem] = useMutation(RemoveProductFromWishlistDocument)
99
+
100
+ const preventAnimationBubble: React.MouseEventHandler<HTMLButtonElement> = (e) => {
101
+ e.preventDefault()
102
+ e.stopPropagation()
103
+ }
104
+
105
+ const handleClick: React.MouseEventHandler<HTMLButtonElement> = (e) => {
106
+ e.preventDefault()
107
+
108
+ if (!sku) {
109
+ return
110
+ }
111
+
112
+ if (isLoggedIn) {
113
+ if (inWishlist && !ignoreProductWishlistStatus) {
114
+ const wishlistItemsInSession =
115
+ GetCustomerWishlistData?.customer?.wishlists[0]?.items_v2?.items || []
116
+
117
+ const item = wishlistItemsInSession.find((element) => element?.product?.sku == sku)
118
+
119
+ if (item?.id) {
120
+ removeWishlistItem({ variables: { wishlistItemId: item.id } })
121
+ }
122
+ } else {
123
+ addWishlistItem({
124
+ variables: { input: { sku, quantity: 1, selected_options: selectedOptions } },
125
+ })
126
+ }
127
+ } else if (inWishlist) {
128
+ cache.modify({
129
+ id: cache.identify({ __typename: 'GuestWishlist' }),
130
+ fields: {
131
+ items(existingItems = []) {
132
+ const items = existingItems.filter((item) => item.sku !== sku)
133
+ return items
134
+ },
135
+ },
136
+ })
137
+ } else {
138
+ /** Merging of wishlist items is done by policy, see typePolicies.ts */
139
+ cache.writeQuery({
140
+ query: GuestWishlistDocument,
141
+ data: {
142
+ guestWishlist: {
143
+ __typename: 'GuestWishlist',
144
+ items: [
145
+ {
146
+ __typename: 'GuestWishlistItem',
147
+ sku,
148
+ quantity: 1,
149
+ selected_options: selectedOptions,
150
+ },
151
+ ],
152
+ },
153
+ },
154
+ broadcast: true,
155
+ })
156
+ }
157
+ }
158
+
159
+ const button = (
160
+ <IconButton
161
+ key={sku}
162
+ onClick={handleClick}
163
+ onMouseDown={preventAnimationBubble}
164
+ size='small'
165
+ className={classes.wishlistButton}
166
+ sx={[
167
+ (theme) => ({
168
+ padding: theme.spacings.xxs,
169
+ }),
170
+ ...(Array.isArray(sx) ? sx : [sx]),
171
+ ]}
172
+ title={inWishlist ? t`Remove from wishlist` : t`Add to wishlist`}
173
+ aria-label={inWishlist ? t`Remove from wishlist` : t`Add to wishlist`}
174
+ >
175
+ {inWishlist ? activeHeart : heart}
176
+ </IconButton>
177
+ )
178
+
179
+ return !hideForGuest || isLoggedIn ? button : null
180
+ }
@@ -0,0 +1,12 @@
1
+ import { ProductWishlistChipBase, ProductWishlistChipProps } from './ProductWishlistChipBase'
2
+
3
+ export function ProductWishlistChipDetail(props: ProductWishlistChipProps) {
4
+ return (
5
+ <ProductWishlistChipBase
6
+ sx={(theme) => ({
7
+ boxShadow: theme.shadows[6],
8
+ })}
9
+ {...props}
10
+ />
11
+ )
12
+ }
@@ -0,0 +1,23 @@
1
+ import { useConfigurableContext } from '@graphcommerce/magento-product-configurable'
2
+ import { ProductWishlistChipBase, ProductWishlistChipProps } from './ProductWishlistChipBase'
3
+
4
+ export function ProductWishlistChipDetailConfigurable(props: ProductWishlistChipProps) {
5
+ const { sku } = props
6
+
7
+ let selectedOptions: string[] = []
8
+ if (sku) {
9
+ // eslint-disable-next-line react-hooks/rules-of-hooks
10
+ const context = useConfigurableContext(sku)
11
+ selectedOptions = (Object as any).values(context.selection)
12
+ }
13
+
14
+ return (
15
+ <ProductWishlistChipBase
16
+ sx={(theme) => ({
17
+ boxShadow: theme.shadows[6],
18
+ })}
19
+ selectedOptions={selectedOptions}
20
+ {...props}
21
+ />
22
+ )
23
+ }
@@ -0,0 +1,91 @@
1
+ import { useQuery } from '@graphcommerce/graphql'
2
+ import { CustomerTokenDocument } from '@graphcommerce/magento-customer'
3
+ import { iconHeart, DesktopHeaderBadge, IconSvg, extendableComponent } from '@graphcommerce/next-ui'
4
+ import { t } from '@lingui/macro'
5
+ import { Fab, FabProps as FabPropsType, NoSsr, SxProps, Theme } from '@mui/material'
6
+ import PageLink from 'next/link'
7
+ import React, { useEffect } from 'react'
8
+ import { useWishlistEnabled } from '../../hooks'
9
+ import { GetIsInWishlistsDocument } from '../../queries/GetIsInWishlists.gql'
10
+ import { GuestWishlistDocument } from '../../queries/GuestWishlist.gql'
11
+
12
+ type WishlistFabContentProps = {
13
+ icon?: React.ReactNode
14
+ FabProps?: Omit<FabPropsType, 'children'>
15
+ sx?: SxProps<Theme>
16
+ }
17
+
18
+ const name = 'WishlistFab'
19
+ const parts = ['root'] as const
20
+ const { classes } = extendableComponent(name, parts)
21
+
22
+ const hideForGuest = process.env.NEXT_PUBLIC_WISHLIST_HIDE_FOR_GUEST === '1'
23
+
24
+ function WishlistFabContent(props: WishlistFabContentProps) {
25
+ const { icon, FabProps, sx } = props
26
+
27
+ const { data: token } = useQuery(CustomerTokenDocument)
28
+ const isLoggedIn = token?.customerToken && token?.customerToken.valid
29
+
30
+ const { data: GetCustomerWishlistData, loading } = useQuery(GetIsInWishlistsDocument, {
31
+ skip: !isLoggedIn,
32
+ })
33
+
34
+ const { data: guestWishlistData, loading: loadingGuestWishlistData } = useQuery(
35
+ GuestWishlistDocument,
36
+ {
37
+ ssr: false,
38
+ skip: isLoggedIn === true,
39
+ },
40
+ )
41
+
42
+ let activeWishlist = false
43
+ if (isLoggedIn) {
44
+ const wishlistItemCount = GetCustomerWishlistData?.customer?.wishlists[0]?.items_count || 0
45
+ activeWishlist = wishlistItemCount > 0
46
+ } else {
47
+ const wishlist = guestWishlistData?.guestWishlist?.items || []
48
+ activeWishlist = wishlist.length > 0
49
+ }
50
+
51
+ const wishlistIcon = icon ?? <IconSvg src={iconHeart} size='large' />
52
+
53
+ return (
54
+ <PageLink href='/wishlist' passHref>
55
+ <Fab
56
+ color='inherit'
57
+ data-test-id='wishlist-fab'
58
+ aria-label={t`Wishlist`}
59
+ size='large'
60
+ className={classes.root}
61
+ {...FabProps}
62
+ sx={sx}
63
+ >
64
+ {activeWishlist ? (
65
+ <DesktopHeaderBadge color='primary' variant='dot' overlap='circular'>
66
+ {wishlistIcon}
67
+ </DesktopHeaderBadge>
68
+ ) : (
69
+ wishlistIcon
70
+ )}
71
+ </Fab>
72
+ </PageLink>
73
+ )
74
+ }
75
+
76
+ export function WishlistFab(props: WishlistFabContentProps) {
77
+ const isWishlistEnabled = useWishlistEnabled()
78
+
79
+ const { data: token } = useQuery(CustomerTokenDocument)
80
+ const isLoggedIn = token?.customerToken && token?.customerToken.valid
81
+
82
+ return (
83
+ <>
84
+ {isWishlistEnabled && (!hideForGuest || isLoggedIn) && (
85
+ <NoSsr>
86
+ <WishlistFabContent {...props} />
87
+ </NoSsr>
88
+ )}
89
+ </>
90
+ )
91
+ }
@@ -0,0 +1,135 @@
1
+ import type { ProductInterface } from '@graphcommerce/graphql-mesh'
2
+ import { ApolloCartErrorAlert, useFormGqlMutationCart } from '@graphcommerce/magento-cart'
3
+ import {
4
+ ProductAddToCartDocument,
5
+ ProductAddToCartMutationVariables,
6
+ } from '@graphcommerce/magento-product'
7
+ import { MoneyProps } from '@graphcommerce/magento-store'
8
+ import {
9
+ Button,
10
+ MessageSnackbar,
11
+ TextInputNumber,
12
+ iconChevronRight,
13
+ IconSvg,
14
+ extendableComponent,
15
+ AnimatedRow,
16
+ } from '@graphcommerce/next-ui'
17
+ import { Trans } from '@lingui/macro'
18
+ import { ButtonProps, Box, Alert } from '@mui/material'
19
+ import { AnimatePresence } from 'framer-motion'
20
+ import PageLink from 'next/link'
21
+ import React from 'react'
22
+
23
+ const { classes, selectors } = extendableComponent('ProductAddToCart', [
24
+ 'root',
25
+ 'button',
26
+ 'price',
27
+ 'divider',
28
+ 'buttonWrapper',
29
+ ] as const)
30
+
31
+ export type AddToCartProps = React.ComponentProps<typeof ProductAddToCart>
32
+
33
+ export function ProductAddToCart(
34
+ props: Pick<ProductInterface, 'name'> & {
35
+ variables: Omit<ProductAddToCartMutationVariables, 'cartId'>
36
+ name: string
37
+ price: MoneyProps
38
+ additionalButtons?: React.ReactNode
39
+ children?: React.ReactNode
40
+ } & Omit<ButtonProps, 'type' | 'name'>,
41
+ ) {
42
+ const { name, children, variables, price, sx, additionalButtons, ...buttonProps } = props
43
+
44
+ const form = useFormGqlMutationCart(ProductAddToCartDocument, {
45
+ defaultValues: { ...variables },
46
+ })
47
+
48
+ const { handleSubmit, formState, error, muiRegister, required, data } = form
49
+ const submitHandler = handleSubmit(() => {})
50
+
51
+ return (
52
+ <Box component='form' onSubmit={submitHandler} noValidate className={classes.root}>
53
+ <Box
54
+ sx={(theme) => ({
55
+ gridArea: 'itemQuantity',
56
+ })}
57
+ >
58
+ <TextInputNumber
59
+ variant='outlined'
60
+ error={formState.isSubmitted && !!formState.errors.quantity}
61
+ required={required.quantity}
62
+ inputProps={{ min: 1 }}
63
+ {...muiRegister('quantity', { required: required.quantity })}
64
+ helperText={formState.isSubmitted && formState.errors.quantity}
65
+ disabled={formState.isSubmitting}
66
+ size='small'
67
+ sx={(theme) => ({
68
+ alignSelf: 'flex-start',
69
+ })}
70
+ />
71
+ </Box>
72
+ {children}
73
+ <Box
74
+ className={classes.buttonWrapper}
75
+ sx={(theme) => ({
76
+ gridArea: 'itemCartButton',
77
+ alignSelf: 'flex-start',
78
+ position: 'absolute',
79
+ left: '0',
80
+ bottom: '-35px',
81
+ })}
82
+ >
83
+ <Button
84
+ type='submit'
85
+ className={classes.button}
86
+ loading={formState.isSubmitting}
87
+ color='primary'
88
+ variant='text'
89
+ size='medium'
90
+ {...buttonProps}
91
+ >
92
+ <Trans>Add to Cart</Trans>
93
+ </Button>
94
+ </Box>
95
+
96
+ <ApolloCartErrorAlert error={error} />
97
+
98
+ <AnimatePresence initial={false}>
99
+ {data?.addProductsToCart?.user_errors.map((e) => (
100
+ <AnimatedRow key={e?.code}>
101
+ <Alert severity='error'>{e?.message}</Alert>
102
+ </AnimatedRow>
103
+ ))}
104
+ </AnimatePresence>
105
+
106
+ <MessageSnackbar
107
+ open={
108
+ !formState.isSubmitting &&
109
+ formState.isSubmitSuccessful &&
110
+ !error?.message &&
111
+ !data?.addProductsToCart?.user_errors?.length
112
+ }
113
+ variant='pill'
114
+ action={
115
+ <PageLink href='/cart' passHref>
116
+ <Button
117
+ id='view-shopping-cart-button'
118
+ size='medium'
119
+ variant='pill'
120
+ color='secondary'
121
+ endIcon={<IconSvg src={iconChevronRight} />}
122
+ >
123
+ <Trans>View shopping cart</Trans>
124
+ </Button>
125
+ </PageLink>
126
+ }
127
+ >
128
+ <Trans>
129
+ <strong>{name}</strong> has been added to your shopping cart!
130
+ </Trans>
131
+ </MessageSnackbar>
132
+ </Box>
133
+ )
134
+ }
135
+ ProductAddToCart.selectors = selectors
@@ -0,0 +1,3 @@
1
+ fragment WishlistItem on WishlistItemInterface @injectable {
2
+ id
3
+ }
@@ -0,0 +1,30 @@
1
+ /* eslint-disable @typescript-eslint/no-floating-promises */
2
+ /* eslint-disable react-hooks/rules-of-hooks */
3
+ import { SxProps, Theme } from '@mui/material'
4
+
5
+ import { PropsWithChildren } from 'react'
6
+ import { ProductAddToCart } from './ProductAddToCart'
7
+ import { WishlistItemBase } from './WishlistItemBase'
8
+ import { WishlistItemProductFragment } from './WishlistItemProduct.gql'
9
+
10
+ type OptionalProductWishlistParent = {
11
+ wishlistItemId?: string
12
+ }
13
+
14
+ export type WishlistItemProps = PropsWithChildren<WishlistItemProductFragment> & {
15
+ sx?: SxProps<Theme>
16
+ } & OptionalProductWishlistParent
17
+
18
+ export function WishlistItem(props: WishlistItemProps) {
19
+ const { sku, name, price_range } = props
20
+
21
+ return (
22
+ <WishlistItemBase {...props}>
23
+ <ProductAddToCart
24
+ variables={{ sku: sku ?? '', quantity: 1 }}
25
+ name={name ?? ''}
26
+ price={price_range.minimum_price.regular_price}
27
+ />
28
+ </WishlistItemBase>
29
+ )
30
+ }