@graphcommerce/magento-product-configurable 9.0.0-canary.115 → 9.0.0-canary.117
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 +4 -0
- package/ConfigurableCartItem/ConfigurableCartItem.tsx +4 -0
- package/index.ts +0 -2
- package/package.json +18 -18
- package/ConfigurableContext/ConfigurableContext.tsx +0 -157
- package/ConfigurableContext/cheapestVariant.ts +0 -14
- package/ConfigurableOptions/ConfigurableOptions.tsx +0 -146
- package/ConfigurableProductAddToCart/ConfigurableProductAddToCart.tsx +0 -182
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,10 @@ import { CartItem } from '@graphcommerce/magento-cart-items'
|
|
|
3
3
|
import type { ConfigurableCartItemFragment } from './ConfigurableCartItem.gql'
|
|
4
4
|
import { OptionsList } from './OptionsList'
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* @deprecated
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
6
10
|
export function ConfigurableCartItem(props: ConfigurableCartItemFragment & CartItemProps) {
|
|
7
11
|
const { configurable_options, configured_variant, product } = props
|
|
8
12
|
return (
|
package/index.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
export * from './components'
|
|
2
2
|
export * from './ConfigurableCartItem/ConfigurableCartItem'
|
|
3
|
-
export * from './ConfigurableContext/ConfigurableContext'
|
|
4
|
-
export * from './ConfigurableProductAddToCart/ConfigurableProductAddToCart'
|
|
5
3
|
export * from './ConfigurableProductPage.gql'
|
|
6
4
|
export * from './graphql'
|
|
7
5
|
export * from './hooks'
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@graphcommerce/magento-product-configurable",
|
|
3
3
|
"homepage": "https://www.graphcommerce.org/",
|
|
4
4
|
"repository": "github:graphcommerce-org/graphcommerce",
|
|
5
|
-
"version": "9.0.0-canary.
|
|
5
|
+
"version": "9.0.0-canary.117",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"prettier": "@graphcommerce/prettier-config-pwa",
|
|
8
8
|
"eslintConfig": {
|
|
@@ -12,23 +12,23 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"peerDependencies": {
|
|
15
|
-
"@graphcommerce/ecommerce-ui": "^9.0.0-canary.
|
|
16
|
-
"@graphcommerce/eslint-config-pwa": "^9.0.0-canary.
|
|
17
|
-
"@graphcommerce/graphql": "^9.0.0-canary.
|
|
18
|
-
"@graphcommerce/graphql-mesh": "^9.0.0-canary.
|
|
19
|
-
"@graphcommerce/image": "^9.0.0-canary.
|
|
20
|
-
"@graphcommerce/lingui-next": "9.0.0-canary.
|
|
21
|
-
"@graphcommerce/magento-cart": "^9.0.0-canary.
|
|
22
|
-
"@graphcommerce/magento-cart-items": "^9.0.0-canary.
|
|
23
|
-
"@graphcommerce/magento-category": "^9.0.0-canary.
|
|
24
|
-
"@graphcommerce/magento-customer": "^9.0.0-canary.
|
|
25
|
-
"@graphcommerce/magento-product": "^9.0.0-canary.
|
|
26
|
-
"@graphcommerce/magento-product-simple": "^9.0.0-canary.
|
|
27
|
-
"@graphcommerce/magento-store": "^9.0.0-canary.
|
|
28
|
-
"@graphcommerce/next-ui": "^9.0.0-canary.
|
|
29
|
-
"@graphcommerce/prettier-config-pwa": "^9.0.0-canary.
|
|
30
|
-
"@graphcommerce/react-hook-form": "^9.0.0-canary.
|
|
31
|
-
"@graphcommerce/typescript-config-pwa": "^9.0.0-canary.
|
|
15
|
+
"@graphcommerce/ecommerce-ui": "^9.0.0-canary.117",
|
|
16
|
+
"@graphcommerce/eslint-config-pwa": "^9.0.0-canary.117",
|
|
17
|
+
"@graphcommerce/graphql": "^9.0.0-canary.117",
|
|
18
|
+
"@graphcommerce/graphql-mesh": "^9.0.0-canary.117",
|
|
19
|
+
"@graphcommerce/image": "^9.0.0-canary.117",
|
|
20
|
+
"@graphcommerce/lingui-next": "9.0.0-canary.117",
|
|
21
|
+
"@graphcommerce/magento-cart": "^9.0.0-canary.117",
|
|
22
|
+
"@graphcommerce/magento-cart-items": "^9.0.0-canary.117",
|
|
23
|
+
"@graphcommerce/magento-category": "^9.0.0-canary.117",
|
|
24
|
+
"@graphcommerce/magento-customer": "^9.0.0-canary.117",
|
|
25
|
+
"@graphcommerce/magento-product": "^9.0.0-canary.117",
|
|
26
|
+
"@graphcommerce/magento-product-simple": "^9.0.0-canary.117",
|
|
27
|
+
"@graphcommerce/magento-store": "^9.0.0-canary.117",
|
|
28
|
+
"@graphcommerce/next-ui": "^9.0.0-canary.117",
|
|
29
|
+
"@graphcommerce/prettier-config-pwa": "^9.0.0-canary.117",
|
|
30
|
+
"@graphcommerce/react-hook-form": "^9.0.0-canary.117",
|
|
31
|
+
"@graphcommerce/typescript-config-pwa": "^9.0.0-canary.117",
|
|
32
32
|
"@lingui/core": "^4.2.1",
|
|
33
33
|
"@lingui/macro": "^4.2.1",
|
|
34
34
|
"@lingui/react": "^4.2.1",
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import type { Context, Dispatch, SetStateAction } from 'react'
|
|
2
|
-
import { createContext, useCallback, useContext, useMemo, useState } from 'react'
|
|
3
|
-
import type { ConfigurableProductFormFragment } from './ConfigurableProductForm.gql'
|
|
4
|
-
import cheapestVariant from './cheapestVariant'
|
|
5
|
-
|
|
6
|
-
export type ConfigurableProductFormProps = ConfigurableProductFormFragment & {
|
|
7
|
-
sku: string
|
|
8
|
-
children?: React.ReactNode
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export type Selected = { [attrCode: string]: number }
|
|
12
|
-
export type Variants = NonNullable<ConfigurableProductFormProps['variants']>
|
|
13
|
-
type GetVariants = (values?: Selected) => Variants
|
|
14
|
-
type GetUids = (values?: Selected) => string[]
|
|
15
|
-
|
|
16
|
-
type ConfigurableContext = {
|
|
17
|
-
selection: Selected
|
|
18
|
-
variants: Variants
|
|
19
|
-
cheapest: Variants[0]
|
|
20
|
-
select: Dispatch<SetStateAction<Selected>>
|
|
21
|
-
options: ConfigurableProductFormFragment['configurable_options']
|
|
22
|
-
getVariants: GetVariants
|
|
23
|
-
getUids: GetUids
|
|
24
|
-
}
|
|
25
|
-
const contexts: { [sku: string]: Context<ConfigurableContext> } = {}
|
|
26
|
-
|
|
27
|
-
function configurableContext(sku: string): Context<ConfigurableContext> {
|
|
28
|
-
if (contexts?.[sku]) return contexts[sku]
|
|
29
|
-
contexts[sku] = createContext<ConfigurableContext>({
|
|
30
|
-
selection: {},
|
|
31
|
-
variants: [],
|
|
32
|
-
cheapest: {},
|
|
33
|
-
select: () => {},
|
|
34
|
-
options: undefined,
|
|
35
|
-
getVariants: () => [],
|
|
36
|
-
getUids: () => [],
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
return contexts[sku]
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
type AttributeTree = {
|
|
43
|
-
code: string
|
|
44
|
-
values: AttributeValues
|
|
45
|
-
}
|
|
46
|
-
type AttributeValues = {
|
|
47
|
-
[index: string]: {
|
|
48
|
-
variants: NonNullable<ConfigurableProductFormProps['variants']>
|
|
49
|
-
attribute?: AttributeTree
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function generateAttrTree(
|
|
54
|
-
idx: number,
|
|
55
|
-
options: ConfigurableProductFormProps['configurable_options'],
|
|
56
|
-
variants: ConfigurableProductFormProps['variants'],
|
|
57
|
-
selected: Selected,
|
|
58
|
-
tree?: AttributeTree,
|
|
59
|
-
) {
|
|
60
|
-
const attribute = options?.[idx]
|
|
61
|
-
if (!attribute || !attribute.attribute_code) return tree
|
|
62
|
-
|
|
63
|
-
const attributeTree: AttributeTree = { code: attribute.attribute_code, values: {} }
|
|
64
|
-
|
|
65
|
-
attribute.values?.forEach((val) => {
|
|
66
|
-
if (!val?.uid) return
|
|
67
|
-
const newSelected = { ...selected, [attributeTree.code]: [val.uid] } as Selected
|
|
68
|
-
|
|
69
|
-
const filteredVariants = variants?.filter(
|
|
70
|
-
(variant) =>
|
|
71
|
-
!!variant?.attributes?.find(
|
|
72
|
-
(attr) => attr?.code === attribute.attribute_code && val.uid === attr?.uid,
|
|
73
|
-
),
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
attributeTree.values[val.uid] = {
|
|
77
|
-
variants: filteredVariants ?? [],
|
|
78
|
-
attribute: generateAttrTree(idx + 1, options, filteredVariants, newSelected),
|
|
79
|
-
}
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
return attributeTree
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function traverseAttrTree(selection: Selected, attrTree: AttributeTree | undefined): Variants {
|
|
86
|
-
if (!attrTree) return []
|
|
87
|
-
|
|
88
|
-
const id = selection?.[attrTree.code]
|
|
89
|
-
const attrVal = id ? attrTree.values[id] : undefined
|
|
90
|
-
|
|
91
|
-
// We have a request, but isn't found in the current tree node
|
|
92
|
-
if (id && !attrVal) return []
|
|
93
|
-
|
|
94
|
-
if (attrVal?.attribute) return traverseAttrTree(selection, attrVal.attribute)
|
|
95
|
-
if (attrVal?.variants) return attrVal.variants
|
|
96
|
-
|
|
97
|
-
const attrValues = Object.entries(attrTree.values)
|
|
98
|
-
const variantList: NonNullable<ConfigurableProductFormProps['variants']> = []
|
|
99
|
-
|
|
100
|
-
attrValues.forEach(([optionId, attrVal2]) => {
|
|
101
|
-
variantList.push(
|
|
102
|
-
...(attrVal2.attribute
|
|
103
|
-
? traverseAttrTree({ ...selection, [attrTree.code]: Number(optionId) }, attrVal2.attribute)
|
|
104
|
-
: attrVal2.variants),
|
|
105
|
-
)
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
return variantList
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export function ConfigurableContextProvider(props: ConfigurableProductFormProps) {
|
|
112
|
-
const { children, sku, configurable_options, variants: providedVariants } = props
|
|
113
|
-
const [selection, select] = useState<Selected>({})
|
|
114
|
-
|
|
115
|
-
if (!configurable_options || !providedVariants)
|
|
116
|
-
throw Error('please provide configurabl_options and variants')
|
|
117
|
-
|
|
118
|
-
const lookupTree = useMemo(
|
|
119
|
-
() => generateAttrTree(0, configurable_options, providedVariants, {}),
|
|
120
|
-
[configurable_options, providedVariants],
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
const getVariants: GetVariants = useCallback(
|
|
124
|
-
(options: Selected = {}) => traverseAttrTree(options, lookupTree),
|
|
125
|
-
[lookupTree],
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
const getUids: GetUids = useCallback(
|
|
129
|
-
(options: Selected = {}) =>
|
|
130
|
-
(getVariants(options as unknown as Selected) ?? [])
|
|
131
|
-
.map((variant) => (variant?.attributes?.map((attr) => attr?.uid) ?? []) as string[])
|
|
132
|
-
.flat(),
|
|
133
|
-
[getVariants],
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
const context = configurableContext(sku)
|
|
137
|
-
const variants = getVariants(selection)
|
|
138
|
-
|
|
139
|
-
const value = useMemo(
|
|
140
|
-
() => ({
|
|
141
|
-
selection,
|
|
142
|
-
variants,
|
|
143
|
-
cheapest: cheapestVariant(variants),
|
|
144
|
-
select,
|
|
145
|
-
getVariants,
|
|
146
|
-
getUids,
|
|
147
|
-
options: configurable_options,
|
|
148
|
-
}),
|
|
149
|
-
[configurable_options, getUids, getVariants, selection, variants],
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
return <context.Provider value={value}>{children}</context.Provider>
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
export function useConfigurableContext(sku: string): ConfigurableContext {
|
|
156
|
-
return useContext(configurableContext(sku))
|
|
157
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { ConfigurableProductFormFragment } from './ConfigurableProductForm.gql'
|
|
2
|
-
|
|
3
|
-
type Variants = NonNullable<ConfigurableProductFormFragment['variants']>
|
|
4
|
-
|
|
5
|
-
export default function cheapestVariant(variants: Variants): Variants[0] {
|
|
6
|
-
if (!variants.length) return null
|
|
7
|
-
const cheapest = variants?.reduce((prev, curr) =>
|
|
8
|
-
(curr?.product?.price_range.minimum_price.final_price.value ?? 0) <
|
|
9
|
-
(prev?.product?.price_range.minimum_price.final_price.value ?? 0)
|
|
10
|
-
? curr
|
|
11
|
-
: prev,
|
|
12
|
-
)
|
|
13
|
-
return cheapest
|
|
14
|
-
}
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import type { FieldErrors, UseControllerProps } from '@graphcommerce/ecommerce-ui'
|
|
2
|
-
import { Controller } from '@graphcommerce/ecommerce-ui'
|
|
3
|
-
import {
|
|
4
|
-
RenderType,
|
|
5
|
-
SectionHeader,
|
|
6
|
-
ToggleButton,
|
|
7
|
-
ToggleButtonGroup,
|
|
8
|
-
extendableComponent,
|
|
9
|
-
} from '@graphcommerce/next-ui'
|
|
10
|
-
import type { BaseTextFieldProps, SxProps } from '@mui/material'
|
|
11
|
-
import { FormHelperText } from '@mui/material'
|
|
12
|
-
import React from 'react'
|
|
13
|
-
import type { Selected } from '../ConfigurableContext/ConfigurableContext'
|
|
14
|
-
import { useConfigurableContext } from '../ConfigurableContext/ConfigurableContext'
|
|
15
|
-
import { ColorSwatchData } from '../Swatches/ColorSwatchData'
|
|
16
|
-
import { ImageSwatchData } from '../Swatches/ImageSwatchData'
|
|
17
|
-
import { TextSwatchData } from '../Swatches/TextSwatchData'
|
|
18
|
-
import type { SwatchSize, SwatchTypeRenderer } from '../Swatches/types'
|
|
19
|
-
|
|
20
|
-
export type ConfigurableOptionsInputProps = {
|
|
21
|
-
sku: string
|
|
22
|
-
errors?: FieldErrors
|
|
23
|
-
size?: SwatchSize
|
|
24
|
-
sx?: SxProps
|
|
25
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
|
-
} & UseControllerProps<any> &
|
|
27
|
-
Pick<BaseTextFieldProps, 'FormHelperTextProps' | 'helperText'> & {
|
|
28
|
-
optionEndLabels?: Record<string, React.ReactNode>
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const renderer: SwatchTypeRenderer = { TextSwatchData, ImageSwatchData, ColorSwatchData }
|
|
32
|
-
|
|
33
|
-
const compName = 'ConfigurableOptionsInput'
|
|
34
|
-
const parts = ['buttonGroup', 'button', 'helperText'] as const
|
|
35
|
-
const { classes } = extendableComponent(compName, parts)
|
|
36
|
-
|
|
37
|
-
export function ConfigurableOptionsInput(props: ConfigurableOptionsInputProps) {
|
|
38
|
-
const {
|
|
39
|
-
sku,
|
|
40
|
-
FormHelperTextProps,
|
|
41
|
-
name,
|
|
42
|
-
defaultValue,
|
|
43
|
-
errors,
|
|
44
|
-
helperText,
|
|
45
|
-
optionEndLabels,
|
|
46
|
-
size = 'large',
|
|
47
|
-
sx,
|
|
48
|
-
...controlProps
|
|
49
|
-
} = props
|
|
50
|
-
|
|
51
|
-
const { options, selection, select, getVariants } = useConfigurableContext(sku)
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<>
|
|
55
|
-
{options?.map((option) => {
|
|
56
|
-
if (!option?.uid || !option.attribute_code) return null
|
|
57
|
-
|
|
58
|
-
const { attribute_code } = option
|
|
59
|
-
const error = errors?.[attribute_code]
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<Controller
|
|
63
|
-
key={option.uid}
|
|
64
|
-
defaultValue={selection[attribute_code] ?? ''}
|
|
65
|
-
name={`${name}[${attribute_code}]`}
|
|
66
|
-
{...controlProps}
|
|
67
|
-
render={({
|
|
68
|
-
field: { onChange, value, name: inputName, ref, onBlur },
|
|
69
|
-
fieldState: { error: errorHelperText },
|
|
70
|
-
}) => (
|
|
71
|
-
<>
|
|
72
|
-
<SectionHeader
|
|
73
|
-
labelLeft={option?.label}
|
|
74
|
-
labelRight={optionEndLabels?.[option?.attribute_code ?? '']}
|
|
75
|
-
/>
|
|
76
|
-
<ToggleButtonGroup
|
|
77
|
-
defaultValue={selection[attribute_code] ?? ''}
|
|
78
|
-
required
|
|
79
|
-
exclusive
|
|
80
|
-
onChange={(_, val: string | number) => {
|
|
81
|
-
onChange(val)
|
|
82
|
-
select((prev) => ({ ...prev, [attribute_code]: val }) as Selected)
|
|
83
|
-
}}
|
|
84
|
-
ref={ref}
|
|
85
|
-
onBlur={onBlur}
|
|
86
|
-
value={value}
|
|
87
|
-
className={classes.buttonGroup}
|
|
88
|
-
size={size}
|
|
89
|
-
sx={sx}
|
|
90
|
-
>
|
|
91
|
-
{option?.values?.map((val) => {
|
|
92
|
-
if (!val?.uid || !option.attribute_code) return null
|
|
93
|
-
|
|
94
|
-
// Fall back to text swatch if no swatch is given
|
|
95
|
-
const swatch_data = val.swatch_data ?? {
|
|
96
|
-
__typename: 'TextSwatchData',
|
|
97
|
-
value: val.store_label,
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const copySelection = { ...selection }
|
|
101
|
-
delete copySelection[attribute_code]
|
|
102
|
-
|
|
103
|
-
const itemVariant = getVariants(copySelection).find((variant) =>
|
|
104
|
-
variant?.attributes?.find((attribute) => attribute?.uid === val.uid),
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
return (
|
|
108
|
-
<ToggleButton
|
|
109
|
-
key={val.uid}
|
|
110
|
-
value={val.uid ?? ''}
|
|
111
|
-
name={inputName}
|
|
112
|
-
className={classes.button}
|
|
113
|
-
disabled={!itemVariant}
|
|
114
|
-
size={size}
|
|
115
|
-
>
|
|
116
|
-
<RenderType
|
|
117
|
-
renderer={renderer}
|
|
118
|
-
{...val}
|
|
119
|
-
{...swatch_data}
|
|
120
|
-
price={itemVariant?.product?.price_range.minimum_price.final_price}
|
|
121
|
-
size={size}
|
|
122
|
-
/>
|
|
123
|
-
</ToggleButton>
|
|
124
|
-
)
|
|
125
|
-
})}
|
|
126
|
-
</ToggleButtonGroup>
|
|
127
|
-
{error && (
|
|
128
|
-
<FormHelperText
|
|
129
|
-
error
|
|
130
|
-
{...FormHelperTextProps}
|
|
131
|
-
className={classes.helperText}
|
|
132
|
-
sx={{
|
|
133
|
-
position: 'absolute',
|
|
134
|
-
}}
|
|
135
|
-
>
|
|
136
|
-
{`${option.label} is ${errorHelperText?.type}`}
|
|
137
|
-
</FormHelperText>
|
|
138
|
-
)}
|
|
139
|
-
</>
|
|
140
|
-
)}
|
|
141
|
-
/>
|
|
142
|
-
)
|
|
143
|
-
})}
|
|
144
|
-
</>
|
|
145
|
-
)
|
|
146
|
-
}
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import { NumberFieldElement } from '@graphcommerce/ecommerce-ui'
|
|
2
|
-
import { ApolloCartErrorAlert, useFormGqlMutationCart } from '@graphcommerce/magento-cart'
|
|
3
|
-
import { Money } from '@graphcommerce/magento-store'
|
|
4
|
-
import {
|
|
5
|
-
Button,
|
|
6
|
-
IconSvg,
|
|
7
|
-
MessageSnackbar,
|
|
8
|
-
extendableComponent,
|
|
9
|
-
iconChevronRight,
|
|
10
|
-
} from '@graphcommerce/next-ui'
|
|
11
|
-
import { Trans } from '@lingui/macro'
|
|
12
|
-
import type { SxProps, Theme } from '@mui/material'
|
|
13
|
-
import { Alert, Box, Divider, Typography } from '@mui/material'
|
|
14
|
-
import React from 'react'
|
|
15
|
-
import { useConfigurableContext } from '../ConfigurableContext/ConfigurableContext'
|
|
16
|
-
import cheapestVariant from '../ConfigurableContext/cheapestVariant'
|
|
17
|
-
import type { ConfigurableOptionsInputProps } from '../ConfigurableOptions/ConfigurableOptions'
|
|
18
|
-
import { ConfigurableOptionsInput } from '../ConfigurableOptions/ConfigurableOptions'
|
|
19
|
-
import type { ConfigurableProductAddToCartMutationVariables } from '../graphql/ConfigurableProductAddToCart.gql'
|
|
20
|
-
import { ConfigurableProductAddToCartDocument } from '../graphql/ConfigurableProductAddToCart.gql'
|
|
21
|
-
|
|
22
|
-
export type ConfigurableProductAddToCartProps = {
|
|
23
|
-
variables: Omit<ConfigurableProductAddToCartMutationVariables, 'cartId' | 'selectedOptions'>
|
|
24
|
-
name: string
|
|
25
|
-
optionEndLabels?: Record<string, React.ReactNode>
|
|
26
|
-
children?: React.ReactNode
|
|
27
|
-
additionalButtons?: React.ReactNode
|
|
28
|
-
sx?: SxProps<Theme>
|
|
29
|
-
optionsProps?: Omit<
|
|
30
|
-
ConfigurableOptionsInputProps,
|
|
31
|
-
'name' | 'sku' | 'control' | 'rules' | 'errors' | 'optionEndLabels'
|
|
32
|
-
>
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const compName = 'ConfigurableOptionsInput'
|
|
36
|
-
const parts = ['form', 'button', 'finalPrice', 'quantity', 'divider', 'buttonWrapper'] as const
|
|
37
|
-
const { classes } = extendableComponent(compName, parts)
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* @deprecated
|
|
41
|
-
*/
|
|
42
|
-
export function ConfigurableProductAddToCart(props: ConfigurableProductAddToCartProps) {
|
|
43
|
-
const {
|
|
44
|
-
name,
|
|
45
|
-
children,
|
|
46
|
-
variables,
|
|
47
|
-
optionEndLabels,
|
|
48
|
-
optionsProps,
|
|
49
|
-
additionalButtons,
|
|
50
|
-
sx = [],
|
|
51
|
-
...buttonProps
|
|
52
|
-
} = props
|
|
53
|
-
|
|
54
|
-
const { getVariants, selection } = useConfigurableContext(variables.sku)
|
|
55
|
-
|
|
56
|
-
const form = useFormGqlMutationCart(ConfigurableProductAddToCartDocument, {
|
|
57
|
-
defaultValues: { ...variables },
|
|
58
|
-
onBeforeSubmit: ({ selectedOptions, ...vars }) => ({
|
|
59
|
-
...vars,
|
|
60
|
-
selectedOptions: Object.values(selectedOptions),
|
|
61
|
-
}),
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
const { handleSubmit, formState, required, control, error, data } = form
|
|
65
|
-
const submitHandler = handleSubmit(() => {})
|
|
66
|
-
|
|
67
|
-
return (
|
|
68
|
-
<Box
|
|
69
|
-
component='form'
|
|
70
|
-
onSubmit={submitHandler}
|
|
71
|
-
noValidate
|
|
72
|
-
className={classes.form}
|
|
73
|
-
sx={[
|
|
74
|
-
{
|
|
75
|
-
width: '100%',
|
|
76
|
-
},
|
|
77
|
-
...(Array.isArray(sx) ? sx : [sx]),
|
|
78
|
-
]}
|
|
79
|
-
>
|
|
80
|
-
<Divider className={classes.divider} sx={(theme) => ({ margin: `${theme.spacings.sm} 0` })} />
|
|
81
|
-
<ConfigurableOptionsInput
|
|
82
|
-
name='selectedOptions'
|
|
83
|
-
sku={variables.sku}
|
|
84
|
-
control={control}
|
|
85
|
-
rules={{ required: required.selectedOptions }}
|
|
86
|
-
errors={formState.errors}
|
|
87
|
-
optionEndLabels={optionEndLabels}
|
|
88
|
-
{...optionsProps}
|
|
89
|
-
/>
|
|
90
|
-
|
|
91
|
-
<NumberFieldElement
|
|
92
|
-
variant='outlined'
|
|
93
|
-
error={formState.isSubmitted && !!formState.errors.quantity}
|
|
94
|
-
required={required.quantity}
|
|
95
|
-
inputProps={{ min: 1 }}
|
|
96
|
-
name='quantity'
|
|
97
|
-
rules={{ required: required.quantity }}
|
|
98
|
-
control={control}
|
|
99
|
-
helperText={formState.isSubmitted && formState.errors.quantity?.message}
|
|
100
|
-
// disabled={loading}
|
|
101
|
-
size='small'
|
|
102
|
-
className={classes.quantity}
|
|
103
|
-
sx={(theme) => ({ marginTop: theme.spacings.sm })}
|
|
104
|
-
/>
|
|
105
|
-
<Divider className={classes.divider} sx={(theme) => ({ margin: `${theme.spacings.sm} 0` })} />
|
|
106
|
-
<Typography
|
|
107
|
-
component='div'
|
|
108
|
-
variant='h3'
|
|
109
|
-
className={classes.finalPrice}
|
|
110
|
-
sx={(theme) => ({ marginTop: theme.spacings.sm })}
|
|
111
|
-
>
|
|
112
|
-
<Money
|
|
113
|
-
{...cheapestVariant(getVariants(selection))?.product?.price_range.minimum_price
|
|
114
|
-
.final_price}
|
|
115
|
-
/>
|
|
116
|
-
</Typography>
|
|
117
|
-
{children}
|
|
118
|
-
<Box
|
|
119
|
-
sx={(theme) => ({
|
|
120
|
-
display: 'flex',
|
|
121
|
-
alignItems: 'center',
|
|
122
|
-
columnGap: theme.spacings.xs,
|
|
123
|
-
})}
|
|
124
|
-
className={classes.buttonWrapper}
|
|
125
|
-
>
|
|
126
|
-
<Button
|
|
127
|
-
type='submit'
|
|
128
|
-
loading={formState.isSubmitting}
|
|
129
|
-
color='primary'
|
|
130
|
-
variant='pill'
|
|
131
|
-
size='large'
|
|
132
|
-
className={classes.button}
|
|
133
|
-
{...buttonProps}
|
|
134
|
-
sx={(theme) => ({
|
|
135
|
-
marginTop: theme.spacings.sm,
|
|
136
|
-
marginBottom: theme.spacings.sm,
|
|
137
|
-
width: '100%',
|
|
138
|
-
})}
|
|
139
|
-
>
|
|
140
|
-
<Trans>Add to Cart</Trans>
|
|
141
|
-
</Button>
|
|
142
|
-
{additionalButtons}
|
|
143
|
-
</Box>
|
|
144
|
-
|
|
145
|
-
<ApolloCartErrorAlert error={error} />
|
|
146
|
-
|
|
147
|
-
{data?.addProductsToCart?.user_errors.map((e) => (
|
|
148
|
-
<Box key={e?.code}>
|
|
149
|
-
<Alert severity='error'>{e?.message}</Alert>
|
|
150
|
-
</Box>
|
|
151
|
-
))}
|
|
152
|
-
|
|
153
|
-
<MessageSnackbar
|
|
154
|
-
open={
|
|
155
|
-
!formState.isSubmitting &&
|
|
156
|
-
formState.isSubmitSuccessful &&
|
|
157
|
-
!error?.message &&
|
|
158
|
-
!data?.addProductsToCart?.user_errors?.length
|
|
159
|
-
}
|
|
160
|
-
variant='pill'
|
|
161
|
-
severity='success'
|
|
162
|
-
autoHide
|
|
163
|
-
action={
|
|
164
|
-
<Button
|
|
165
|
-
href='/cart'
|
|
166
|
-
id='view-shopping-cart-button'
|
|
167
|
-
size='medium'
|
|
168
|
-
variant='pill'
|
|
169
|
-
color='secondary'
|
|
170
|
-
endIcon={<IconSvg src={iconChevronRight} />}
|
|
171
|
-
>
|
|
172
|
-
<Trans>View shopping cart</Trans>
|
|
173
|
-
</Button>
|
|
174
|
-
}
|
|
175
|
-
>
|
|
176
|
-
<Trans>
|
|
177
|
-
<strong>{name}</strong> has been added to your shopping cart!
|
|
178
|
-
</Trans>
|
|
179
|
-
</MessageSnackbar>
|
|
180
|
-
</Box>
|
|
181
|
-
)
|
|
182
|
-
}
|