@graphcommerce/ecommerce-ui 9.0.0-canary.78 → 9.0.0-canary.80

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,15 @@
1
1
  # @graphcommerce/ecommerce-ui
2
2
 
3
+ ## 9.0.0-canary.80
4
+
5
+ ### Patch Changes
6
+
7
+ - [#2341](https://github.com/graphcommerce-org/graphcommerce/pull/2341) [`7085b4a`](https://github.com/graphcommerce-org/graphcommerce/commit/7085b4a86088328fe54dc4e82ccd296d6459cae7) - Updated all form FieldElements to also accept defaultValue, shouldUnregister and disabled. Moved AutoCompleteElement, CheckboxElement, MultiSelectElement, SliderElement, SwitchElement, ToggleButtonGroup to useController. Removed all parseError props ([@Giovanni-Schroevers](https://github.com/Giovanni-Schroevers))
8
+
9
+ - [#2341](https://github.com/graphcommerce-org/graphcommerce/pull/2341) [`022cbd6`](https://github.com/graphcommerce-org/graphcommerce/commit/022cbd664ea4e8a82997c5edf4451b9182558429) - Moved ActionCardListForm to @graphcommerce/ecommerce-ui to resolve issue with circular dependencies. ([@Giovanni-Schroevers](https://github.com/Giovanni-Schroevers))
10
+
11
+ ## 9.0.0-canary.79
12
+
3
13
  ## 9.0.0-canary.78
4
14
 
5
15
  ## 9.0.0-canary.77
@@ -0,0 +1,85 @@
1
+ import { ActionCardList, ActionCardListProps, ActionCardProps } from '@graphcommerce/next-ui'
2
+ import { ControllerProps, FieldValues, useController } from '@graphcommerce/react-hook-form'
3
+ import React, { MouseEventHandler } from 'react'
4
+
5
+ export type ActionCardItemBase = Pick<ActionCardProps, 'value'>
6
+
7
+ export type ActionCardItemRenderProps<T> = ActionCardProps & {
8
+ onReset: MouseEventHandler<HTMLElement>
9
+ } & T
10
+
11
+ export type ActionCardListFormProps<A, F extends FieldValues = FieldValues> = Omit<
12
+ ActionCardListProps,
13
+ 'value' | 'error' | 'onChange' | 'children'
14
+ > &
15
+ Omit<ControllerProps<F>, 'render'> & {
16
+ items: A[]
17
+ render: React.FC<ActionCardItemRenderProps<A>>
18
+ }
19
+
20
+ export function ActionCardListForm<
21
+ T extends ActionCardItemBase,
22
+ F extends FieldValues = FieldValues,
23
+ >(props: ActionCardListFormProps<T, F>) {
24
+ const {
25
+ required,
26
+ rules,
27
+ items,
28
+ render,
29
+ control,
30
+ name,
31
+ errorMessage,
32
+ defaultValue,
33
+ multiple,
34
+ disabled,
35
+ shouldUnregister,
36
+ ...other
37
+ } = props
38
+ const RenderItem = render as React.FC<ActionCardItemRenderProps<ActionCardItemBase>>
39
+
40
+ function onSelect(itemValue: unknown, selectValues: unknown) {
41
+ return multiple
42
+ ? Array.isArray(selectValues) && selectValues.some((selectValue) => selectValue === itemValue)
43
+ : selectValues === itemValue
44
+ }
45
+
46
+ const {
47
+ field: { onChange, value, ref },
48
+ fieldState,
49
+ formState,
50
+ } = useController({
51
+ ...props,
52
+ control,
53
+ name,
54
+ defaultValue,
55
+ rules: { required, ...rules },
56
+ disabled,
57
+ shouldUnregister,
58
+ })
59
+
60
+ return (
61
+ <ActionCardList
62
+ {...other}
63
+ multiple={multiple}
64
+ required={required}
65
+ value={value}
66
+ ref={ref}
67
+ onChange={(_, incomming) => onChange(incomming)}
68
+ error={formState.isSubmitted && !!fieldState.error}
69
+ errorMessage={fieldState.error?.message}
70
+ >
71
+ {items.map((item) => (
72
+ <RenderItem
73
+ {...item}
74
+ key={`${item.value}`}
75
+ value={item.value}
76
+ selected={onSelect(item.value, value)}
77
+ onReset={(e) => {
78
+ e.preventDefault()
79
+ onChange(null)
80
+ }}
81
+ />
82
+ ))}
83
+ </ActionCardList>
84
+ )
85
+ }
@@ -1,5 +1,10 @@
1
1
  /* eslint-disable @typescript-eslint/no-restricted-imports */
2
- import { Controller, ControllerProps, FieldValues } from '@graphcommerce/react-hook-form'
2
+ import {
3
+ Controller,
4
+ ControllerProps,
5
+ FieldValues,
6
+ useController,
7
+ } from '@graphcommerce/react-hook-form'
3
8
  import { i18n } from '@lingui/core'
4
9
  import {
5
10
  Autocomplete,
@@ -48,6 +53,9 @@ export function AutocompleteElement<TFieldValues extends FieldValues>({
48
53
  multiple,
49
54
  matchId,
50
55
  label,
56
+ disabled,
57
+ defaultValue,
58
+ shouldUnregister,
51
59
  }: AutocompleteElementProps<
52
60
  TFieldValues,
53
61
  AutoDefault | string | any,
@@ -60,99 +68,102 @@ export function AutocompleteElement<TFieldValues extends FieldValues>({
60
68
  required: rules?.required || i18n._(/* i18n */ 'This field is required'),
61
69
  }),
62
70
  }
71
+
72
+ const {
73
+ field: { onChange, onBlur, value, ...fieldRest },
74
+ fieldState: { error },
75
+ } = useController({
76
+ name,
77
+ control,
78
+ rules: validationRules,
79
+ defaultValue,
80
+ disabled,
81
+ shouldUnregister,
82
+ })
83
+
84
+ const values = Array.isArray(value) ? (value as (typeof value)[]) : [value]
85
+ let currentValue = multiple ? value || [] : value || null
86
+ if (matchId) {
87
+ currentValue = multiple
88
+ ? values.map((i: any) => options.find((j) => (j.id || j) === i))
89
+ : options.find((i) => (i.id || i) === value) || null
90
+ }
91
+
63
92
  return (
64
- <Controller
65
- name={name}
66
- control={control}
67
- rules={validationRules}
68
- render={({ field: { onChange, onBlur, value, ...fieldRest }, fieldState: { error } }) => {
69
- const values = Array.isArray(value) ? (value as (typeof value)[]) : [value]
70
- let currentValue = multiple ? value || [] : value || null
93
+ <Autocomplete
94
+ {...autocompleteProps}
95
+ value={currentValue}
96
+ loading={loading}
97
+ multiple={multiple}
98
+ options={options}
99
+ disableCloseOnSelect={
100
+ typeof autocompleteProps?.disableCloseOnSelect === 'boolean'
101
+ ? autocompleteProps.disableCloseOnSelect
102
+ : !!multiple
103
+ }
104
+ isOptionEqualToValue={
105
+ autocompleteProps?.isOptionEqualToValue
106
+ ? autocompleteProps.isOptionEqualToValue
107
+ : (option, v) => (v ? option.id === (v?.id || v) : false)
108
+ }
109
+ getOptionLabel={
110
+ autocompleteProps?.getOptionLabel
111
+ ? autocompleteProps.getOptionLabel
112
+ : (option) => `${option?.label || option}`
113
+ }
114
+ onChange={(event, value, reason, details) => {
115
+ let changedVal = value
71
116
  if (matchId) {
72
- currentValue = multiple
73
- ? values.map((i: any) => options.find((j) => (j.id || j) === i))
74
- : options.find((i) => (i.id || i) === value) || null
117
+ changedVal = Array.isArray(value) ? value.map((i: any) => i?.id || i) : value?.id || value
118
+ }
119
+ onChange(changedVal)
120
+ if (autocompleteProps?.onChange) {
121
+ autocompleteProps.onChange(event, value, reason, details)
122
+ }
123
+ }}
124
+ renderOption={
125
+ autocompleteProps?.renderOption ??
126
+ (showCheckbox
127
+ ? (props, option, { selected }) => (
128
+ <li {...props}>
129
+ <Checkbox sx={{ marginRight: 1 }} checked={selected} />
130
+ {autocompleteProps?.getOptionLabel?.(option) || option.label || option}
131
+ </li>
132
+ )
133
+ : undefined)
134
+ }
135
+ onBlur={(event) => {
136
+ onBlur()
137
+ if (typeof autocompleteProps?.onBlur === 'function') {
138
+ autocompleteProps.onBlur(event)
75
139
  }
76
- return (
77
- <Autocomplete
78
- {...autocompleteProps}
79
- value={currentValue}
80
- loading={loading}
81
- multiple={multiple}
82
- options={options}
83
- disableCloseOnSelect={
84
- typeof autocompleteProps?.disableCloseOnSelect === 'boolean'
85
- ? autocompleteProps.disableCloseOnSelect
86
- : !!multiple
87
- }
88
- isOptionEqualToValue={
89
- autocompleteProps?.isOptionEqualToValue
90
- ? autocompleteProps.isOptionEqualToValue
91
- : (option, v) => (v ? option.id === (v?.id || v) : false)
92
- }
93
- getOptionLabel={
94
- autocompleteProps?.getOptionLabel
95
- ? autocompleteProps.getOptionLabel
96
- : (option) => `${option?.label || option}`
97
- }
98
- onChange={(event, value, reason, details) => {
99
- let changedVal = value
100
- if (matchId) {
101
- changedVal = Array.isArray(value)
102
- ? value.map((i: any) => i?.id || i)
103
- : value?.id || value
104
- }
105
- onChange(changedVal)
106
- if (autocompleteProps?.onChange) {
107
- autocompleteProps.onChange(event, value, reason, details)
108
- }
109
- }}
110
- renderOption={
111
- autocompleteProps?.renderOption ??
112
- (showCheckbox
113
- ? (props, option, { selected }) => (
114
- <li {...props}>
115
- <Checkbox sx={{ marginRight: 1 }} checked={selected} />
116
- {autocompleteProps?.getOptionLabel?.(option) || option.label || option}
117
- </li>
118
- )
119
- : undefined)
120
- }
121
- onBlur={(event) => {
122
- onBlur()
123
- if (typeof autocompleteProps?.onBlur === 'function') {
124
- autocompleteProps.onBlur(event)
125
- }
126
- }}
127
- renderInput={(params) => (
128
- <TextField
129
- name={name}
130
- required={rules?.required ? true : required}
131
- label={label}
132
- {...textFieldProps}
133
- {...params}
134
- error={!!error}
135
- InputProps={{
136
- ...params.InputProps,
137
- endAdornment: (
138
- <>
139
- {loading ? <CircularProgress color='inherit' size={20} /> : null}
140
- {params.InputProps.endAdornment}
141
- </>
142
- ),
143
- ...textFieldProps?.InputProps,
144
- }}
145
- inputProps={{
146
- ...params.inputProps,
147
- ...textFieldProps?.inputProps,
148
- }}
149
- helperText={error ? error.message : textFieldProps?.helperText}
150
- />
151
- )}
152
- {...fieldRest}
153
- />
154
- )
155
140
  }}
141
+ renderInput={(params) => (
142
+ <TextField
143
+ name={name}
144
+ required={rules?.required ? true : required}
145
+ label={label}
146
+ {...textFieldProps}
147
+ {...params}
148
+ error={!!error}
149
+ InputProps={{
150
+ ...params.InputProps,
151
+ endAdornment: (
152
+ <>
153
+ {loading ? <CircularProgress color='inherit' size={20} /> : null}
154
+ {params.InputProps.endAdornment}
155
+ </>
156
+ ),
157
+ ...textFieldProps?.InputProps,
158
+ }}
159
+ inputProps={{
160
+ ...params.inputProps,
161
+ ...textFieldProps?.inputProps,
162
+ }}
163
+ helperText={error ? error.message : textFieldProps?.helperText}
164
+ />
165
+ )}
166
+ {...fieldRest}
156
167
  />
157
168
  )
158
169
  }
@@ -1,10 +1,4 @@
1
- /* eslint-disable @typescript-eslint/no-restricted-imports */
2
- import {
3
- FieldError,
4
- useController,
5
- FieldValues,
6
- UseControllerProps,
7
- } from '@graphcommerce/react-hook-form'
1
+ import { useController, FieldValues, UseControllerProps } from '@graphcommerce/react-hook-form'
8
2
  import { i18n } from '@lingui/core'
9
3
  import {
10
4
  Checkbox,
@@ -21,8 +15,6 @@ export type CheckboxButtonGroupProps<T extends FieldValues> = {
21
15
  options: { id: string | number; label: string }[] | any[]
22
16
  helperText?: string
23
17
  required?: boolean
24
- /** @deprecated Form value parsing should happen in the handleSubmit function of the form */
25
- parseError?: (error: FieldError) => string
26
18
  label?: string
27
19
  labelKey?: string
28
20
  valueKey?: string
@@ -33,22 +25,27 @@ export type CheckboxButtonGroupProps<T extends FieldValues> = {
33
25
  checkboxColor?: CheckboxProps['color']
34
26
  } & UseControllerProps<T>
35
27
 
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 {
28
+ export function CheckboxButtonGroup<TFieldValues extends FieldValues>(
29
+ props: CheckboxButtonGroupProps<TFieldValues>,
30
+ ): JSX.Element {
31
+ const {
32
+ helperText,
33
+ options,
34
+ label,
35
+ name,
36
+ required,
37
+ labelKey = 'label',
38
+ valueKey = 'id',
39
+ returnObject,
40
+ disabled,
41
+ row,
42
+ control,
43
+ checkboxColor,
44
+ defaultValue,
45
+ shouldUnregister,
46
+ ...rest
47
+ } = props
48
+
52
49
  const theme = useTheme()
53
50
  const {
54
51
  field: { value = [], onChange },
@@ -57,13 +54,12 @@ export function CheckboxButtonGroup<TFieldValues extends FieldValues>({
57
54
  name,
58
55
  rules: required ? { required: i18n._(/* i18n */ 'This field is required') } : undefined,
59
56
  control,
57
+ defaultValue,
58
+ disabled,
59
+ shouldUnregister,
60
60
  })
61
61
 
62
- helperText = error
63
- ? typeof parseError === 'function'
64
- ? parseError(error)
65
- : error.message
66
- : helperText
62
+ const parsedHelperText = error ? error.message : helperText
67
63
 
68
64
  const handleChange = (index: number | string) => {
69
65
  const newArray: (string | number)[] | any[] = [...value]
@@ -120,7 +116,7 @@ export function CheckboxButtonGroup<TFieldValues extends FieldValues>({
120
116
  )
121
117
  })}
122
118
  </FormGroup>
123
- {helperText && <FormHelperText>{helperText}</FormHelperText>}
119
+ {parsedHelperText && <FormHelperText>{parsedHelperText}</FormHelperText>}
124
120
  </FormControl>
125
121
  )
126
122
  }
@@ -1,9 +1,4 @@
1
- import {
2
- Controller,
3
- ControllerProps,
4
- FieldError,
5
- FieldValues,
6
- } from '@graphcommerce/react-hook-form'
1
+ import { ControllerProps, FieldValues, useController } from '@graphcommerce/react-hook-form'
7
2
  import { i18n } from '@lingui/core'
8
3
  import {
9
4
  Checkbox,
@@ -19,70 +14,71 @@ import {
19
14
  } from '@mui/material'
20
15
 
21
16
  export type CheckboxElementProps<T extends FieldValues> = Omit<CheckboxProps, 'name'> & {
22
- /** @deprecated Form value parsing should happen in the handleSubmit function of the form */
23
- parseError?: (error: FieldError) => string
24
17
  label?: FormControlLabelProps['label']
25
18
  helperText?: string
26
19
  sx?: SxProps<Theme>
27
20
  formControl?: Omit<FormControlProps<'div'>, 'required' | 'error'>
28
21
  } & Omit<ControllerProps<T>, 'render'>
29
22
 
30
- export function CheckboxElement<TFieldValues extends FieldValues>({
31
- name,
32
- rules = {},
33
- required,
34
- parseError,
35
- label,
36
- control,
37
- helperText,
38
- sx,
39
- formControl,
40
- ...rest
41
- }: CheckboxElementProps<TFieldValues>): JSX.Element {
23
+ export function CheckboxElement<TFieldValues extends FieldValues>(
24
+ props: CheckboxElementProps<TFieldValues>,
25
+ ): JSX.Element {
26
+ const {
27
+ name,
28
+ rules = {},
29
+ required,
30
+ label,
31
+ control,
32
+ helperText,
33
+ sx,
34
+ formControl,
35
+ defaultValue,
36
+ disabled,
37
+ shouldUnregister,
38
+ ...rest
39
+ } = props
40
+
42
41
  if (required && !rules.required) {
43
42
  rules.required = i18n._(/* i18n */ 'This field is required')
44
43
  }
45
44
 
45
+ const {
46
+ field: { value, onChange, ref, ...field },
47
+ fieldState: { invalid, error },
48
+ } = useController({
49
+ name,
50
+ rules,
51
+ control,
52
+ defaultValue,
53
+ disabled,
54
+ shouldUnregister,
55
+ })
56
+
57
+ const parsedHelperText = error ? error.message : helperText
58
+
46
59
  return (
47
- <Controller
48
- name={name}
49
- rules={rules}
50
- control={control}
51
- render={({ field: { value, onChange, ref, ...field }, fieldState: { invalid, error } }) => {
52
- // eslint-disable-next-line no-nested-ternary
53
- const parsedHelperText = error
54
- ? typeof parseError === 'function'
55
- ? parseError(error)
56
- : error.message
57
- : helperText
58
- return (
59
- <FormControl required={required} error={invalid} {...formControl}>
60
- <FormGroup row>
61
- <FormControlLabel
62
- label={label || ''}
63
- control={
64
- <Checkbox
65
- {...rest}
66
- {...field}
67
- inputRef={ref}
68
- color={rest.color || 'primary'}
69
- sx={{
70
- ...(Array.isArray(sx) ? sx : [sx]),
71
- color: invalid ? 'error.main' : undefined,
72
- }}
73
- value={value}
74
- checked={!!value}
75
- onChange={() => onChange(!value)}
76
- />
77
- }
78
- />
79
- </FormGroup>
80
- {parsedHelperText && (
81
- <FormHelperText error={invalid}>{parsedHelperText}</FormHelperText>
82
- )}
83
- </FormControl>
84
- )
85
- }}
86
- />
60
+ <FormControl required={required} error={invalid} {...formControl}>
61
+ <FormGroup row>
62
+ <FormControlLabel
63
+ label={label || ''}
64
+ control={
65
+ <Checkbox
66
+ {...rest}
67
+ {...field}
68
+ inputRef={ref}
69
+ color={rest.color || 'primary'}
70
+ sx={{
71
+ ...(Array.isArray(sx) ? sx : [sx]),
72
+ color: invalid ? 'error.main' : undefined,
73
+ }}
74
+ value={value}
75
+ checked={!!value}
76
+ onChange={() => onChange(!value)}
77
+ />
78
+ }
79
+ />
80
+ </FormGroup>
81
+ {parsedHelperText && <FormHelperText error={invalid}>{parsedHelperText}</FormHelperText>}
82
+ </FormControl>
87
83
  )
88
84
  }
@@ -1,11 +1,6 @@
1
1
  /* eslint-disable no-nested-ternary */
2
2
  import { IconSvg, iconClose } from '@graphcommerce/next-ui'
3
- import {
4
- Controller,
5
- FieldError,
6
- FieldValues,
7
- ControllerProps,
8
- } from '@graphcommerce/react-hook-form'
3
+ import { FieldValues, ControllerProps, useController } from '@graphcommerce/react-hook-form'
9
4
  import { i18n } from '@lingui/core'
10
5
  import {
11
6
  Checkbox,
@@ -27,8 +22,6 @@ export type MultiSelectElementProps<T extends FieldValues> = Omit<SelectProps, '
27
22
  itemValue?: string
28
23
  itemLabel?: string
29
24
  required?: boolean
30
- /** @deprecated Form value parsing should happen in the handleSubmit function of the form */
31
- parseError?: (error: FieldError) => string
32
25
  minWidth?: number
33
26
  menuMaxHeight?: number
34
27
  menuMaxWidth?: number
@@ -44,7 +37,7 @@ const ITEM_PADDING_TOP = 8
44
37
  export function MultiSelectElement<TFieldValues extends FieldValues>(
45
38
  props: MultiSelectElementProps<TFieldValues>,
46
39
  ): JSX.Element {
47
- let {
40
+ const {
48
41
  options,
49
42
  label = '',
50
43
  itemKey = 'id',
@@ -52,7 +45,6 @@ export function MultiSelectElement<TFieldValues extends FieldValues>(
52
45
  itemLabel = 'label',
53
46
  required = false,
54
47
  rules = {},
55
- parseError,
56
48
  name,
57
49
  menuMaxHeight = ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
58
50
  menuMaxWidth = 250,
@@ -62,114 +54,115 @@ export function MultiSelectElement<TFieldValues extends FieldValues>(
62
54
  control,
63
55
  showCheckbox,
64
56
  formControlProps,
57
+ shouldUnregister,
58
+ defaultValue,
59
+ disabled,
65
60
  ...rest
66
61
  } = props
67
62
  if (required && !rules.required) {
68
63
  rules.required = i18n._(/* i18n */ 'This field is required')
69
64
  }
70
65
 
66
+ const {
67
+ field: { value, onChange, ...field },
68
+ fieldState: { invalid, error },
69
+ } = useController({
70
+ name,
71
+ rules,
72
+ control,
73
+ defaultValue,
74
+ disabled,
75
+ shouldUnregister,
76
+ })
77
+
78
+ const parsedHelperText = error ? error.message : helperText
79
+
71
80
  return (
72
- <Controller
73
- name={name}
74
- rules={rules}
75
- control={control}
76
- render={({ field: { value, onChange, ...field }, fieldState: { invalid, error } }) => {
77
- helperText = error
78
- ? typeof parseError === 'function'
79
- ? parseError(error)
80
- : error.message
81
- : helperText
82
- return (
83
- <FormControl
84
- {...formControlProps}
85
- style={{
86
- ...formControlProps?.style,
87
- minWidth,
88
- }}
89
- variant={rest.variant}
90
- fullWidth={rest.fullWidth}
91
- error={invalid}
92
- size={rest.size}
93
- >
94
- {label && (
95
- <InputLabel
96
- size={rest.size === 'small' ? 'small' : undefined}
97
- error={invalid}
98
- htmlFor={rest.id || `select-multi-select-${name}`}
99
- required={required}
100
- >
101
- {label}
102
- </InputLabel>
103
- )}
104
- <Select
105
- {...rest}
106
- {...field}
107
- id={rest.id || `select-multi-select-${name}`}
108
- multiple
109
- label={label || undefined}
110
- error={invalid}
111
- value={value || []}
112
- required={required}
113
- onChange={onChange}
114
- MenuProps={{
115
- ...rest.MenuProps,
116
- PaperProps: {
117
- ...(rest.MenuProps?.PaperProps ?? {
118
- style: {
119
- maxHeight: menuMaxHeight,
120
- width: menuMaxWidth,
121
- ...rest.MenuProps?.PaperProps?.style,
122
- },
123
- }),
124
- },
81
+ <FormControl
82
+ {...formControlProps}
83
+ style={{
84
+ ...formControlProps?.style,
85
+ minWidth,
86
+ }}
87
+ variant={rest.variant}
88
+ fullWidth={rest.fullWidth}
89
+ error={invalid}
90
+ size={rest.size}
91
+ >
92
+ {label && (
93
+ <InputLabel
94
+ size={rest.size === 'small' ? 'small' : undefined}
95
+ error={invalid}
96
+ htmlFor={rest.id || `select-multi-select-${name}`}
97
+ required={required}
98
+ >
99
+ {label}
100
+ </InputLabel>
101
+ )}
102
+ <Select
103
+ {...rest}
104
+ {...field}
105
+ id={rest.id || `select-multi-select-${name}`}
106
+ multiple
107
+ label={label || undefined}
108
+ error={invalid}
109
+ value={value || []}
110
+ required={required}
111
+ onChange={onChange}
112
+ MenuProps={{
113
+ ...rest.MenuProps,
114
+ PaperProps: {
115
+ ...(rest.MenuProps?.PaperProps ?? {
116
+ style: {
117
+ maxHeight: menuMaxHeight,
118
+ width: menuMaxWidth,
119
+ ...rest.MenuProps?.PaperProps?.style,
120
+ },
121
+ }),
122
+ },
123
+ }}
124
+ renderValue={
125
+ typeof rest.renderValue === 'function'
126
+ ? rest.renderValue
127
+ : showChips
128
+ ? (selected) => (
129
+ <div style={{ display: 'flex', flexWrap: 'wrap' }}>
130
+ {((selected as any[]) || []).map((selectedValue) => (
131
+ <Chip
132
+ key={selectedValue}
133
+ label={selectedValue}
134
+ style={{ display: 'flex', flexWrap: 'wrap' }}
135
+ onDelete={() => {
136
+ onChange(value.filter((i: any) => i !== selectedValue))
137
+ // setValue(name, formValue.filter((i: any) => i !== value), { shouldValidate: true })
138
+ }}
139
+ deleteIcon={<IconSvg src={iconClose} />}
140
+ />
141
+ ))}
142
+ </div>
143
+ )
144
+ : (selected) => (Array.isArray(selected) ? selected.join(', ') : '')
145
+ }
146
+ >
147
+ {options.map((item) => {
148
+ const val: string | number = item[itemValue || itemKey] || item
149
+ const isChecked = Array.isArray(value) ? value.includes(val) : false
150
+ return (
151
+ <MenuItem
152
+ key={val}
153
+ value={val}
154
+ sx={{
155
+ fontWeight: (theme) =>
156
+ isChecked ? theme.typography.fontWeightBold : theme.typography.fontWeightRegular,
125
157
  }}
126
- renderValue={
127
- typeof rest.renderValue === 'function'
128
- ? rest.renderValue
129
- : showChips
130
- ? (selected) => (
131
- <div style={{ display: 'flex', flexWrap: 'wrap' }}>
132
- {((selected as any[]) || []).map((selectedValue) => (
133
- <Chip
134
- key={selectedValue}
135
- label={selectedValue}
136
- style={{ display: 'flex', flexWrap: 'wrap' }}
137
- onDelete={() => {
138
- onChange(value.filter((i: any) => i !== selectedValue))
139
- // setValue(name, formValue.filter((i: any) => i !== value), { shouldValidate: true })
140
- }}
141
- deleteIcon={<IconSvg src={iconClose} />}
142
- />
143
- ))}
144
- </div>
145
- )
146
- : (selected) => (Array.isArray(selected) ? selected.join(', ') : '')
147
- }
148
158
  >
149
- {options.map((item) => {
150
- const val: string | number = item[itemValue || itemKey] || item
151
- const isChecked = Array.isArray(value) ? value.includes(val) : false
152
- return (
153
- <MenuItem
154
- key={val}
155
- value={val}
156
- sx={{
157
- fontWeight: (theme) =>
158
- isChecked
159
- ? theme.typography.fontWeightBold
160
- : theme.typography.fontWeightRegular,
161
- }}
162
- >
163
- {showCheckbox && <Checkbox checked={isChecked} />}
164
- <ListItemText primary={item[itemLabel] || item} />
165
- </MenuItem>
166
- )
167
- })}
168
- </Select>
169
- {helperText && <FormHelperText>{helperText}</FormHelperText>}
170
- </FormControl>
171
- )
172
- }}
173
- />
159
+ {showCheckbox && <Checkbox checked={isChecked} />}
160
+ <ListItemText primary={item[itemLabel] || item} />
161
+ </MenuItem>
162
+ )
163
+ })}
164
+ </Select>
165
+ {parsedHelperText && <FormHelperText>{parsedHelperText}</FormHelperText>}
166
+ </FormControl>
174
167
  )
175
168
  }
@@ -44,6 +44,8 @@ export function NumberFieldElement<T extends FieldValues>(props: NumberFieldElem
44
44
  required,
45
45
  defaultValue,
46
46
  variant = 'outlined',
47
+ disabled,
48
+ shouldUnregister,
47
49
  ...textFieldProps
48
50
  } = props
49
51
 
@@ -61,6 +63,8 @@ export function NumberFieldElement<T extends FieldValues>(props: NumberFieldElem
61
63
  control,
62
64
  rules,
63
65
  defaultValue,
66
+ disabled,
67
+ shouldUnregister,
64
68
  })
65
69
 
66
70
  const valueAsNumber = value ? parseFloat(value) : 0
@@ -1,9 +1,4 @@
1
- import {
2
- FieldError,
3
- useController,
4
- FieldValues,
5
- UseControllerProps,
6
- } from '@graphcommerce/react-hook-form'
1
+ import { useController, FieldValues, UseControllerProps } from '@graphcommerce/react-hook-form'
7
2
  import { i18n } from '@lingui/core'
8
3
  import {
9
4
  FormControl,
@@ -20,8 +15,6 @@ export type RadioButtonGroupProps<T extends FieldValues> = {
20
15
  options: { label: string; id: string | number }[] | any[]
21
16
  helperText?: string
22
17
  required?: boolean
23
- /** @deprecated Form value parsing should happen in the handleSubmit function of the form */
24
- parseError?: (error: FieldError) => string
25
18
  label?: string
26
19
  labelKey?: string
27
20
  valueKey?: string
@@ -32,21 +25,27 @@ export type RadioButtonGroupProps<T extends FieldValues> = {
32
25
  row?: boolean
33
26
  } & UseControllerProps<T>
34
27
 
35
- export function RadioButtonGroup<TFieldValues extends FieldValues>({
36
- helperText,
37
- options,
38
- label,
39
- name,
40
- parseError,
41
- labelKey = 'label',
42
- valueKey = 'id',
43
- required,
44
- emptyOptionLabel,
45
- returnObject,
46
- row,
47
- control,
48
- ...rest
49
- }: RadioButtonGroupProps<TFieldValues>): JSX.Element {
28
+ export function RadioButtonGroup<TFieldValues extends FieldValues>(
29
+ props: RadioButtonGroupProps<TFieldValues>,
30
+ ): JSX.Element {
31
+ const {
32
+ helperText,
33
+ options,
34
+ label,
35
+ name,
36
+ labelKey = 'label',
37
+ valueKey = 'id',
38
+ required,
39
+ emptyOptionLabel,
40
+ returnObject,
41
+ row,
42
+ control,
43
+ defaultValue,
44
+ disabled,
45
+ shouldUnregister,
46
+ ...rest
47
+ } = props
48
+
50
49
  const theme = useTheme()
51
50
  const {
52
51
  field: { value, onChange },
@@ -55,13 +54,12 @@ export function RadioButtonGroup<TFieldValues extends FieldValues>({
55
54
  name,
56
55
  rules: required ? { required: i18n._(/* i18n */ 'This field is required') } : undefined,
57
56
  control,
57
+ defaultValue,
58
+ disabled,
59
+ shouldUnregister,
58
60
  })
59
61
 
60
- helperText = error
61
- ? typeof parseError === 'function'
62
- ? parseError(error)
63
- : error.message
64
- : helperText
62
+ const parsedHelperText = error ? error.message : helperText
65
63
 
66
64
  const onRadioChange = (event: ChangeEvent<HTMLInputElement>) => {
67
65
  const radioValue = (event.target as HTMLInputElement).value
@@ -125,7 +123,7 @@ export function RadioButtonGroup<TFieldValues extends FieldValues>({
125
123
  )
126
124
  })}
127
125
  </RadioGroup>
128
- {helperText && <FormHelperText>{helperText}</FormHelperText>}
126
+ {parsedHelperText && <FormHelperText>{parsedHelperText}</FormHelperText>}
129
127
  </FormControl>
130
128
  )
131
129
  }
@@ -1,8 +1,8 @@
1
1
  import {
2
- Controller,
3
2
  ControllerProps,
4
3
  FieldError,
5
4
  FieldValues,
5
+ useController,
6
6
  } from '@graphcommerce/react-hook-form'
7
7
  import { i18n } from '@lingui/core'
8
8
  import {
@@ -16,8 +16,6 @@ import {
16
16
 
17
17
  export type SliderElementProps<T extends FieldValues> = Omit<SliderProps, 'control'> & {
18
18
  label?: string
19
- /** @deprecated Form value parsing should happen in the handleSubmit function of the form */
20
- parseError?: (error: FieldError) => string
21
19
  required?: boolean
22
20
  formControlProps?: FormControlProps
23
21
  } & Omit<ControllerProps<T>, 'render'>
@@ -27,39 +25,40 @@ export function SliderElement<TFieldValues extends FieldValues>({
27
25
  control,
28
26
  label,
29
27
  rules = {},
30
- parseError,
31
28
  required,
32
29
  formControlProps,
30
+ defaultValue,
31
+ disabled,
32
+ shouldUnregister,
33
33
  ...other
34
34
  }: SliderElementProps<TFieldValues>) {
35
35
  if (required && !rules.required) {
36
36
  rules.required = i18n._(/* i18n */ 'This field is required')
37
37
  }
38
+
39
+ const {
40
+ field,
41
+ fieldState: { invalid, error },
42
+ } = useController({
43
+ name,
44
+ control,
45
+ rules,
46
+ defaultValue,
47
+ disabled,
48
+ shouldUnregister,
49
+ })
50
+
51
+ const parsedHelperText = error ? error.message : null
52
+
38
53
  return (
39
- <Controller
40
- name={name}
41
- control={control}
42
- rules={rules}
43
- render={({ field, fieldState: { invalid, error } }) => {
44
- const parsedHelperText = error
45
- ? typeof parseError === 'function'
46
- ? parseError(error)
47
- : error.message
48
- : null
49
- return (
50
- <FormControl error={invalid} required={required} fullWidth {...formControlProps}>
51
- {label && (
52
- <FormLabel component='legend' error={invalid}>
53
- {label}
54
- </FormLabel>
55
- )}
56
- <Slider {...other} {...field} valueLabelDisplay={other.valueLabelDisplay || 'auto'} />
57
- {parsedHelperText && (
58
- <FormHelperText error={invalid}>{parsedHelperText}</FormHelperText>
59
- )}
60
- </FormControl>
61
- )
62
- }}
63
- />
54
+ <FormControl error={invalid} required={required} fullWidth {...formControlProps}>
55
+ {label && (
56
+ <FormLabel component='legend' error={invalid}>
57
+ {label}
58
+ </FormLabel>
59
+ )}
60
+ <Slider {...other} {...field} valueLabelDisplay={other.valueLabelDisplay || 'auto'} />
61
+ {parsedHelperText && <FormHelperText error={invalid}>{parsedHelperText}</FormHelperText>}
62
+ </FormControl>
64
63
  )
65
64
  }
@@ -1,4 +1,4 @@
1
- import { Controller, FieldValues, ControllerProps } from '@graphcommerce/react-hook-form'
1
+ import { FieldValues, ControllerProps, useController } from '@graphcommerce/react-hook-form'
2
2
  import { FormControlLabel, FormControlLabelProps, Switch } from '@mui/material'
3
3
 
4
4
  type IProps = Omit<FormControlLabelProps, 'control'>
@@ -8,18 +8,20 @@ export type SwitchElementProps<T extends FieldValues> = IProps & Omit<Controller
8
8
  export function SwitchElement<TFieldValues extends FieldValues>({
9
9
  name,
10
10
  control,
11
+ defaultValue,
12
+ disabled,
13
+ shouldUnregister,
14
+ rules,
11
15
  ...other
12
16
  }: SwitchElementProps<TFieldValues>) {
13
- return (
14
- <FormControlLabel
15
- control={
16
- <Controller
17
- name={name}
18
- control={control}
19
- render={({ field }) => <Switch {...field} checked={!!field.value} />}
20
- />
21
- }
22
- {...other}
23
- />
24
- )
17
+ const { field } = useController({
18
+ name,
19
+ control,
20
+ defaultValue,
21
+ disabled,
22
+ shouldUnregister,
23
+ rules,
24
+ })
25
+
26
+ return <FormControlLabel control={<Switch {...field} checked={!!field.value} />} {...other} />
25
27
  }
@@ -27,7 +27,9 @@ export function TextFieldElement<TFieldValues extends FieldValues>({
27
27
  control,
28
28
  defaultValue,
29
29
  rules = validation,
30
+ shouldUnregister,
30
31
  showValid,
32
+ disabled,
31
33
  ...rest
32
34
  }: TextFieldElementProps<TFieldValues>): JSX.Element {
33
35
  if (required && !rules.required) {
@@ -44,7 +46,7 @@ export function TextFieldElement<TFieldValues extends FieldValues>({
44
46
  const {
45
47
  field: { onChange, ref, value = '', ...field },
46
48
  fieldState: { error },
47
- } = useController({ name, control, rules, defaultValue })
49
+ } = useController({ name, control, rules, defaultValue, shouldUnregister, disabled })
48
50
 
49
51
  return (
50
52
  <TextField
@@ -1,8 +1,8 @@
1
1
  import {
2
- Controller,
3
2
  ControllerProps,
4
3
  FieldError,
5
4
  FieldValues,
5
+ useController,
6
6
  } from '@graphcommerce/react-hook-form'
7
7
  import { i18n } from '@lingui/core'
8
8
  import {
@@ -25,8 +25,6 @@ type SingleToggleButtonProps = Omit<ToggleButtonProps, 'value' | 'children'> & {
25
25
  export type ToggleButtonGroupElementProps<T extends FieldValues> = ToggleButtonGroupProps & {
26
26
  required?: boolean
27
27
  label?: string
28
- /** @deprecated Form value parsing should happen in the handleSubmit function of the form */
29
- parseError?: (error: FieldError) => string
30
28
  options: SingleToggleButtonProps[]
31
29
  formLabelProps?: FormLabelProps
32
30
  helperText?: string
@@ -39,9 +37,11 @@ export function ToggleButtonGroupElement<TFieldValues extends FieldValues = Fiel
39
37
  rules = {},
40
38
  required,
41
39
  options = [],
42
- parseError,
43
40
  helperText,
44
41
  formLabelProps,
42
+ defaultValue,
43
+ disabled,
44
+ shouldUnregister,
45
45
  ...toggleButtonGroupProps
46
46
  }: ToggleButtonGroupElementProps<TFieldValues>) {
47
47
  if (required && !rules.required) {
@@ -49,50 +49,51 @@ export function ToggleButtonGroupElement<TFieldValues extends FieldValues = Fiel
49
49
  }
50
50
 
51
51
  const isRequired = required || !!rules?.required
52
+
53
+ const {
54
+ field: { value, onChange, onBlur },
55
+ fieldState: { invalid, error },
56
+ } = useController({
57
+ name,
58
+ control,
59
+ rules,
60
+ defaultValue,
61
+ disabled,
62
+ shouldUnregister,
63
+ })
64
+
65
+ const renderHelperText = error ? error.message : helperText
66
+
52
67
  return (
53
- <Controller
54
- name={name}
55
- control={control}
56
- rules={rules}
57
- render={({ field: { value, onChange, onBlur }, fieldState: { invalid, error } }) => {
58
- const renderHelperText = error
59
- ? typeof parseError === 'function'
60
- ? parseError(error)
61
- : error.message
62
- : helperText
63
- return (
64
- <FormControl error={invalid} required={isRequired}>
65
- {label && (
66
- <FormLabel
67
- {...formLabelProps}
68
- error={invalid}
69
- required={isRequired}
70
- sx={{ mb: 1, ...formLabelProps?.sx }}
71
- >
72
- {label}
73
- </FormLabel>
74
- )}
75
- <ToggleButtonGroup
76
- {...toggleButtonGroupProps}
77
- value={value}
78
- onBlur={onBlur}
79
- onChange={(event, val) => {
80
- onChange(val)
81
- if (typeof toggleButtonGroupProps.onChange === 'function') {
82
- toggleButtonGroupProps.onChange(event, val)
83
- }
84
- }}
85
- >
86
- {options.map(({ label, id, ...toggleProps }) => (
87
- <ToggleButton value={id} {...toggleProps} key={id}>
88
- {label}
89
- </ToggleButton>
90
- ))}
91
- </ToggleButtonGroup>
92
- {renderHelperText && <FormHelperText>{renderHelperText}</FormHelperText>}
93
- </FormControl>
94
- )
95
- }}
96
- />
68
+ <FormControl error={invalid} required={isRequired}>
69
+ {label && (
70
+ <FormLabel
71
+ {...formLabelProps}
72
+ error={invalid}
73
+ required={isRequired}
74
+ sx={{ mb: 1, ...formLabelProps?.sx }}
75
+ >
76
+ {label}
77
+ </FormLabel>
78
+ )}
79
+ <ToggleButtonGroup
80
+ {...toggleButtonGroupProps}
81
+ value={value}
82
+ onBlur={onBlur}
83
+ onChange={(event, val) => {
84
+ onChange(val)
85
+ if (typeof toggleButtonGroupProps.onChange === 'function') {
86
+ toggleButtonGroupProps.onChange(event, val)
87
+ }
88
+ }}
89
+ >
90
+ {options.map(({ label, id, ...toggleProps }) => (
91
+ <ToggleButton value={id} {...toggleProps} key={id}>
92
+ {label}
93
+ </ToggleButton>
94
+ ))}
95
+ </ToggleButtonGroup>
96
+ {renderHelperText && <FormHelperText>{renderHelperText}</FormHelperText>}
97
+ </FormControl>
97
98
  )
98
99
  }
@@ -1,3 +1,4 @@
1
+ export * from './ActionCardListForm'
1
2
  export * from './AutoCompleteElement'
2
3
  export * from './CheckboxButtonGroup'
3
4
  export * from './CheckboxElement'
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": "9.0.0-canary.78",
5
+ "version": "9.0.0-canary.80",
6
6
  "sideEffects": false,
7
7
  "prettier": "@graphcommerce/prettier-config-pwa",
8
8
  "eslintConfig": {
@@ -12,12 +12,12 @@
12
12
  }
13
13
  },
14
14
  "peerDependencies": {
15
- "@graphcommerce/eslint-config-pwa": "^9.0.0-canary.78",
16
- "@graphcommerce/graphql": "^9.0.0-canary.78",
17
- "@graphcommerce/next-ui": "^9.0.0-canary.78",
18
- "@graphcommerce/prettier-config-pwa": "^9.0.0-canary.78",
19
- "@graphcommerce/react-hook-form": "^9.0.0-canary.78",
20
- "@graphcommerce/typescript-config-pwa": "^9.0.0-canary.78",
15
+ "@graphcommerce/eslint-config-pwa": "^9.0.0-canary.80",
16
+ "@graphcommerce/graphql": "^9.0.0-canary.80",
17
+ "@graphcommerce/next-ui": "^9.0.0-canary.80",
18
+ "@graphcommerce/prettier-config-pwa": "^9.0.0-canary.80",
19
+ "@graphcommerce/react-hook-form": "^9.0.0-canary.80",
20
+ "@graphcommerce/typescript-config-pwa": "^9.0.0-canary.80",
21
21
  "@lingui/core": "^4.2.1",
22
22
  "@lingui/macro": "^4.2.1",
23
23
  "@lingui/react": "^4.2.1",