@graphcommerce/magento-product-configurable 9.0.0-canary.99 → 9.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.
- package/CHANGELOG.md +87 -1054
- package/ConfigurableCartItem/ConfigurableCartItem.tsx +7 -2
- package/ConfigurableCartItem/OptionsList.tsx +5 -4
- package/SwatchList.tsx +3 -3
- package/Swatches/ColorSwatchData.tsx +7 -6
- package/Swatches/ImageSwatchData.tsx +8 -6
- package/Swatches/TextSwatchData.tsx +6 -5
- package/Swatches/types.ts +4 -4
- package/components/ConfigurableCartItemOptions/ConfigurableCartItemOptions.tsx +4 -3
- package/components/ConfigurableOptionValue/ConfigurableOptionValue.tsx +2 -2
- package/components/ConfigurableOptionValueColor/ConfigurableOptionValueColor.tsx +2 -2
- package/components/ConfigurableOptionValueImage/ConfigurableOptionValueImage.tsx +2 -2
- package/components/ConfigurableOptionValueText/ConfigurableOptionValueText.tsx +2 -2
- package/components/ConfigurableProductOptions/ConfigurableProductOption.tsx +16 -13
- package/components/ConfigurableProductOptions/ConfigurableProductOptions.tsx +11 -5
- package/components/ProductListItemConfigurable/ProductListItemConfigurable.tsx +3 -6
- package/hooks/useConfigurableOptionsSelection.ts +2 -1
- package/index.ts +0 -2
- package/package.json +19 -19
- package/plugins/ConfigurableProductPage/ConfigurableProductPageDescription.tsx +2 -2
- package/plugins/ConfigurableProductPage/ConfigurableProductPageGallery.tsx +1 -0
- package/plugins/ConfigurableProductPage/ConfigurableProductPageMeta.tsx +2 -5
- package/plugins/ConfigurableProductPage/ConfigurableProductPagePrice.tsx +1 -1
- package/test/addConfigurableProductToCart.ts +2 -1
- package/utils/defaultConfigurableOptionsSelection.ts +3 -3
- package/ConfigurableContext/ConfigurableContext.tsx +0 -165
- package/ConfigurableContext/cheapestVariant.ts +0 -14
- package/ConfigurableOptions/ConfigurableOptions.tsx +0 -143
- package/ConfigurableProductAddToCart/ConfigurableProductAddToCart.tsx +0 -185
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import { Controller, FieldErrors, UseControllerProps } from '@graphcommerce/ecommerce-ui'
|
|
2
|
-
import {
|
|
3
|
-
RenderType,
|
|
4
|
-
SectionHeader,
|
|
5
|
-
ToggleButton,
|
|
6
|
-
ToggleButtonGroup,
|
|
7
|
-
extendableComponent,
|
|
8
|
-
} from '@graphcommerce/next-ui'
|
|
9
|
-
import { BaseTextFieldProps, FormHelperText, SxProps } from '@mui/material'
|
|
10
|
-
import React from 'react'
|
|
11
|
-
import { Selected, useConfigurableContext } from '../ConfigurableContext/ConfigurableContext'
|
|
12
|
-
import { ColorSwatchData } from '../Swatches/ColorSwatchData'
|
|
13
|
-
import { ImageSwatchData } from '../Swatches/ImageSwatchData'
|
|
14
|
-
import { TextSwatchData } from '../Swatches/TextSwatchData'
|
|
15
|
-
import { SwatchTypeRenderer, SwatchSize } from '../Swatches/types'
|
|
16
|
-
|
|
17
|
-
export type ConfigurableOptionsInputProps = {
|
|
18
|
-
sku: string
|
|
19
|
-
errors?: FieldErrors
|
|
20
|
-
size?: SwatchSize
|
|
21
|
-
sx?: SxProps
|
|
22
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
23
|
-
} & UseControllerProps<any> &
|
|
24
|
-
Pick<BaseTextFieldProps, 'FormHelperTextProps' | 'helperText'> & {
|
|
25
|
-
optionEndLabels?: Record<string, React.ReactNode>
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const renderer: SwatchTypeRenderer = { TextSwatchData, ImageSwatchData, ColorSwatchData }
|
|
29
|
-
|
|
30
|
-
const compName = 'ConfigurableOptionsInput' as const
|
|
31
|
-
const parts = ['buttonGroup', 'button', 'helperText'] as const
|
|
32
|
-
const { classes } = extendableComponent(compName, parts)
|
|
33
|
-
|
|
34
|
-
export function ConfigurableOptionsInput(props: ConfigurableOptionsInputProps) {
|
|
35
|
-
const {
|
|
36
|
-
sku,
|
|
37
|
-
FormHelperTextProps,
|
|
38
|
-
name,
|
|
39
|
-
defaultValue,
|
|
40
|
-
errors,
|
|
41
|
-
helperText,
|
|
42
|
-
optionEndLabels,
|
|
43
|
-
size = 'large',
|
|
44
|
-
sx,
|
|
45
|
-
...controlProps
|
|
46
|
-
} = props
|
|
47
|
-
|
|
48
|
-
const { options, selection, select, getVariants } = useConfigurableContext(sku)
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
<>
|
|
52
|
-
{options?.map((option) => {
|
|
53
|
-
if (!option?.uid || !option.attribute_code) return null
|
|
54
|
-
|
|
55
|
-
const { attribute_code } = option
|
|
56
|
-
const error = errors?.[attribute_code]
|
|
57
|
-
|
|
58
|
-
return (
|
|
59
|
-
<Controller
|
|
60
|
-
key={option.uid}
|
|
61
|
-
defaultValue={selection[attribute_code] ?? ''}
|
|
62
|
-
name={`${name}[${attribute_code}]`}
|
|
63
|
-
{...controlProps}
|
|
64
|
-
render={({
|
|
65
|
-
field: { onChange, value, name: inputName, ref, onBlur },
|
|
66
|
-
fieldState: { error: errorHelperText },
|
|
67
|
-
}) => (
|
|
68
|
-
<>
|
|
69
|
-
<SectionHeader
|
|
70
|
-
labelLeft={option?.label}
|
|
71
|
-
labelRight={optionEndLabels?.[option?.attribute_code ?? '']}
|
|
72
|
-
/>
|
|
73
|
-
<ToggleButtonGroup
|
|
74
|
-
defaultValue={selection[attribute_code] ?? ''}
|
|
75
|
-
required
|
|
76
|
-
exclusive
|
|
77
|
-
onChange={(_, val: string | number) => {
|
|
78
|
-
onChange(val)
|
|
79
|
-
select((prev) => ({ ...prev, [attribute_code]: val } as Selected))
|
|
80
|
-
}}
|
|
81
|
-
ref={ref}
|
|
82
|
-
onBlur={onBlur}
|
|
83
|
-
value={value}
|
|
84
|
-
className={classes.buttonGroup}
|
|
85
|
-
size={size}
|
|
86
|
-
sx={sx}
|
|
87
|
-
>
|
|
88
|
-
{option?.values?.map((val) => {
|
|
89
|
-
if (!val?.uid || !option.attribute_code) return null
|
|
90
|
-
|
|
91
|
-
// Fall back to text swatch if no swatch is given
|
|
92
|
-
const swatch_data = val.swatch_data ?? {
|
|
93
|
-
__typename: 'TextSwatchData',
|
|
94
|
-
value: val.store_label,
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const copySelection = { ...selection }
|
|
98
|
-
delete copySelection[attribute_code]
|
|
99
|
-
|
|
100
|
-
const itemVariant = getVariants(copySelection).find((variant) =>
|
|
101
|
-
variant?.attributes?.find((attribute) => attribute?.uid === val.uid),
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
return (
|
|
105
|
-
<ToggleButton
|
|
106
|
-
key={val.uid}
|
|
107
|
-
value={val.uid ?? ''}
|
|
108
|
-
name={inputName}
|
|
109
|
-
className={classes.button}
|
|
110
|
-
disabled={!itemVariant}
|
|
111
|
-
size={size}
|
|
112
|
-
>
|
|
113
|
-
<RenderType
|
|
114
|
-
renderer={renderer}
|
|
115
|
-
{...val}
|
|
116
|
-
{...swatch_data}
|
|
117
|
-
price={itemVariant?.product?.price_range.minimum_price.final_price}
|
|
118
|
-
size={size}
|
|
119
|
-
/>
|
|
120
|
-
</ToggleButton>
|
|
121
|
-
)
|
|
122
|
-
})}
|
|
123
|
-
</ToggleButtonGroup>
|
|
124
|
-
{error && (
|
|
125
|
-
<FormHelperText
|
|
126
|
-
error
|
|
127
|
-
{...FormHelperTextProps}
|
|
128
|
-
className={classes.helperText}
|
|
129
|
-
sx={{
|
|
130
|
-
position: 'absolute',
|
|
131
|
-
}}
|
|
132
|
-
>
|
|
133
|
-
{`${option.label} is ${errorHelperText?.type}`}
|
|
134
|
-
</FormHelperText>
|
|
135
|
-
)}
|
|
136
|
-
</>
|
|
137
|
-
)}
|
|
138
|
-
/>
|
|
139
|
-
)
|
|
140
|
-
})}
|
|
141
|
-
</>
|
|
142
|
-
)
|
|
143
|
-
}
|
|
@@ -1,185 +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
|
-
extendableComponent,
|
|
7
|
-
iconChevronRight,
|
|
8
|
-
MessageSnackbar,
|
|
9
|
-
IconSvg,
|
|
10
|
-
} from '@graphcommerce/next-ui'
|
|
11
|
-
import { Trans } from '@lingui/macro'
|
|
12
|
-
import { Divider, Typography, Alert, Box, SxProps, Theme } from '@mui/material'
|
|
13
|
-
import React from 'react'
|
|
14
|
-
import { useConfigurableContext } from '../ConfigurableContext/ConfigurableContext'
|
|
15
|
-
import cheapestVariant from '../ConfigurableContext/cheapestVariant'
|
|
16
|
-
import {
|
|
17
|
-
ConfigurableOptionsInput,
|
|
18
|
-
ConfigurableOptionsInputProps,
|
|
19
|
-
} from '../ConfigurableOptions/ConfigurableOptions'
|
|
20
|
-
import {
|
|
21
|
-
ConfigurableProductAddToCartDocument,
|
|
22
|
-
ConfigurableProductAddToCartMutationVariables,
|
|
23
|
-
} from '../graphql/ConfigurableProductAddToCart.gql'
|
|
24
|
-
|
|
25
|
-
type ConfigurableProductAddToCartProps = {
|
|
26
|
-
variables: Omit<ConfigurableProductAddToCartMutationVariables, 'cartId' | 'selectedOptions'>
|
|
27
|
-
name: string
|
|
28
|
-
optionEndLabels?: Record<string, React.ReactNode>
|
|
29
|
-
children?: React.ReactNode
|
|
30
|
-
additionalButtons?: React.ReactNode
|
|
31
|
-
sx?: SxProps<Theme>
|
|
32
|
-
optionsProps?: Omit<
|
|
33
|
-
ConfigurableOptionsInputProps,
|
|
34
|
-
'name' | 'sku' | 'control' | 'rules' | 'errors' | 'optionEndLabels'
|
|
35
|
-
>
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const compName = 'ConfigurableOptionsInput' as const
|
|
39
|
-
const parts = ['form', 'button', 'finalPrice', 'quantity', 'divider', 'buttonWrapper'] as const
|
|
40
|
-
const { classes } = extendableComponent(compName, parts)
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* @deprecated
|
|
44
|
-
*/
|
|
45
|
-
export function ConfigurableProductAddToCart(props: ConfigurableProductAddToCartProps) {
|
|
46
|
-
const {
|
|
47
|
-
name,
|
|
48
|
-
children,
|
|
49
|
-
variables,
|
|
50
|
-
optionEndLabels,
|
|
51
|
-
optionsProps,
|
|
52
|
-
additionalButtons,
|
|
53
|
-
sx = [],
|
|
54
|
-
...buttonProps
|
|
55
|
-
} = props
|
|
56
|
-
|
|
57
|
-
const { getVariants, selection } = useConfigurableContext(variables.sku)
|
|
58
|
-
|
|
59
|
-
const form = useFormGqlMutationCart(ConfigurableProductAddToCartDocument, {
|
|
60
|
-
defaultValues: { ...variables },
|
|
61
|
-
onBeforeSubmit: ({ selectedOptions, ...vars }) => ({
|
|
62
|
-
...vars,
|
|
63
|
-
selectedOptions: Object.values(selectedOptions),
|
|
64
|
-
}),
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
const { handleSubmit, formState, required, control, error, data } = form
|
|
68
|
-
const submitHandler = handleSubmit(() => {})
|
|
69
|
-
|
|
70
|
-
return (
|
|
71
|
-
<Box
|
|
72
|
-
component='form'
|
|
73
|
-
onSubmit={submitHandler}
|
|
74
|
-
noValidate
|
|
75
|
-
className={classes.form}
|
|
76
|
-
sx={[
|
|
77
|
-
{
|
|
78
|
-
width: '100%',
|
|
79
|
-
},
|
|
80
|
-
...(Array.isArray(sx) ? sx : [sx]),
|
|
81
|
-
]}
|
|
82
|
-
>
|
|
83
|
-
<Divider className={classes.divider} sx={(theme) => ({ margin: `${theme.spacings.sm} 0` })} />
|
|
84
|
-
<ConfigurableOptionsInput
|
|
85
|
-
name='selectedOptions'
|
|
86
|
-
sku={variables.sku}
|
|
87
|
-
control={control}
|
|
88
|
-
rules={{ required: required.selectedOptions }}
|
|
89
|
-
errors={formState.errors}
|
|
90
|
-
optionEndLabels={optionEndLabels}
|
|
91
|
-
{...optionsProps}
|
|
92
|
-
/>
|
|
93
|
-
|
|
94
|
-
<NumberFieldElement
|
|
95
|
-
variant='outlined'
|
|
96
|
-
error={formState.isSubmitted && !!formState.errors.quantity}
|
|
97
|
-
required={required.quantity}
|
|
98
|
-
inputProps={{ min: 1 }}
|
|
99
|
-
name='quantity'
|
|
100
|
-
rules={{ required: required.quantity }}
|
|
101
|
-
control={control}
|
|
102
|
-
helperText={formState.isSubmitted && formState.errors.quantity?.message}
|
|
103
|
-
// disabled={loading}
|
|
104
|
-
size='small'
|
|
105
|
-
className={classes.quantity}
|
|
106
|
-
sx={(theme) => ({ marginTop: theme.spacings.sm })}
|
|
107
|
-
/>
|
|
108
|
-
<Divider className={classes.divider} sx={(theme) => ({ margin: `${theme.spacings.sm} 0` })} />
|
|
109
|
-
<Typography
|
|
110
|
-
component='div'
|
|
111
|
-
variant='h3'
|
|
112
|
-
className={classes.finalPrice}
|
|
113
|
-
sx={(theme) => ({ marginTop: theme.spacings.sm })}
|
|
114
|
-
>
|
|
115
|
-
<Money
|
|
116
|
-
{...cheapestVariant(getVariants(selection))?.product?.price_range.minimum_price
|
|
117
|
-
.final_price}
|
|
118
|
-
/>
|
|
119
|
-
</Typography>
|
|
120
|
-
{children}
|
|
121
|
-
<Box
|
|
122
|
-
sx={(theme) => ({
|
|
123
|
-
display: 'flex',
|
|
124
|
-
alignItems: 'center',
|
|
125
|
-
columnGap: theme.spacings.xs,
|
|
126
|
-
})}
|
|
127
|
-
className={classes.buttonWrapper}
|
|
128
|
-
>
|
|
129
|
-
<Button
|
|
130
|
-
type='submit'
|
|
131
|
-
loading={formState.isSubmitting}
|
|
132
|
-
color='primary'
|
|
133
|
-
variant='pill'
|
|
134
|
-
size='large'
|
|
135
|
-
className={classes.button}
|
|
136
|
-
{...buttonProps}
|
|
137
|
-
sx={(theme) => ({
|
|
138
|
-
marginTop: theme.spacings.sm,
|
|
139
|
-
marginBottom: theme.spacings.sm,
|
|
140
|
-
width: '100%',
|
|
141
|
-
})}
|
|
142
|
-
>
|
|
143
|
-
<Trans>Add to Cart</Trans>
|
|
144
|
-
</Button>
|
|
145
|
-
{additionalButtons}
|
|
146
|
-
</Box>
|
|
147
|
-
|
|
148
|
-
<ApolloCartErrorAlert error={error} />
|
|
149
|
-
|
|
150
|
-
{data?.addProductsToCart?.user_errors.map((e) => (
|
|
151
|
-
<Box key={e?.code}>
|
|
152
|
-
<Alert severity='error'>{e?.message}</Alert>
|
|
153
|
-
</Box>
|
|
154
|
-
))}
|
|
155
|
-
|
|
156
|
-
<MessageSnackbar
|
|
157
|
-
open={
|
|
158
|
-
!formState.isSubmitting &&
|
|
159
|
-
formState.isSubmitSuccessful &&
|
|
160
|
-
!error?.message &&
|
|
161
|
-
!data?.addProductsToCart?.user_errors?.length
|
|
162
|
-
}
|
|
163
|
-
variant='pill'
|
|
164
|
-
severity='success'
|
|
165
|
-
autoHide
|
|
166
|
-
action={
|
|
167
|
-
<Button
|
|
168
|
-
href='/cart'
|
|
169
|
-
id='view-shopping-cart-button'
|
|
170
|
-
size='medium'
|
|
171
|
-
variant='pill'
|
|
172
|
-
color='secondary'
|
|
173
|
-
endIcon={<IconSvg src={iconChevronRight} />}
|
|
174
|
-
>
|
|
175
|
-
<Trans>View shopping cart</Trans>
|
|
176
|
-
</Button>
|
|
177
|
-
}
|
|
178
|
-
>
|
|
179
|
-
<Trans>
|
|
180
|
-
<strong>{name}</strong> has been added to your shopping cart!
|
|
181
|
-
</Trans>
|
|
182
|
-
</MessageSnackbar>
|
|
183
|
-
</Box>
|
|
184
|
-
)
|
|
185
|
-
}
|