@graphcommerce/ecommerce-ui 5.1.0-canary.0 → 5.1.0-canary.10

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 CHANGED
@@ -1,5 +1,40 @@
1
1
  # @graphcommerce/ecommerce-ui
2
2
 
3
+ ## 5.1.0-canary.10
4
+
5
+ ## 5.1.0-canary.9
6
+
7
+ ## 5.1.0-canary.8
8
+
9
+ ## 5.1.0-canary.7
10
+
11
+ ## 5.1.0-canary.6
12
+
13
+ ## 5.1.0-canary.5
14
+
15
+ ### Patch Changes
16
+
17
+ - [`b1444b933`](https://github.com/graphcommerce-org/graphcommerce/commit/b1444b9336107d3ac111563f9b62a884f1b26a8d) - Bring password reset page more in line with standard forms, add missing translations. ([@paales](https://github.com/paales))
18
+
19
+ ## 5.1.0-canary.4
20
+
21
+ ## 5.1.0-canary.3
22
+
23
+ ### Patch Changes
24
+
25
+ - [#1752](https://github.com/graphcommerce-org/graphcommerce/pull/1752) [`2a6a4d9ec`](https://github.com/graphcommerce-org/graphcommerce/commit/2a6a4d9ecfa1b58a66ba9b9d00016d6feda9aa95) - Updated dependencies to latest versions, except for nextjs; Solve tons of peer dependency issues.
26
+
27
+ - Updated the @mui/material package
28
+ - Removed dependencies on react-hook-form-mui and @playwright/test
29
+ - Upgraded dependencies including type-fest and graphql-mesh
30
+ - Solved peer dependency issues ([@paales](https://github.com/paales))
31
+
32
+ - [#1752](https://github.com/graphcommerce-org/graphcommerce/pull/1752) [`56d950ba3`](https://github.com/graphcommerce-org/graphcommerce/commit/56d950ba3d31828467b34f7e163586b628567a43) - Removed dependency on react-hook-form-mui ([@paales](https://github.com/paales))
33
+
34
+ ## 5.1.0-canary.2
35
+
36
+ ## 5.1.0-canary.1
37
+
3
38
  ## 5.1.0-canary.0
4
39
 
5
40
  ## 5.0.0
@@ -0,0 +1,165 @@
1
+ import {
2
+ Control,
3
+ Controller,
4
+ ControllerProps,
5
+ Path,
6
+ FieldValues,
7
+ } from '@graphcommerce/react-hook-form'
8
+ import {
9
+ Autocomplete,
10
+ AutocompleteProps,
11
+ Checkbox,
12
+ TextField,
13
+ TextFieldProps,
14
+ CircularProgress,
15
+ } from '@mui/material'
16
+
17
+ export type AutocompleteElementProps<
18
+ F extends FieldValues,
19
+ T,
20
+ M extends boolean | undefined,
21
+ D extends boolean | undefined,
22
+ > = {
23
+ name: Path<F>
24
+ control?: Control<F>
25
+ options: T[]
26
+ loading?: boolean
27
+ multiple?: M
28
+ matchId?: boolean
29
+ rules?: ControllerProps['rules']
30
+ required?: boolean
31
+ label?: TextFieldProps['label']
32
+ showCheckbox?: boolean
33
+ autocompleteProps?: Omit<
34
+ AutocompleteProps<T, M, D, any>,
35
+ 'name' | 'options' | 'loading' | 'renderInput'
36
+ >
37
+ textFieldProps?: Omit<TextFieldProps, 'name' | 'required' | 'label'>
38
+ }
39
+
40
+ type AutoDefault = {
41
+ id: string | number // must keep id in case of keepObject
42
+ label: string
43
+ }
44
+
45
+ export function AutocompleteElement<TFieldValues extends FieldValues>({
46
+ textFieldProps,
47
+ autocompleteProps,
48
+ name,
49
+ control,
50
+ options,
51
+ loading,
52
+ showCheckbox,
53
+ rules,
54
+ required,
55
+ multiple,
56
+ matchId,
57
+ label,
58
+ }: AutocompleteElementProps<
59
+ TFieldValues,
60
+ AutoDefault | string | any,
61
+ boolean | undefined,
62
+ boolean | undefined
63
+ >) {
64
+ const validationRules: ControllerProps['rules'] = {
65
+ ...rules,
66
+ ...(required && {
67
+ required: rules?.required || 'This field is required',
68
+ }),
69
+ }
70
+ return (
71
+ <Controller
72
+ name={name}
73
+ control={control}
74
+ rules={validationRules}
75
+ render={({ field: { onChange, onBlur, value, ...fieldRest }, fieldState: { error } }) => {
76
+ const values = Array.isArray(value) ? (value as typeof value[]) : [value]
77
+ let currentValue = multiple ? value || [] : value || null
78
+ if (matchId) {
79
+ currentValue = multiple
80
+ ? values.map((i: any) => options.find((j) => (j.id || j) === i))
81
+ : options.find((i) => (i.id || i) === value) || null
82
+ }
83
+ return (
84
+ <Autocomplete
85
+ {...autocompleteProps}
86
+ value={currentValue}
87
+ loading={loading}
88
+ multiple={multiple}
89
+ options={options}
90
+ disableCloseOnSelect={
91
+ typeof autocompleteProps?.disableCloseOnSelect === 'boolean'
92
+ ? autocompleteProps.disableCloseOnSelect
93
+ : !!multiple
94
+ }
95
+ isOptionEqualToValue={
96
+ autocompleteProps?.isOptionEqualToValue
97
+ ? autocompleteProps.isOptionEqualToValue
98
+ : (option, value) => (value ? option.id === (value?.id || value) : false)
99
+ }
100
+ getOptionLabel={
101
+ autocompleteProps?.getOptionLabel
102
+ ? autocompleteProps.getOptionLabel
103
+ : (option) => `${option?.label || option}`
104
+ }
105
+ onChange={(event, value, reason, details) => {
106
+ let changedVal = value
107
+ if (matchId) {
108
+ changedVal = Array.isArray(value)
109
+ ? value.map((i: any) => i?.id || i)
110
+ : value?.id || value
111
+ }
112
+ onChange(changedVal)
113
+ if (autocompleteProps?.onChange) {
114
+ autocompleteProps.onChange(event, value, reason, details)
115
+ }
116
+ }}
117
+ renderOption={
118
+ autocompleteProps?.renderOption ??
119
+ (showCheckbox
120
+ ? (props, option, { selected }) => (
121
+ <li {...props}>
122
+ <Checkbox sx={{ marginRight: 1 }} checked={selected} />
123
+ {autocompleteProps?.getOptionLabel?.(option) || option.label || option}
124
+ </li>
125
+ )
126
+ : undefined)
127
+ }
128
+ onBlur={(event) => {
129
+ onBlur()
130
+ if (typeof autocompleteProps?.onBlur === 'function') {
131
+ autocompleteProps.onBlur(event)
132
+ }
133
+ }}
134
+ renderInput={(params) => (
135
+ <TextField
136
+ name={name}
137
+ required={rules?.required ? true : required}
138
+ label={label}
139
+ {...textFieldProps}
140
+ {...params}
141
+ error={!!error}
142
+ InputProps={{
143
+ ...params.InputProps,
144
+ endAdornment: (
145
+ <>
146
+ {loading ? <CircularProgress color='inherit' size={20} /> : null}
147
+ {params.InputProps.endAdornment}
148
+ </>
149
+ ),
150
+ ...textFieldProps?.InputProps,
151
+ }}
152
+ inputProps={{
153
+ ...params.inputProps,
154
+ ...textFieldProps?.inputProps,
155
+ }}
156
+ helperText={error ? error.message : textFieldProps?.helperText}
157
+ />
158
+ )}
159
+ {...fieldRest}
160
+ />
161
+ )
162
+ }}
163
+ />
164
+ )
165
+ }
@@ -0,0 +1,126 @@
1
+ import {
2
+ Control,
3
+ FieldError,
4
+ Path,
5
+ useController,
6
+ FieldValues,
7
+ } from '@graphcommerce/react-hook-form'
8
+ import {
9
+ Checkbox,
10
+ CheckboxProps,
11
+ FormControl,
12
+ FormControlLabel,
13
+ FormGroup,
14
+ FormHelperText,
15
+ FormLabel,
16
+ useTheme,
17
+ } from '@mui/material'
18
+
19
+ export type CheckboxButtonGroupProps<T extends FieldValues> = {
20
+ options: { id: string | number; label: string }[] | any[]
21
+ helperText?: string
22
+ name: Path<T>
23
+ required?: boolean
24
+ parseError?: (error: FieldError) => string
25
+ label?: string
26
+ labelKey?: string
27
+ valueKey?: string
28
+ onChange?: Function
29
+ returnObject?: boolean
30
+ disabled?: boolean
31
+ row?: boolean
32
+ control?: Control<T>
33
+ checkboxColor?: CheckboxProps['color']
34
+ }
35
+
36
+ export function CheckboxButtonGroup<TFieldValues extends FieldValues>({
37
+ helperText,
38
+ options,
39
+ label,
40
+ name,
41
+ parseError,
42
+ required,
43
+ labelKey = 'label',
44
+ valueKey = 'id',
45
+ returnObject,
46
+ disabled,
47
+ row,
48
+ control,
49
+ checkboxColor,
50
+ ...rest
51
+ }: CheckboxButtonGroupProps<TFieldValues>): JSX.Element {
52
+ const theme = useTheme()
53
+ const {
54
+ field: { value = [], onChange },
55
+ fieldState: { invalid, error },
56
+ } = useController({
57
+ name,
58
+ rules: required ? { required: 'This field is required' } : undefined,
59
+ control,
60
+ })
61
+
62
+ helperText = error
63
+ ? typeof parseError === 'function'
64
+ ? parseError(error)
65
+ : error.message
66
+ : helperText
67
+
68
+ const handleChange = (index: number | string) => {
69
+ const newArray: (string | number)[] | any[] = [...value]
70
+ const exists =
71
+ value.findIndex((i: any) => (returnObject ? i[valueKey] === index : i === index)) === -1
72
+ if (exists) {
73
+ newArray.push(returnObject ? options.find((i) => i[valueKey] === index) : index)
74
+ } else {
75
+ newArray.splice(
76
+ value.findIndex((i: any) => (returnObject ? i[valueKey] === index : i === index)),
77
+ 1,
78
+ )
79
+ }
80
+ // setValue(name, newArray, { shouldValidate: true })
81
+ onChange(newArray)
82
+ if (typeof rest.onChange === 'function') {
83
+ rest.onChange(newArray)
84
+ }
85
+ }
86
+
87
+ return (
88
+ <FormControl error={invalid} required={required}>
89
+ {label && <FormLabel error={invalid}>{label}</FormLabel>}
90
+ <FormGroup row={row}>
91
+ {options.map((option: any) => {
92
+ const optionKey = option[valueKey]
93
+ if (!optionKey) {
94
+ console.error(
95
+ `CheckboxButtonGroup: valueKey ${valueKey} does not exist on option`,
96
+ option,
97
+ )
98
+ }
99
+ const isChecked =
100
+ value.findIndex((item: any) =>
101
+ returnObject ? item[valueKey] === optionKey : item === optionKey,
102
+ ) !== -1
103
+ return (
104
+ <FormControlLabel
105
+ control={
106
+ <Checkbox
107
+ sx={{
108
+ color: invalid ? theme.palette.error.main : undefined,
109
+ }}
110
+ color={checkboxColor || 'primary'}
111
+ value={optionKey}
112
+ checked={isChecked}
113
+ disabled={disabled}
114
+ onChange={() => handleChange(optionKey)}
115
+ />
116
+ }
117
+ label={option[labelKey]}
118
+ key={optionKey}
119
+ />
120
+ )
121
+ })}
122
+ </FormGroup>
123
+ {helperText && <FormHelperText>{helperText}</FormHelperText>}
124
+ </FormControl>
125
+ )
126
+ }
@@ -0,0 +1,83 @@
1
+ import {
2
+ Control,
3
+ Controller,
4
+ ControllerProps,
5
+ FieldError,
6
+ Path,
7
+ FieldValues,
8
+ } from '@graphcommerce/react-hook-form'
9
+ import {
10
+ Checkbox,
11
+ CheckboxProps,
12
+ FormControl,
13
+ FormControlLabel,
14
+ FormControlLabelProps,
15
+ FormGroup,
16
+ FormHelperText,
17
+ } from '@mui/material'
18
+
19
+ export type CheckboxElementProps<T extends FieldValues> = Omit<CheckboxProps, 'name'> & {
20
+ validation?: ControllerProps['rules']
21
+ name: Path<T>
22
+ parseError?: (error: FieldError) => string
23
+ label?: FormControlLabelProps['label']
24
+ helperText?: string
25
+ control?: Control<T>
26
+ }
27
+
28
+ export function CheckboxElement<TFieldValues extends FieldValues>({
29
+ name,
30
+ validation = {},
31
+ required,
32
+ parseError,
33
+ label,
34
+ control,
35
+ helperText,
36
+ ...rest
37
+ }: CheckboxElementProps<TFieldValues>): JSX.Element {
38
+ if (required && !validation.required) {
39
+ validation.required = 'This field is required'
40
+ }
41
+
42
+ return (
43
+ <Controller
44
+ name={name}
45
+ rules={validation}
46
+ control={control}
47
+ render={({ field: { value, onChange }, fieldState: { invalid, error } }) => {
48
+ const parsedHelperText = error
49
+ ? typeof parseError === 'function'
50
+ ? parseError(error)
51
+ : error.message
52
+ : helperText
53
+ return (
54
+ <FormControl required={required} error={invalid}>
55
+ <FormGroup row>
56
+ <FormControlLabel
57
+ label={label || ''}
58
+ control={
59
+ <Checkbox
60
+ {...rest}
61
+ color={rest.color || 'primary'}
62
+ sx={{
63
+ ...rest.sx,
64
+ color: invalid ? 'error.main' : undefined,
65
+ }}
66
+ value={value}
67
+ checked={!!value}
68
+ onChange={() => {
69
+ onChange(!value)
70
+ }}
71
+ />
72
+ }
73
+ />
74
+ </FormGroup>
75
+ {parsedHelperText && (
76
+ <FormHelperText error={invalid}>{parsedHelperText}</FormHelperText>
77
+ )}
78
+ </FormControl>
79
+ )
80
+ }}
81
+ />
82
+ )
83
+ }
@@ -0,0 +1,171 @@
1
+ /* eslint-disable no-nested-ternary */
2
+ import { IconSvg, iconClose } from '@graphcommerce/next-ui'
3
+ import { Control, Controller, FieldError, Path, FieldValues } from '@graphcommerce/react-hook-form'
4
+ import {
5
+ Checkbox,
6
+ Chip,
7
+ FormControl,
8
+ FormControlProps,
9
+ FormHelperText,
10
+ InputLabel,
11
+ ListItemText,
12
+ MenuItem,
13
+ Select,
14
+ SelectProps,
15
+ } from '@mui/material'
16
+
17
+ export type MultiSelectElementProps<T extends FieldValues> = Omit<SelectProps, 'value'> & {
18
+ options: { id: string | number; label: string }[] | any[]
19
+ label?: string
20
+ itemKey?: string
21
+ itemValue?: string
22
+ itemLabel?: string
23
+ required?: boolean
24
+ validation?: any
25
+ name: Path<T>
26
+ parseError?: (error: FieldError) => string
27
+ minWidth?: number
28
+ menuMaxHeight?: number
29
+ menuMaxWidth?: number
30
+ helperText?: string
31
+ showChips?: boolean
32
+ control?: Control<T>
33
+ showCheckbox?: boolean
34
+ formControlProps?: Omit<FormControlProps, 'fullWidth' | 'variant'>
35
+ }
36
+
37
+ const ITEM_HEIGHT = 48
38
+ const ITEM_PADDING_TOP = 8
39
+
40
+ export function MultiSelectElement<TFieldValues extends FieldValues>(
41
+ props: MultiSelectElementProps<TFieldValues>,
42
+ ): JSX.Element {
43
+ let {
44
+ options,
45
+ label = '',
46
+ itemKey = 'id',
47
+ itemValue = '',
48
+ itemLabel = 'label',
49
+ required = false,
50
+ validation = {},
51
+ parseError,
52
+ name,
53
+ menuMaxHeight = ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
54
+ menuMaxWidth = 250,
55
+ minWidth = 120,
56
+ helperText,
57
+ showChips,
58
+ control,
59
+ showCheckbox,
60
+ formControlProps,
61
+ ...rest
62
+ } = props
63
+ if (required && !validation.required) {
64
+ validation.required = 'This field is required'
65
+ }
66
+
67
+ return (
68
+ <Controller
69
+ name={name}
70
+ rules={validation}
71
+ control={control}
72
+ render={({ field: { value, onChange, onBlur }, fieldState: { invalid, error } }) => {
73
+ helperText = error
74
+ ? typeof parseError === 'function'
75
+ ? parseError(error)
76
+ : error.message
77
+ : helperText
78
+ return (
79
+ <FormControl
80
+ {...formControlProps}
81
+ style={{
82
+ ...formControlProps?.style,
83
+ minWidth,
84
+ }}
85
+ variant={rest.variant}
86
+ fullWidth={rest.fullWidth}
87
+ error={invalid}
88
+ size={rest.size}
89
+ >
90
+ {label && (
91
+ <InputLabel
92
+ size={rest.size === 'small' ? 'small' : undefined}
93
+ error={invalid}
94
+ htmlFor={rest.id || `select-multi-select-${name}`}
95
+ required={required}
96
+ >
97
+ {label}
98
+ </InputLabel>
99
+ )}
100
+ <Select
101
+ {...rest}
102
+ id={rest.id || `select-multi-select-${name}`}
103
+ multiple
104
+ label={label || undefined}
105
+ error={invalid}
106
+ value={value || []}
107
+ required={required}
108
+ onChange={onChange}
109
+ onBlur={onBlur}
110
+ MenuProps={{
111
+ ...rest.MenuProps,
112
+ PaperProps: {
113
+ ...(rest.MenuProps?.PaperProps ?? {
114
+ style: {
115
+ maxHeight: menuMaxHeight,
116
+ width: menuMaxWidth,
117
+ ...rest.MenuProps?.PaperProps?.style,
118
+ },
119
+ }),
120
+ },
121
+ }}
122
+ renderValue={
123
+ typeof rest.renderValue === 'function'
124
+ ? rest.renderValue
125
+ : showChips
126
+ ? (selected) => (
127
+ <div style={{ display: 'flex', flexWrap: 'wrap' }}>
128
+ {((selected as any[]) || []).map((selectedValue) => (
129
+ <Chip
130
+ key={selectedValue}
131
+ label={selectedValue}
132
+ style={{ display: 'flex', flexWrap: 'wrap' }}
133
+ onDelete={() => {
134
+ onChange(value.filter((i: any) => i !== selectedValue))
135
+ // setValue(name, formValue.filter((i: any) => i !== value), { shouldValidate: true })
136
+ }}
137
+ deleteIcon={<IconSvg src={iconClose} />}
138
+ />
139
+ ))}
140
+ </div>
141
+ )
142
+ : (selected) => (Array.isArray(selected) ? selected.join(', ') : '')
143
+ }
144
+ >
145
+ {options.map((item) => {
146
+ const val: string | number = item[itemValue || itemKey] || item
147
+ const isChecked = Array.isArray(value) ? value.includes(val) : false
148
+ return (
149
+ <MenuItem
150
+ key={val}
151
+ value={val}
152
+ sx={{
153
+ fontWeight: (theme) =>
154
+ isChecked
155
+ ? theme.typography.fontWeightBold
156
+ : theme.typography.fontWeightRegular,
157
+ }}
158
+ >
159
+ {showCheckbox && <Checkbox checked={isChecked} />}
160
+ <ListItemText primary={item[itemLabel] || item} />
161
+ </MenuItem>
162
+ )
163
+ })}
164
+ </Select>
165
+ {helperText && <FormHelperText>{helperText}</FormHelperText>}
166
+ </FormControl>
167
+ )
168
+ }}
169
+ />
170
+ )
171
+ }
@@ -0,0 +1,36 @@
1
+ import { iconEye, iconEyeCrossed, IconSvg } from '@graphcommerce/next-ui'
2
+ import { FieldValues } from '@graphcommerce/react-hook-form'
3
+ import { IconButton, IconButtonProps, InputAdornment } from '@mui/material'
4
+ import { MouseEvent, useState } from 'react'
5
+ import { TextFieldElement, TextFieldElementProps } from './TextFieldElement'
6
+
7
+ export type PasswordElementProps<T extends FieldValues> = TextFieldElementProps<T> & {
8
+ iconColor?: IconButtonProps['color']
9
+ }
10
+
11
+ export function PasswordElement<TFieldValues extends FieldValues>({
12
+ iconColor,
13
+ ...props
14
+ }: PasswordElementProps<TFieldValues>): JSX.Element {
15
+ const [password, setPassword] = useState<boolean>(true)
16
+ return (
17
+ <TextFieldElement
18
+ {...props}
19
+ InputProps={{
20
+ endAdornment: (
21
+ <InputAdornment position='end'>
22
+ <IconButton
23
+ onMouseDown={(e: MouseEvent<HTMLButtonElement>) => e.preventDefault()}
24
+ onClick={() => setPassword(!password)}
25
+ tabIndex={-1}
26
+ color={iconColor ?? 'default'}
27
+ >
28
+ <IconSvg src={password ? iconEyeCrossed : iconEye} size='medium' />
29
+ </IconButton>
30
+ </InputAdornment>
31
+ ),
32
+ }}
33
+ type={password ? 'password' : 'text'}
34
+ />
35
+ )
36
+ }
@@ -0,0 +1,25 @@
1
+ import { Path, useWatch, FieldValues } from '@graphcommerce/react-hook-form'
2
+ import { i18n } from '@lingui/core'
3
+ import { PasswordElement, PasswordElementProps } from './PasswordElement'
4
+
5
+ export type PasswordRepeatElementProps<T extends FieldValues> = PasswordElementProps<T> & {
6
+ passwordFieldName: Path<T>
7
+ }
8
+ export function PasswordRepeatElement<TFieldValues extends FieldValues>({
9
+ passwordFieldName,
10
+ ...rest
11
+ }: PasswordRepeatElementProps<TFieldValues>) {
12
+ const pwValue = useWatch({
13
+ name: passwordFieldName,
14
+ control: rest.control,
15
+ })
16
+ return (
17
+ <PasswordElement
18
+ {...rest}
19
+ validation={{
20
+ validate: (value: string) =>
21
+ value === pwValue || i18n._(/* i18n */ "Passwords don't match"),
22
+ }}
23
+ />
24
+ )
25
+ }
@@ -0,0 +1,132 @@
1
+ import {
2
+ Control,
3
+ FieldError,
4
+ Path,
5
+ useController,
6
+ FieldValues,
7
+ } from '@graphcommerce/react-hook-form'
8
+ import {
9
+ FormControl,
10
+ FormControlLabel,
11
+ FormHelperText,
12
+ FormLabel,
13
+ Radio,
14
+ RadioGroup,
15
+ useTheme,
16
+ } from '@mui/material'
17
+ import { ChangeEvent } from 'react'
18
+
19
+ export type RadioButtonGroupProps<T extends FieldValues> = {
20
+ options: { label: string; id: string | number }[] | any[]
21
+ helperText?: string
22
+ name: Path<T>
23
+ required?: boolean
24
+ parseError?: (error: FieldError) => string
25
+ label?: string
26
+ labelKey?: string
27
+ valueKey?: string
28
+ type?: 'number' | 'string'
29
+ emptyOptionLabel?: 'string'
30
+ onChange?: (value: any) => void
31
+ returnObject?: boolean
32
+ row?: boolean
33
+ control?: Control<T>
34
+ }
35
+
36
+ export function RadioButtonGroup<TFieldValues extends FieldValues>({
37
+ helperText,
38
+ options,
39
+ label,
40
+ name,
41
+ parseError,
42
+ labelKey = 'label',
43
+ valueKey = 'id',
44
+ required,
45
+ emptyOptionLabel,
46
+ returnObject,
47
+ row,
48
+ control,
49
+ ...rest
50
+ }: RadioButtonGroupProps<TFieldValues>): JSX.Element {
51
+ const theme = useTheme()
52
+ const {
53
+ field: { value, onChange },
54
+ fieldState: { invalid, error },
55
+ } = useController({
56
+ name,
57
+ rules: required ? { required: 'This field is required' } : undefined,
58
+ control,
59
+ })
60
+
61
+ helperText = error
62
+ ? typeof parseError === 'function'
63
+ ? parseError(error)
64
+ : error.message
65
+ : helperText
66
+
67
+ const onRadioChange = (event: ChangeEvent<HTMLInputElement>) => {
68
+ const radioValue = (event.target as HTMLInputElement).value
69
+ const returnValue = returnObject
70
+ ? options.find((items) => items[valueKey] === radioValue)
71
+ : radioValue
72
+ // setValue(name, returnValue, { shouldValidate: true })
73
+ onChange(returnValue)
74
+ if (typeof rest.onChange === 'function') {
75
+ rest.onChange(returnValue)
76
+ }
77
+ }
78
+
79
+ return (
80
+ <FormControl error={invalid}>
81
+ {label && (
82
+ <FormLabel required={required} error={invalid}>
83
+ {label}
84
+ </FormLabel>
85
+ )}
86
+ <RadioGroup onChange={onRadioChange} name={name} row={row} value={value || ''}>
87
+ {emptyOptionLabel && (
88
+ <FormControlLabel
89
+ control={
90
+ <Radio
91
+ sx={{
92
+ color: invalid ? theme.palette.error.main : undefined,
93
+ }}
94
+ checked={!value}
95
+ />
96
+ }
97
+ label={emptyOptionLabel}
98
+ value=''
99
+ />
100
+ )}
101
+ {options.map((option: any) => {
102
+ const optionKey = option[valueKey]
103
+ if (!optionKey) {
104
+ console.error(
105
+ `CheckboxButtonGroup: valueKey ${valueKey} does not exist on option`,
106
+ option,
107
+ )
108
+ }
109
+ const isChecked = !!(
110
+ value && (returnObject ? value[valueKey] === optionKey : value === optionKey)
111
+ )
112
+ return (
113
+ <FormControlLabel
114
+ control={
115
+ <Radio
116
+ sx={{
117
+ color: invalid ? theme.palette.error.main : undefined,
118
+ }}
119
+ checked={isChecked}
120
+ />
121
+ }
122
+ value={optionKey}
123
+ label={option[labelKey]}
124
+ key={optionKey}
125
+ />
126
+ )
127
+ })}
128
+ </RadioGroup>
129
+ {helperText && <FormHelperText>{helperText}</FormHelperText>}
130
+ </FormControl>
131
+ )
132
+ }
@@ -0,0 +1,73 @@
1
+ import {
2
+ Control,
3
+ Controller,
4
+ ControllerProps,
5
+ FieldError,
6
+ Path,
7
+ FieldValues,
8
+ } from '@graphcommerce/react-hook-form'
9
+ import {
10
+ FormControl,
11
+ FormControlProps,
12
+ FormHelperText,
13
+ FormLabel,
14
+ Slider,
15
+ SliderProps,
16
+ } from '@mui/material'
17
+
18
+ export type SliderElementProps<T extends FieldValues> = Omit<SliderProps, 'control'> & {
19
+ name: Path<T>
20
+ control?: Control<T>
21
+ label?: string
22
+ rules?: ControllerProps['rules']
23
+ parseError?: (error: FieldError) => string
24
+ required?: boolean
25
+ formControlProps?: FormControlProps
26
+ }
27
+
28
+ export function SliderElement<TFieldValues extends FieldValues>({
29
+ name,
30
+ control,
31
+ label,
32
+ rules = {},
33
+ parseError,
34
+ required,
35
+ formControlProps,
36
+ ...other
37
+ }: SliderElementProps<TFieldValues>) {
38
+ if (required && !rules.required) {
39
+ rules.required = 'This field is required'
40
+ }
41
+ return (
42
+ <Controller
43
+ name={name}
44
+ control={control}
45
+ rules={rules}
46
+ render={({ field: { onChange, value }, fieldState: { invalid, error } }) => {
47
+ const parsedHelperText = error
48
+ ? typeof parseError === 'function'
49
+ ? parseError(error)
50
+ : error.message
51
+ : null
52
+ return (
53
+ <FormControl error={invalid} required={required} fullWidth {...formControlProps}>
54
+ {label && (
55
+ <FormLabel component='legend' error={invalid}>
56
+ {label}
57
+ </FormLabel>
58
+ )}
59
+ <Slider
60
+ {...other}
61
+ value={value}
62
+ onChange={onChange}
63
+ valueLabelDisplay={other.valueLabelDisplay || 'auto'}
64
+ />
65
+ {parsedHelperText && (
66
+ <FormHelperText error={invalid}>{parsedHelperText}</FormHelperText>
67
+ )}
68
+ </FormControl>
69
+ )
70
+ }}
71
+ />
72
+ )
73
+ }
@@ -0,0 +1,28 @@
1
+ import { Control, Controller, Path, FieldValues } from '@graphcommerce/react-hook-form'
2
+ import { FormControlLabel, FormControlLabelProps, Switch } from '@mui/material'
3
+
4
+ type IProps = Omit<FormControlLabelProps, 'control'>
5
+
6
+ export type SwitchElementProps<T extends FieldValues> = IProps & {
7
+ name: Path<T>
8
+ control?: Control<T>
9
+ }
10
+
11
+ export function SwitchElement<TFieldValues extends FieldValues>({
12
+ name,
13
+ control,
14
+ ...other
15
+ }: SwitchElementProps<TFieldValues>) {
16
+ return (
17
+ <FormControlLabel
18
+ control={
19
+ <Controller
20
+ name={name}
21
+ control={control}
22
+ render={({ field }) => <Switch {...field} checked={!!field.value} />}
23
+ />
24
+ }
25
+ {...other}
26
+ />
27
+ )
28
+ }
@@ -0,0 +1,101 @@
1
+ import {
2
+ Control,
3
+ Controller,
4
+ ControllerProps,
5
+ FieldError,
6
+ Path,
7
+ FieldValues,
8
+ } from '@graphcommerce/react-hook-form'
9
+ import {
10
+ FormControl,
11
+ FormHelperText,
12
+ FormLabel,
13
+ FormLabelProps,
14
+ ToggleButton,
15
+ ToggleButtonGroup,
16
+ ToggleButtonGroupProps,
17
+ ToggleButtonProps,
18
+ } from '@mui/material'
19
+ import { ReactNode } from 'react'
20
+
21
+ type SingleToggleButtonProps = Omit<ToggleButtonProps, 'value' | 'children'> & {
22
+ id: number | string
23
+ label: ReactNode
24
+ }
25
+
26
+ export type ToggleButtonGroupElementProps<T extends FieldValues> = ToggleButtonGroupProps & {
27
+ required?: boolean
28
+ label?: string
29
+ validation?: ControllerProps['rules']
30
+ name: Path<T>
31
+ parseError?: (error: FieldError) => string
32
+ control?: Control<T>
33
+ options: SingleToggleButtonProps[]
34
+ formLabelProps?: FormLabelProps
35
+ helperText?: string
36
+ }
37
+
38
+ export function ToggleButtonGroupElement<TFieldValues extends FieldValues = FieldValues>({
39
+ name,
40
+ control,
41
+ label,
42
+ validation = {},
43
+ required,
44
+ options = [],
45
+ parseError,
46
+ helperText,
47
+ formLabelProps,
48
+ ...toggleButtonGroupProps
49
+ }: ToggleButtonGroupElementProps<TFieldValues>) {
50
+ if (required && !validation.required) {
51
+ validation.required = 'This field is required'
52
+ }
53
+
54
+ const isRequired = required || !!validation?.required
55
+ return (
56
+ <Controller
57
+ name={name}
58
+ control={control}
59
+ rules={validation}
60
+ render={({ field: { value, onChange, onBlur }, fieldState: { invalid, error } }) => {
61
+ const renderHelperText = error
62
+ ? typeof parseError === 'function'
63
+ ? parseError(error)
64
+ : error.message
65
+ : helperText
66
+ return (
67
+ <FormControl error={invalid} required={isRequired}>
68
+ {label && (
69
+ <FormLabel
70
+ {...formLabelProps}
71
+ error={invalid}
72
+ required={isRequired}
73
+ sx={{ mb: 1, ...formLabelProps?.sx }}
74
+ >
75
+ {label}
76
+ </FormLabel>
77
+ )}
78
+ <ToggleButtonGroup
79
+ {...toggleButtonGroupProps}
80
+ value={value}
81
+ onBlur={onBlur}
82
+ onChange={(event, val) => {
83
+ onChange(val)
84
+ if (typeof toggleButtonGroupProps.onChange === 'function') {
85
+ toggleButtonGroupProps.onChange(event, val)
86
+ }
87
+ }}
88
+ >
89
+ {options.map(({ label, id, ...toggleProps }) => (
90
+ <ToggleButton value={id} {...toggleProps} key={id}>
91
+ {label}
92
+ </ToggleButton>
93
+ ))}
94
+ </ToggleButtonGroup>
95
+ {renderHelperText && <FormHelperText>{renderHelperText}</FormHelperText>}
96
+ </FormControl>
97
+ )
98
+ }}
99
+ />
100
+ )
101
+ }
@@ -1,28 +1,13 @@
1
- export {
2
- AutocompleteElement,
3
- CheckboxButtonGroup,
4
- CheckboxElement,
5
- MultiSelectElement,
6
- PasswordElement,
7
- PasswordRepeatElement,
8
- RadioButtonGroup,
9
- SliderElement,
10
- SwitchElement,
11
- ToggleButtonGroupElement,
12
- } from 'react-hook-form-mui'
13
- export type {
14
- AutocompleteElementProps,
15
- CheckboxButtonGroupProps,
16
- CheckboxElementProps,
17
- MultiSelectElementProps,
18
- PasswordElementProps,
19
- PasswordRepeatElementProps,
20
- RadioButtonGroupProps,
21
- SliderElementProps,
22
- SwitchElementProps,
23
- ToggleButtonGroupElementProps,
24
- } from 'react-hook-form-mui'
25
-
26
- export * from './TextFieldElement'
27
- export * from './SelectElement'
1
+ export * from './AutoCompleteElement'
2
+ export * from './CheckboxButtonGroup'
3
+ export * from './CheckboxElement'
4
+ export * from './MultiSelectElement'
5
+ export * from './PasswordElement'
6
+ export * from './PasswordRepeatElement'
28
7
  export * from './NumberFieldElement'
8
+ export * from './SliderElement'
9
+ export * from './SwitchElement'
10
+ export * from './RadioButtonGroup'
11
+ export * from './SelectElement'
12
+ export * from './TextFieldElement'
13
+ export * from './ToggleButtonGroup'
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@graphcommerce/ecommerce-ui",
3
3
  "homepage": "https://www.graphcommerce.org/",
4
4
  "repository": "github:graphcommerce-org/graphcommerce",
5
- "version": "5.1.0-canary.0",
5
+ "version": "5.1.0-canary.10",
6
6
  "sideEffects": false,
7
7
  "prettier": "@graphcommerce/prettier-config-pwa",
8
8
  "eslintConfig": {
@@ -12,26 +12,22 @@
12
12
  }
13
13
  },
14
14
  "dependencies": {
15
- "@graphcommerce/graphql": "5.1.0-canary.0",
16
- "@graphcommerce/next-ui": "5.1.0-canary.0",
17
- "@graphcommerce/react-hook-form": "5.1.0-canary.0",
18
- "@mui/icons-material": "^5.10.3",
19
- "@mui/x-date-pickers": "^5.0.0",
20
- "react-hook-form-mui": "^5.7.1"
15
+ "@graphcommerce/graphql": "5.1.0-canary.10",
16
+ "@graphcommerce/next-ui": "5.1.0-canary.10",
17
+ "@graphcommerce/react-hook-form": "5.1.0-canary.10"
21
18
  },
22
19
  "devDependencies": {
23
- "@graphcommerce/eslint-config-pwa": "^5.1.0-canary.0",
24
- "@graphcommerce/prettier-config-pwa": "^5.1.0-canary.0",
25
- "@graphcommerce/typescript-config-pwa": "^5.1.0-canary.0",
26
- "@playwright/test": "^1.21.1"
20
+ "@graphcommerce/eslint-config-pwa": "5.1.0-canary.10",
21
+ "@graphcommerce/prettier-config-pwa": "5.1.0-canary.10",
22
+ "@graphcommerce/typescript-config-pwa": "5.1.0-canary.10"
27
23
  },
28
24
  "peerDependencies": {
29
25
  "@lingui/core": "^3.13.2",
30
26
  "@lingui/react": "^3.13.2",
31
- "@mui/material": "5.5.3",
32
- "framer-motion": "^6.2.4",
27
+ "@mui/material": "^5.10.16",
28
+ "framer-motion": "^7.0.0",
33
29
  "next": "^12.1.2",
34
- "react": "^18.0.0",
35
- "react-dom": "^18.0.0"
30
+ "react": "^18.2.0",
31
+ "react-dom": "^18.2.0"
36
32
  }
37
33
  }