@graphcommerce/magento-product 8.1.0-canary.9 → 9.0.0-canary.100

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 (93) hide show
  1. package/Api/ProductListItem.graphql +1 -2
  2. package/Api/ProductPageItem.graphql +1 -1
  3. package/CHANGELOG.md +276 -84
  4. package/Config.graphqls +13 -0
  5. package/components/AddProductsToCart/AddProductsToCartButton.tsx +17 -4
  6. package/components/AddProductsToCart/AddProductsToCartFab.tsx +7 -2
  7. package/components/AddProductsToCart/AddProductsToCartForm.tsx +31 -29
  8. package/components/AddProductsToCart/AddProductsToCartSnackbar.tsx +14 -63
  9. package/components/AddProductsToCart/AddProductsToCartSnackbarMessage.tsx +84 -0
  10. package/components/AddProductsToCart/UseAddProductsToCartAction.graphql +1 -1
  11. package/components/AddProductsToCart/findAddedItems.ts +1 -4
  12. package/components/AddProductsToCart/index.ts +1 -0
  13. package/components/AddProductsToCart/useAddProductsToCartAction.ts +2 -1
  14. package/components/AddProductsToCart/useFormAddProductsToCart.ts +1 -2
  15. package/components/JsonLdProduct/JsonLdProduct.graphql +1 -1
  16. package/components/JsonLdProduct/ProductPageJsonLd.tsx +1 -1
  17. package/components/ProductAddToCart/ProductAddToCart.tsx +6 -8
  18. package/components/ProductCustomizable/CustomizableCheckboxOption.tsx +3 -4
  19. package/components/ProductCustomizable/CustomizableMultipleOption.tsx +2 -2
  20. package/components/ProductCustomizable/CustomizableRadioOption.tsx +2 -2
  21. package/components/ProductCustomizable/ProductCustomizable.graphql +1 -1
  22. package/components/ProductCustomizable/index.ts +1 -0
  23. package/components/ProductCustomizable/productCustomizableSelectors.ts +59 -0
  24. package/components/ProductFiltersPro/PriceSlider.tsx +1 -2
  25. package/components/ProductFiltersPro/ProductFilterEqualChip.tsx +4 -5
  26. package/components/ProductFiltersPro/ProductFilterEqualSection.tsx +6 -7
  27. package/components/ProductFiltersPro/ProductFilterRangeChip.tsx +1 -1
  28. package/components/ProductFiltersPro/ProductFilterRangeSection.tsx +1 -1
  29. package/components/ProductFiltersPro/ProductFiltersPro.tsx +103 -19
  30. package/components/ProductFiltersPro/ProductFiltersProAggregations.tsx +41 -20
  31. package/components/ProductFiltersPro/ProductFiltersProAllFiltersChip.tsx +6 -10
  32. package/components/ProductFiltersPro/ProductFiltersProAllFiltersSidebar.tsx +18 -8
  33. package/components/ProductFiltersPro/ProductFiltersProCategorySection.tsx +130 -0
  34. package/components/ProductFiltersPro/ProductFiltersProChips.tsx +10 -8
  35. package/components/ProductFiltersPro/ProductFiltersProClearAll.tsx +4 -16
  36. package/components/ProductFiltersPro/ProductFiltersProLayoutSidebar.tsx +15 -7
  37. package/components/ProductFiltersPro/ProductFiltersProLimitChip.tsx +2 -8
  38. package/components/ProductFiltersPro/ProductFiltersProLimitSection.tsx +7 -10
  39. package/components/ProductFiltersPro/ProductFiltersProNoResults.tsx +79 -0
  40. package/components/ProductFiltersPro/ProductFiltersProSortChip.tsx +5 -7
  41. package/components/ProductFiltersPro/ProductFiltersProSortDirectionArrow.tsx +2 -4
  42. package/components/ProductFiltersPro/ProductFiltersProSortSection.tsx +11 -3
  43. package/components/ProductFiltersPro/activeAggregations.ts +5 -9
  44. package/components/ProductFiltersPro/applyAggregationCount.ts +14 -8
  45. package/components/ProductFiltersPro/index.ts +9 -0
  46. package/components/ProductFiltersPro/{useClearAllFiltersHandler.ts → useProductFiltersProClearAllAction.ts} +1 -1
  47. package/components/ProductFiltersPro/useProductFiltersProHasFiltersApplied.ts +21 -0
  48. package/components/ProductFiltersPro/useProductFiltersProSort.tsx +7 -3
  49. package/components/ProductList/ProductList.graphql +8 -5
  50. package/components/ProductListCount/ProductListCount.tsx +3 -1
  51. package/components/ProductListFilters/ProductFilters.graphql +11 -2
  52. package/components/ProductListFilters/ProductListFilters.graphql +1 -1
  53. package/components/ProductListFilters/ProductListFilters.tsx +13 -19
  54. package/components/ProductListFiltersContainer/ProductListFiltersContainer.tsx +2 -4
  55. package/components/ProductListItem/ProductDiscountLabel.tsx +2 -3
  56. package/components/ProductListItem/ProductListItem.tsx +3 -3
  57. package/components/ProductListItem/ProductListItemTitleAndPrice.tsx +18 -15
  58. package/components/ProductListItems/ProductFilterTypes.graphql +8 -0
  59. package/components/ProductListItems/ProductListItemsBase.tsx +71 -30
  60. package/components/ProductListItems/filterTypes.tsx +14 -7
  61. package/components/ProductListItems/filteredProductList.tsx +44 -17
  62. package/components/ProductListItems/getFilterTypes.ts +33 -4
  63. package/components/ProductListItems/productListApplyCategoryDefaults.ts +50 -4
  64. package/components/ProductListItems/renderer.tsx +8 -2
  65. package/components/ProductListPagination/ProductListPagination.tsx +39 -20
  66. package/components/ProductListPrice/ProductListPrice.tsx +9 -4
  67. package/components/ProductListSuggestions/ProductListSuggestions.graphql +5 -0
  68. package/components/ProductListSuggestions/ProductListSuggestions.tsx +42 -0
  69. package/components/ProductPageBreadcrumb/ProductPageBreadcrumb.graphql +3 -0
  70. package/components/ProductPageBreadcrumb/ProductPageBreadcrumb.tsx +3 -0
  71. package/components/ProductPageBreadcrumb/ProductPageBreadcrumbs.tsx +40 -0
  72. package/components/ProductPageBreadcrumb/index.ts +1 -0
  73. package/components/ProductPageDescription/ComplexTextValue.graphql +1 -1
  74. package/components/ProductPageDescription/ProductPageDescription.tsx +1 -1
  75. package/components/ProductPageGallery/ProductImage.graphql +1 -0
  76. package/components/ProductPageGallery/ProductPageGallery.tsx +14 -8
  77. package/components/ProductPagePrice/ProductPagePrice.graphql +0 -6
  78. package/components/ProductPagePrice/ProductPagePrice.tsx +19 -12
  79. package/components/ProductPagePrice/ProductPagePriceTiers.tsx +4 -3
  80. package/components/ProductPagePrice/useCustomizableOptionPrice.ts +11 -53
  81. package/components/ProductShortDescription/ProductShortDescription.tsx +2 -0
  82. package/components/ProductSpecs/ProductSpecs.graphql +21 -1
  83. package/components/ProductSpecs/ProductSpecs.tsx +5 -11
  84. package/components/ProductSpecs/ProductSpecsAggregations.tsx +34 -0
  85. package/components/ProductSpecs/ProductSpecsCustomAttributes.tsx +45 -0
  86. package/components/ProductSpecs/ProductSpecsTypes.graphql +8 -0
  87. package/components/ProductStaticPaths/getProductStaticPaths.ts +1 -1
  88. package/components/ProductWeight/ProductWeight.tsx +12 -9
  89. package/components/index.ts +2 -0
  90. package/hooks/useProductList.ts +148 -0
  91. package/hooks/useProductListLink.ts +6 -3
  92. package/index.ts +1 -0
  93. package/package.json +14 -14
@@ -1,5 +1,6 @@
1
+ import { useCartEnabled } from '@graphcommerce/magento-cart'
1
2
  import { Fab, FabProps, iconShoppingBag, iconCheckmark } from '@graphcommerce/next-ui'
2
- import { i18n } from '@lingui/core'
3
+ import { t } from '@lingui/macro'
3
4
  import { SxProps, Theme } from '@mui/material'
4
5
  import {
5
6
  useAddProductsToCartAction,
@@ -16,13 +17,17 @@ export function AddProductsToCartFab(props: AddProductsToCartFabProps) {
16
17
  const { icon = iconShoppingBag, product, sku, ...rest } = props
17
18
  const { showSuccess, ...action } = useAddProductsToCartAction(props)
18
19
 
20
+ const cartEnabled = useCartEnabled()
21
+
22
+ if (!cartEnabled) return null
23
+
19
24
  return (
20
25
  <Fab
21
26
  type='submit'
22
27
  {...rest}
23
28
  {...action}
24
29
  icon={showSuccess && !action.loading ? iconCheckmark : icon}
25
- aria-label={i18n._(/* i18n*/ `Add to Cart`)}
30
+ aria-label={t`Add to Cart`}
26
31
  />
27
32
  )
28
33
  }
@@ -5,8 +5,8 @@ import {
5
5
  CrosssellsDocument,
6
6
  CrosssellsQuery,
7
7
  } from '@graphcommerce/magento-cart'
8
- import { ExtendableComponent, nonNullable } from '@graphcommerce/next-ui'
9
- import { Box, SxProps, Theme, useThemeProps } from '@mui/material'
8
+ import { ErrorSnackbarProps, MessageSnackbarProps, nonNullable } from '@graphcommerce/next-ui'
9
+ import { Box, SxProps, Theme } from '@mui/material'
10
10
  import { useRouter } from 'next/router'
11
11
  import { useMemo, useRef } from 'react'
12
12
  import { AddProductsToCartDocument, AddProductsToCartMutation } from './AddProductsToCart.gql'
@@ -22,30 +22,27 @@ import {
22
22
  } from './useFormAddProductsToCart'
23
23
 
24
24
  export type AddProductsToCartFormProps = {
25
- // The props are actually used, but are passed through useThemeProps and that breaks react/no-unused-prop-types
26
- // eslint-disable-next-line react/no-unused-prop-types
27
25
  children: React.ReactNode
28
- // eslint-disable-next-line react/no-unused-prop-types
29
26
  sx?: SxProps<Theme>
30
- // eslint-disable-next-line react/no-unused-prop-types
31
27
  redirect?: RedirectType
32
-
28
+ snackbarProps?: AddProductsToCartSnackbarProps
29
+
30
+ /**
31
+ * @deprecated use snackbarProps.errorSnackbar instead
32
+ */
33
+ errorSnackbar?: Omit<ErrorSnackbarProps, 'open'>
34
+ /**
35
+ * @deprecated use snackbarProps.successSnackbar instead
36
+ */
37
+ successSnackbar?: Omit<MessageSnackbarProps, 'open' | 'action'>
38
+ /**
39
+ * @deprecated use snackbarProps.disableSuccessSnackbar instead
40
+ */
33
41
  disableSuccessSnackbar?: boolean
34
- } & UseFormGraphQlOptions<AddProductsToCartMutation, AddProductsToCartFields> &
35
- AddProductsToCartSnackbarProps
42
+ } & UseFormGraphQlOptions<AddProductsToCartMutation, AddProductsToCartFields>
36
43
 
37
44
  const name = 'AddProductsToCartForm'
38
45
 
39
- /** Expose the component to be exendable in your theme.components */
40
- declare module '@mui/material/styles/components' {
41
- interface Components {
42
- AddProductsToCartForm?: Pick<
43
- ExtendableComponent<Omit<AddProductsToCartFormProps, 'children'>>,
44
- 'defaultProps'
45
- >
46
- }
47
- }
48
-
49
46
  /**
50
47
  * Component that handles adding products to the cart. Used on the product page, but can be used for
51
48
  * any product listing.
@@ -66,8 +63,9 @@ export function AddProductsToCartForm(props: AddProductsToCartFormProps) {
66
63
  disableSuccessSnackbar,
67
64
  errorSnackbar,
68
65
  successSnackbar,
66
+ snackbarProps,
69
67
  ...formProps
70
- } = useThemeProps({ name, props })
68
+ } = props
71
69
  const router = useRouter()
72
70
  const client = useApolloClient()
73
71
  const crosssellsQuery = useRef<Promise<ApolloQueryResult<CrosssellsQuery>>>()
@@ -79,7 +77,6 @@ export function AddProductsToCartForm(props: AddProductsToCartFormProps) {
79
77
  AddProductsToCartDocument,
80
78
  {
81
79
  ...formProps,
82
- experimental_useV2: true,
83
80
  // We're stripping out incomplete entered options.
84
81
  onBeforeSubmit: async (variables) => {
85
82
  const variables2 = (await formProps.onBeforeSubmit?.(variables)) ?? variables
@@ -90,7 +87,7 @@ export function AddProductsToCartForm(props: AddProductsToCartFormProps) {
90
87
  const requestData = {
91
88
  cartId,
92
89
  cartItems: cartItems
93
- .filter((cartItem) => cartItem.sku)
90
+ .filter((cartItem) => cartItem.sku && cartItem.quantity !== 0)
94
91
  .map(({ customizable_options, ...cartItem }) => {
95
92
  const options = Object.values(customizable_options ?? {})
96
93
  .flat(1)
@@ -103,7 +100,12 @@ export function AddProductsToCartForm(props: AddProductsToCartFormProps) {
103
100
  ...(cartItem.selected_options ?? []).filter(Boolean),
104
101
  ...options,
105
102
  ],
106
- entered_options: cartItem.entered_options?.filter((option) => option?.value),
103
+ entered_options: [
104
+ ...(cartItem.entered_options
105
+ ?.filter((option) => option?.value)
106
+ .filter(nonNullable)
107
+ .map((option) => ({ uid: option.uid, value: `${option?.value}` })) ?? []),
108
+ ],
107
109
  }
108
110
  }),
109
111
  }
@@ -155,12 +157,12 @@ export function AddProductsToCartForm(props: AddProductsToCartFormProps) {
155
157
  <Box component='form' onSubmit={submit} noValidate sx={sx} className={name}>
156
158
  {children}
157
159
  </Box>
158
- {disableSuccessSnackbar ? null : (
159
- <AddProductsToCartSnackbar
160
- errorSnackbar={errorSnackbar}
161
- successSnackbar={successSnackbar}
162
- />
163
- )}
160
+ <AddProductsToCartSnackbar
161
+ errorSnackbar={errorSnackbar}
162
+ successSnackbar={successSnackbar}
163
+ disableSuccessSnackbar={disableSuccessSnackbar}
164
+ {...snackbarProps}
165
+ />
164
166
  </AddProductsToCartContext.Provider>
165
167
  )
166
168
  }
@@ -1,18 +1,7 @@
1
1
  import { useFormState } from '@graphcommerce/ecommerce-ui'
2
- import { ApolloCartErrorSnackbar } from '@graphcommerce/magento-cart'
3
- import {
4
- Button,
5
- ErrorSnackbar,
6
- ErrorSnackbarProps,
7
- iconChevronRight,
8
- IconSvg,
9
- MessageSnackbar,
10
- MessageSnackbarProps,
11
- nonNullable,
12
- useLocale,
13
- } from '@graphcommerce/next-ui'
14
- import { Trans } from '@lingui/react'
2
+ import { ErrorSnackbarProps, MessageSnackbarProps, nonNullable } from '@graphcommerce/next-ui'
15
3
  import { useMemo } from 'react'
4
+ import { AddProductsToCartSnackbarMessage } from './AddProductsToCartSnackbarMessage'
16
5
  import { findAddedItems } from './findAddedItems'
17
6
  import { toUserErrors } from './toUserErrors'
18
7
  import { useFormAddProductsToCart } from './useFormAddProductsToCart'
@@ -20,18 +9,19 @@ import { useFormAddProductsToCart } from './useFormAddProductsToCart'
20
9
  export type AddProductsToCartSnackbarProps = {
21
10
  errorSnackbar?: Omit<ErrorSnackbarProps, 'open'>
22
11
  successSnackbar?: Omit<MessageSnackbarProps, 'open' | 'action'>
12
+ disableSuccessSnackbar?: boolean
23
13
  }
24
14
 
25
15
  export function AddProductsToCartSnackbar(props: AddProductsToCartSnackbarProps) {
26
- const { errorSnackbar, successSnackbar } = props
16
+ const { errorSnackbar, successSnackbar, disableSuccessSnackbar } = props
27
17
  const { error, data, redirect, control, submittedVariables } = useFormAddProductsToCart()
28
- const formState = useFormState({ control })
29
18
 
30
- const formatter = new Intl.ListFormat(useLocale(), { style: 'long', type: 'conjunction' })
19
+ const formState = useFormState({ control })
31
20
 
32
21
  const userErrors = toUserErrors(data)
33
22
 
34
23
  const showSuccess =
24
+ !disableSuccessSnackbar &&
35
25
  !formState.isSubmitting &&
36
26
  formState.isSubmitSuccessful &&
37
27
  !error?.message &&
@@ -43,53 +33,14 @@ export function AddProductsToCartSnackbar(props: AddProductsToCartSnackbarProps)
43
33
  [data, submittedVariables],
44
34
  )
45
35
 
46
- const showErrorSnackbar = userErrors.length > 0
47
-
48
36
  return (
49
- <>
50
- {error && <ApolloCartErrorSnackbar error={error} />}
51
-
52
- {showErrorSnackbar && (
53
- <ErrorSnackbar variant='pill' severity='error' {...errorSnackbar} open={showErrorSnackbar}>
54
- <>{data?.addProductsToCart?.user_errors?.map((e) => e?.message).join(', ')}</>
55
- </ErrorSnackbar>
56
- )}
57
-
58
- {showSuccess && (
59
- <MessageSnackbar
60
- variant='pill'
61
- severity='success'
62
- {...successSnackbar}
63
- open={showSuccess}
64
- action={
65
- <Button
66
- href='/cart'
67
- id='view-shopping-cart-button'
68
- size='medium'
69
- variant='pill'
70
- color='secondary'
71
- endIcon={<IconSvg src={iconChevronRight} />}
72
- sx={{ display: 'flex' }}
73
- >
74
- <Trans id='View shopping cart' />
75
- </Button>
76
- }
77
- >
78
- <Trans
79
- id={
80
- addedItems.length === 1
81
- ? '<0>{name}</0> has been added to your shopping cart!'
82
- : '<0>{name}</0> have been added to your shopping cart!'
83
- }
84
- components={{ 0: <strong /> }}
85
- values={{
86
- name: formatter.format(
87
- addedItems.map((item) => item?.itemInCart?.product.name).filter(nonNullable),
88
- ),
89
- }}
90
- />
91
- </MessageSnackbar>
92
- )}
93
- </>
37
+ <AddProductsToCartSnackbarMessage
38
+ error={!formState.isSubmitting ? error : undefined}
39
+ showSuccess={showSuccess}
40
+ userErrors={data?.addProductsToCart?.user_errors.filter(nonNullable)}
41
+ addedItems={addedItems.map((item) => item.itemInCart?.product.name).filter(nonNullable)}
42
+ errorSnackbar={errorSnackbar}
43
+ successSnackbar={successSnackbar}
44
+ />
94
45
  )
95
46
  }
@@ -0,0 +1,84 @@
1
+ import { ApolloError } from '@graphcommerce/graphql'
2
+ import { CartUserInputError } from '@graphcommerce/graphql-mesh'
3
+ import { ApolloCartErrorSnackbar } from '@graphcommerce/magento-cart'
4
+ import {
5
+ Button,
6
+ ErrorSnackbar,
7
+ ErrorSnackbarProps,
8
+ IconSvg,
9
+ ListFormat,
10
+ MessageSnackbar,
11
+ MessageSnackbarProps,
12
+ iconChevronRight,
13
+ } from '@graphcommerce/next-ui'
14
+ import { Plural, Trans } from '@lingui/macro'
15
+
16
+ export type AddProductsToCartSnackbarMessageProps = {
17
+ errorSnackbar?: Omit<ErrorSnackbarProps, 'open'>
18
+ successSnackbar?: Omit<MessageSnackbarProps, 'open' | 'action'>
19
+ error?: ApolloError
20
+ userErrors?: Pick<CartUserInputError, 'message'>[]
21
+ showSuccess: boolean
22
+ addedItems: string[]
23
+ }
24
+
25
+ export function AddProductsToCartSnackbarMessage(props: AddProductsToCartSnackbarMessageProps) {
26
+ const { errorSnackbar, successSnackbar, error, userErrors, showSuccess, addedItems } = props
27
+
28
+ const showErrorSnackbar = !!userErrors?.length
29
+
30
+ return (
31
+ <>
32
+ {error && <ApolloCartErrorSnackbar error={error} />}
33
+
34
+ {showErrorSnackbar && (
35
+ <ErrorSnackbar variant='pill' severity='error' {...errorSnackbar} open={showErrorSnackbar}>
36
+ <>{userErrors.map((e) => e?.message).join(', ')}</>
37
+ </ErrorSnackbar>
38
+ )}
39
+
40
+ {showSuccess && (
41
+ <MessageSnackbar
42
+ variant='pill'
43
+ severity='success'
44
+ {...successSnackbar}
45
+ open={showSuccess}
46
+ action={
47
+ <Button
48
+ href='/cart'
49
+ id='view-shopping-cart-button'
50
+ size='medium'
51
+ variant='pill'
52
+ color='secondary'
53
+ endIcon={<IconSvg src={iconChevronRight} />}
54
+ sx={{ display: 'flex' }}
55
+ >
56
+ <Trans>View shopping cart</Trans>
57
+ </Button>
58
+ }
59
+ >
60
+ <Plural
61
+ value={addedItems.length}
62
+ one={
63
+ <Trans>
64
+ <ListFormat listStyle='long' type='conjunction'>
65
+ {addedItems.map((item) => item)}
66
+ </ListFormat>{' '}
67
+ has been added to your shopping cart
68
+ </Trans>
69
+ }
70
+ two={
71
+ <Trans>
72
+ <ListFormat listStyle='long' type='conjunction'>
73
+ {addedItems.map((item) => item)}
74
+ </ListFormat>{' '}
75
+ have been added to your shopping cart!
76
+ </Trans>
77
+ }
78
+ other={<Trans># products have been added to your shopping cart!</Trans>}
79
+ />
80
+ </MessageSnackbar>
81
+ )}
82
+ </>
83
+ )
84
+ }
@@ -1,4 +1,4 @@
1
- fragment UseAddProductsToCartAction on ProductInterface @injectable {
1
+ fragment UseAddProductsToCartAction on ProductInterface {
2
2
  uid
3
3
  sku
4
4
  only_x_left_in_stock
@@ -47,10 +47,7 @@ export function findAddedItems(
47
47
  }
48
48
  }
49
49
 
50
- const customizable_options = isTypename(cartItem, ['ConfigurableCartItem'])
51
- ? cartItem.configurable_customizable
52
- : cartItem.customizable_options
53
-
50
+ const { customizable_options } = cartItem
54
51
  const matchEntered = filterNonNullableKeys(itemVariable.entered_options).every(
55
52
  (requestOption) =>
56
53
  customizable_options.find(
@@ -4,6 +4,7 @@ export * from './AddProductsToCartError'
4
4
  export * from './AddProductsToCartFab'
5
5
  export * from './AddProductsToCartForm'
6
6
  export * from './AddProductsToCartSnackbar'
7
+ export * from './AddProductsToCartSnackbarMessage'
7
8
  export * from './AddProductsToCartQuantity'
8
9
  export * from './useAddProductsToCartAction'
9
10
  export * from './useFormAddProductsToCart'
@@ -68,7 +68,8 @@ export function useAddProductsToCartAction(
68
68
  if (process.env.NODE_ENV !== 'production') {
69
69
  if (!sku) console.warn(`You must provide a 'sku' to useAddProductsToCartAction`)
70
70
  }
71
- setValue(`cartItems.${index}.sku`, sku ?? '')
71
+ // TODO should be removed, setting the form value on submission isn't a great idea.
72
+ if (!getValues(`cartItems.${index}.sku`)) setValue(`cartItems.${index}.sku`, sku ?? '')
72
73
  startTransition(() => {
73
74
  onClickIncoming?.(e)
74
75
  })
@@ -1,11 +1,10 @@
1
1
  import { UseFormGqlMutationReturn } from '@graphcommerce/ecommerce-ui'
2
2
  import { createContext, useContext } from 'react'
3
- import type { LiteralUnion } from 'type-fest'
3
+ import type { Simplify, LiteralUnion } from 'type-fest'
4
4
  import {
5
5
  AddProductsToCartMutation,
6
6
  AddProductsToCartMutationVariables,
7
7
  } from './AddProductsToCart.gql'
8
- import { Simplify } from 'type-fest'
9
8
 
10
9
  export type RedirectType = LiteralUnion<'added' | undefined | false, `/${string}`>
11
10
 
@@ -1,4 +1,4 @@
1
- fragment JsonLdProduct on ProductInterface @inject(into: ["ProductPageItem"]) @injectable {
1
+ fragment JsonLdProduct on ProductInterface @inject(into: ["ProductPageItem"]) {
2
2
  uid
3
3
  name
4
4
  sku
@@ -13,5 +13,5 @@ export function ProductPageJsonLd<T extends { '@type': string }, P extends JsonL
13
13
  props: ProductPageJsonLdProps<T, P>,
14
14
  ) {
15
15
  const { product, render } = props
16
- return <JsonLd<T> item={render(product)} />
16
+ return <JsonLd<T> item={render(product)} keyVal='product-jsonld' />
17
17
  }
@@ -9,7 +9,7 @@ import {
9
9
  IconSvg,
10
10
  extendableComponent,
11
11
  } from '@graphcommerce/next-ui'
12
- import { Trans } from '@lingui/react'
12
+ import { Trans } from '@lingui/macro'
13
13
  import { Divider, Typography, ButtonProps, Box, Alert } from '@mui/material'
14
14
  import React from 'react'
15
15
  import { ProductAddToCartDocument, ProductAddToCartMutationVariables } from './ProductAddToCart.gql'
@@ -93,7 +93,7 @@ export function ProductAddToCart(
93
93
  })}
94
94
  {...buttonProps}
95
95
  >
96
- <Trans id='Add to Cart' />
96
+ <Trans>Add to Cart</Trans>
97
97
  </Button>
98
98
  {additionalButtons}
99
99
  </Box>
@@ -125,15 +125,13 @@ export function ProductAddToCart(
125
125
  color='secondary'
126
126
  endIcon={<IconSvg src={iconChevronRight} />}
127
127
  >
128
- <Trans id='View shopping cart' />
128
+ <Trans>View shopping cart</Trans>
129
129
  </Button>
130
130
  }
131
131
  >
132
- <Trans
133
- id='<0>{name}</0> has been added to your shopping cart!'
134
- components={{ 0: <strong /> }}
135
- values={{ name }}
136
- />
132
+ <Trans>
133
+ <strong>{name}</strong> has been added to your shopping cart!
134
+ </Trans>
137
135
  </MessageSnackbar>
138
136
  </Box>
139
137
  )
@@ -1,14 +1,13 @@
1
+ import { ActionCardListForm } from '@graphcommerce/ecommerce-ui'
1
2
  import { Money } from '@graphcommerce/magento-store'
2
3
  import {
3
- ActionCardListForm,
4
4
  ActionCard,
5
- filterNonNullableKeys,
6
5
  ActionCardProps,
6
+ filterNonNullableKeys,
7
7
  SectionHeader,
8
8
  } from '@graphcommerce/next-ui'
9
9
  import { i18n } from '@lingui/core'
10
- // eslint-disable-next-line @typescript-eslint/no-restricted-imports
11
- import { Checkbox, Box } from '@mui/material'
10
+ import { Box, Checkbox } from '@mui/material'
12
11
  import { useFormAddProductsToCart } from '../AddProductsToCart'
13
12
  import { OptionTypeRenderer } from './CustomizableAreaOption'
14
13
 
@@ -1,9 +1,9 @@
1
+ import { ActionCardListForm } from '@graphcommerce/ecommerce-ui'
1
2
  import { Money } from '@graphcommerce/magento-store'
2
3
  import {
3
- ActionCardListForm,
4
4
  ActionCard,
5
- filterNonNullableKeys,
6
5
  ActionCardProps,
6
+ filterNonNullableKeys,
7
7
  SectionHeader,
8
8
  } from '@graphcommerce/next-ui'
9
9
  import { i18n } from '@lingui/core'
@@ -1,9 +1,9 @@
1
+ import { ActionCardListForm } from '@graphcommerce/ecommerce-ui'
1
2
  import { Money } from '@graphcommerce/magento-store'
2
3
  import {
3
- ActionCardListForm,
4
4
  ActionCard,
5
- filterNonNullableKeys,
6
5
  ActionCardProps,
6
+ filterNonNullableKeys,
7
7
  SectionHeader,
8
8
  } from '@graphcommerce/next-ui'
9
9
  import { i18n } from '@lingui/core'
@@ -1,4 +1,4 @@
1
- fragment ProductCustomizable on CustomizableProductInterface @injectable {
1
+ fragment ProductCustomizable on CustomizableProductInterface {
2
2
  options {
3
3
  uid
4
4
  __typename
@@ -1 +1,2 @@
1
1
  export * from './ProductCustomizable'
2
+ export * from './productCustomizableSelectors'
@@ -0,0 +1,59 @@
1
+ import type { PriceTypeEnum } from '@graphcommerce/graphql-mesh'
2
+ import type { Simplify } from 'type-fest'
3
+ import type { CustomizableAreaOptionFragment } from './CustomizableAreaOption.gql'
4
+ import type { CustomizableCheckboxOptionFragment } from './CustomizableCheckboxOption.gql'
5
+ import type { CustomizableDateOptionFragment } from './CustomizableDateOption.gql'
6
+ import type { CustomizableDropDownOptionFragment } from './CustomizableDropDownOption.gql'
7
+ import type { CustomizableFieldOptionFragment } from './CustomizableFieldOption.gql'
8
+ import type { CustomizableFileOptionFragment } from './CustomizableFileOption.gql'
9
+ import type { CustomizableMultipleOptionFragment } from './CustomizableMultipleOption.gql'
10
+ import type { CustomizableRadioOptionFragment } from './CustomizableRadioOption.gql'
11
+ import type { ProductCustomizable_SimpleProduct_Fragment } from './ProductCustomizable.gql'
12
+
13
+ export type CustomizableProductOptionBase =
14
+ | {
15
+ price?: number | null | undefined
16
+ price_type?: PriceTypeEnum | null | undefined
17
+ uid?: string | null | undefined
18
+ }
19
+ | undefined
20
+ | null
21
+
22
+ export type AnyOption = NonNullable<
23
+ NonNullable<ProductCustomizable_SimpleProduct_Fragment['options']>[number]
24
+ >
25
+
26
+ export type OptionValueSelector = {
27
+ [T in AnyOption as T['__typename']]: (
28
+ option: T,
29
+ ) => CustomizableProductOptionBase | CustomizableProductOptionBase[]
30
+ }
31
+
32
+ type MissingOptionValueSelectors = Omit<
33
+ OptionValueSelector,
34
+ keyof typeof productCustomizableSelectors
35
+ >
36
+ type DefinedOptionValueSelectors = Partial<
37
+ Pick<OptionValueSelector, keyof typeof productCustomizableSelectors>
38
+ >
39
+
40
+ type Selectors = Simplify<
41
+ keyof MissingOptionValueSelectors extends never
42
+ ? (MissingOptionValueSelectors & DefinedOptionValueSelectors) | undefined
43
+ : MissingOptionValueSelectors & DefinedOptionValueSelectors
44
+ >
45
+
46
+ export const productCustomizableSelectors = {
47
+ CustomizableAreaOption: (o: CustomizableAreaOptionFragment) => o.areaValue,
48
+ CustomizableCheckboxOption: (o: CustomizableCheckboxOptionFragment) => o.checkboxValue,
49
+ CustomizableFileOption: (o: CustomizableFileOptionFragment) => o.fileValue,
50
+ CustomizableDateOption: (o: CustomizableDateOptionFragment) => o.dateValue,
51
+ CustomizableDropDownOption: (o: CustomizableDropDownOptionFragment) => o.dropdownValue,
52
+ CustomizableFieldOption: (o: CustomizableFieldOptionFragment) => o.fieldValue,
53
+ CustomizableMultipleOption: (o: CustomizableMultipleOptionFragment) => o.multipleValue,
54
+ CustomizableRadioOption: (o: CustomizableRadioOptionFragment) => o.radioValue,
55
+ }
56
+
57
+ export type SelectorsProp = keyof MissingOptionValueSelectors extends never
58
+ ? { selectors?: Selectors }
59
+ : { selectors: Selectors }
@@ -1,10 +1,9 @@
1
1
  import { FilterRangeTypeInput } from '@graphcommerce/graphql-mesh'
2
2
  import { Money } from '@graphcommerce/magento-store'
3
3
  import { extendableComponent, filterNonNullableKeys } from '@graphcommerce/next-ui'
4
- // eslint-disable-next-line @typescript-eslint/no-restricted-imports
5
4
  import { Box, Slider, SxProps, Theme, useEventCallback } from '@mui/material'
6
5
  import { useCallback } from 'react'
7
- import { FilterProps } from './ProductFiltersProAggregations'
6
+ import type { FilterProps } from './ProductFiltersProAggregations'
8
7
 
9
8
  export type PriceSliderProps = {
10
9
  value: FilterRangeTypeInput | null | undefined
@@ -1,18 +1,17 @@
1
- import { useWatch } from '@graphcommerce/ecommerce-ui'
1
+ import { ActionCardListForm, useWatch } from '@graphcommerce/ecommerce-ui'
2
2
  import type { ProductAttributeFilterInput } from '@graphcommerce/graphql-mesh'
3
3
  import {
4
- ChipOverlayOrPopper,
5
- ActionCardListForm,
6
4
  ActionCard,
5
+ ChipOverlayOrPopper,
7
6
  filterNonNullableKeys,
8
- IconSvg,
9
7
  iconCirle,
8
+ IconSvg,
10
9
  } from '@graphcommerce/next-ui'
11
10
  import { Box } from '@mui/material'
12
11
  import { useMemo } from 'react'
13
12
  import { isFilterTypeEqual } from '../ProductListItems/filterTypes'
14
13
  import { useProductFiltersPro } from './ProductFiltersPro'
15
- import { FilterProps } from './ProductFiltersProAggregations'
14
+ import type { FilterProps } from './ProductFiltersProAggregations'
16
15
 
17
16
  export function ProductFilterEqualChip(props: FilterProps) {
18
17
  const { aggregation } = props
@@ -1,20 +1,19 @@
1
- import { useWatch } from '@graphcommerce/ecommerce-ui'
1
+ import { ActionCardListForm, useWatch } from '@graphcommerce/ecommerce-ui'
2
2
  import type { ProductAttributeFilterInput } from '@graphcommerce/graphql-mesh'
3
3
  import {
4
- ActionCardListForm,
5
4
  ActionCard,
6
- filterNonNullableKeys,
7
- IconSvg,
8
- iconCirle,
9
5
  ActionCardAccordion,
10
6
  Button,
7
+ filterNonNullableKeys,
8
+ iconCirle,
9
+ IconSvg,
11
10
  } from '@graphcommerce/next-ui'
12
11
  import { Trans } from '@lingui/react'
13
12
  import { Box } from '@mui/material'
14
13
  import { useMemo } from 'react'
15
14
  import { isFilterTypeEqual } from '../ProductListItems/filterTypes'
16
15
  import { useProductFiltersPro } from './ProductFiltersPro'
17
- import { FilterProps } from './ProductFiltersProAggregations'
16
+ import type { FilterProps } from './ProductFiltersProAggregations'
18
17
 
19
18
  export function ProductFilterEqualSection(props: FilterProps) {
20
19
  const { aggregation } = props
@@ -66,7 +65,7 @@ export function ProductFilterEqualSection(props: FilterProps) {
66
65
  multiple
67
66
  layout='list'
68
67
  variant='default'
69
- size='medium'
68
+ size='responsive'
70
69
  items={items}
71
70
  showMoreAfter={4}
72
71
  />
@@ -5,7 +5,7 @@ import { ChipOverlayOrPopper, extendableComponent } from '@graphcommerce/next-ui
5
5
  import { isFilterTypeRange } from '../ProductListItems/filterTypes'
6
6
  import { getMinMaxFromOptions, PriceSlider } from './PriceSlider'
7
7
  import { useProductFiltersPro } from './ProductFiltersPro'
8
- import { FilterProps } from './ProductFiltersProAggregations'
8
+ import type { FilterProps } from './ProductFiltersProAggregations'
9
9
 
10
10
  const { classes } = extendableComponent('FilterRangeType', ['root', 'container', 'slider'] as const)
11
11
 
@@ -5,7 +5,7 @@ import { Trans } from '@lingui/react'
5
5
  import { isFilterTypeRange } from '../ProductListItems/filterTypes'
6
6
  import { PriceSlider, getMinMaxFromOptions } from './PriceSlider'
7
7
  import { useProductFiltersPro } from './ProductFiltersPro'
8
- import { FilterProps } from './ProductFiltersProAggregations'
8
+ import type { FilterProps } from './ProductFiltersProAggregations'
9
9
 
10
10
  export function ProductFilterRangeSection(props: FilterProps) {
11
11
  const { aggregation } = props