@graphcommerce/ecommerce-ui 1.5.0 → 1.5.1

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,14 @@
1
1
  # @graphcommerce/ecommerce-ui
2
2
 
3
+ ## 1.5.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1660](https://github.com/graphcommerce-org/graphcommerce/pull/1660) [`e5048c5ec`](https://github.com/graphcommerce-org/graphcommerce/commit/e5048c5ec52b83dbe70a246ffdcea65b55a884c6) Thanks [@paales](https://github.com/paales)! - Flatten the NumberFieldElement to use the Controller directly
8
+
9
+ - Updated dependencies [[`75ae24a93`](https://github.com/graphcommerce-org/graphcommerce/commit/75ae24a93bd74e3b9b7efda21ec7ba6fbe9a3a75)]:
10
+ - @graphcommerce/react-hook-form@3.3.4
11
+
3
12
  ## 1.5.0
4
13
 
5
14
  ### Minor Changes
@@ -5,32 +5,33 @@ import {
5
5
  IconSvg,
6
6
  responsiveVal,
7
7
  } from '@graphcommerce/next-ui'
8
- import { FieldValues } from '@graphcommerce/react-hook-form'
8
+ import { Controller, ControllerProps, FieldValues } from '@graphcommerce/react-hook-form'
9
9
  import { i18n } from '@lingui/core'
10
10
  import {
11
11
  IconButton,
12
12
  IconButtonProps,
13
13
  SxProps,
14
- useForkRef,
15
14
  Theme,
16
- useEventCallback,
15
+ TextField,
16
+ TextFieldProps,
17
17
  } from '@mui/material'
18
- import { ChangeEvent, Ref, useRef } from 'react'
19
- import { TextFieldElement, TextFieldElementProps } from './TextFieldElement'
20
18
 
21
19
  export type NumberFieldElementProps<T extends FieldValues = FieldValues> = Omit<
22
- TextFieldElementProps<T>,
23
- 'type'
20
+ TextFieldProps,
21
+ 'type' | 'defaultValue'
24
22
  > & {
25
23
  DownProps?: IconButtonProps
26
24
  UpProps?: IconButtonProps
27
25
  sx?: SxProps<Theme>
28
- }
26
+ } & Omit<ControllerProps<T>, 'render'>
29
27
 
30
28
  type OwnerState = { size?: 'small' | 'medium' }
31
- const name = 'TextInputNumber' as const
29
+ const componentName = 'TextInputNumber' as const
32
30
  const parts = ['quantity', 'quantityInput', 'button'] as const
33
- const { withState } = extendableComponent<OwnerState, typeof name, typeof parts>(name, parts)
31
+ const { withState } = extendableComponent<OwnerState, typeof componentName, typeof parts>(
32
+ componentName,
33
+ parts,
34
+ )
34
35
 
35
36
  export function NumberFieldElement<T extends FieldValues = FieldValues>(
36
37
  props: NumberFieldElementProps<T>,
@@ -39,81 +40,100 @@ export function NumberFieldElement<T extends FieldValues = FieldValues>(
39
40
  DownProps = {},
40
41
  UpProps = {},
41
42
  inputProps = {},
42
- inputRef,
43
43
  sx = [],
44
44
  size = 'medium',
45
+ control,
46
+ name,
47
+ rules = {},
48
+ required,
49
+ defaultValue,
45
50
  ...textFieldProps
46
51
  } = props
47
52
 
48
53
  const classes = withState({ size })
49
- const ref = useRef<HTMLInputElement>(null)
50
- const forkRef = useForkRef(ref, inputRef as Ref<HTMLInputElement>)
51
54
 
52
- return (
53
- <TextFieldElement
54
- {...textFieldProps}
55
- size={size}
56
- type='number'
57
- inputRef={forkRef}
58
- className={`${textFieldProps.className ?? ''} ${classes.quantity}`}
59
- sx={[{ width: responsiveVal(80, 120) }, ...(Array.isArray(sx) ? sx : [sx])]}
60
- autoComplete='off'
61
- InputProps={{
62
- ...textFieldProps.InputProps,
63
- startAdornment: (
64
- <IconButton
65
- aria-label={i18n._(/* i18n */ 'Decrease')}
66
- size='medium'
67
- edge='start'
68
- onClick={useEventCallback(() => {
69
- if ((ref.current?.value ?? Infinity) >= inputProps.max) return
55
+ if (required && !rules.required) {
56
+ rules.required = i18n._(/* i18n */ 'This field is required')
57
+ }
70
58
 
71
- ref.current?.stepUp()
72
- ref.current?.dispatchEvent(new Event('change', { bubbles: true }))
73
- })}
74
- tabIndex={-1}
75
- color='inherit'
76
- {...DownProps}
77
- className={`${classes.button} ${DownProps.className ?? ''}`}
78
- >
79
- {DownProps.children ?? <IconSvg src={iconMin} size='small' />}
80
- </IconButton>
81
- ),
82
- endAdornment: (
83
- <IconButton
84
- aria-label={i18n._(/* i18n */ 'Increase')}
85
- size='medium'
86
- edge='end'
87
- onClick={useEventCallback(() => {
88
- if ((ref.current?.value ?? 0) <= inputProps.min) return
59
+ return (
60
+ <Controller
61
+ name={name}
62
+ control={control}
63
+ rules={rules}
64
+ defaultValue={defaultValue}
65
+ render={({ field: { value, onChange, onBlur }, fieldState: { invalid, error } }) => {
66
+ const valueAsNumber = value ? parseFloat(value) : 0
89
67
 
90
- ref.current?.stepDown()
91
- ref.current?.dispatchEvent(new Event('change', { bubbles: true }))
92
- })}
93
- tabIndex={-1}
94
- color='inherit'
95
- {...UpProps}
96
- className={`${classes.button} ${UpProps.className ?? ''}`}
97
- >
98
- {UpProps.children ?? <IconSvg src={iconPlus} size='small' />}
99
- </IconButton>
100
- ),
101
- }}
102
- onChange={(e: ChangeEvent<HTMLInputElement>) => {
103
- if (textFieldProps.onChange) textFieldProps.onChange(e)
104
- }}
105
- inputProps={{
106
- ...inputProps,
107
- 'aria-label': i18n._(/* i18n */ 'Number'),
108
- sx: [
109
- {
110
- textAlign: 'center',
111
- '&::-webkit-inner-spin-button,&::-webkit-outer-spin-button': {
112
- appearance: 'none',
113
- },
114
- },
115
- ],
116
- className: `${inputProps?.className ?? ''} ${classes.quantityInput}`,
68
+ return (
69
+ <TextField
70
+ {...textFieldProps}
71
+ name={name}
72
+ value={value ?? ''}
73
+ onChange={(ev) => {
74
+ const newValue = (ev.target as HTMLInputElement).valueAsNumber
75
+ onChange(Number.isNaN(newValue) ? '' : newValue)
76
+ textFieldProps.onChange?.(ev)
77
+ }}
78
+ onBlur={onBlur}
79
+ required={required}
80
+ error={invalid}
81
+ helperText={error ? error.message : textFieldProps.helperText}
82
+ size={size}
83
+ type='number'
84
+ className={`${textFieldProps.className ?? ''} ${classes.quantity}`}
85
+ sx={[{ width: responsiveVal(80, 120) }, ...(Array.isArray(sx) ? sx : [sx])]}
86
+ autoComplete='off'
87
+ InputProps={{
88
+ ...textFieldProps.InputProps,
89
+ startAdornment: (
90
+ <IconButton
91
+ aria-label={i18n._(/* i18n */ 'Decrease')}
92
+ size='medium'
93
+ edge='start'
94
+ onClick={() => {
95
+ if ((valueAsNumber || Infinity) <= inputProps.min) return
96
+ onChange(value - 1)
97
+ }}
98
+ tabIndex={-1}
99
+ color='inherit'
100
+ {...DownProps}
101
+ className={`${classes.button} ${DownProps.className ?? ''}`}
102
+ >
103
+ {DownProps.children ?? <IconSvg src={iconMin} size='small' />}
104
+ </IconButton>
105
+ ),
106
+ endAdornment: (
107
+ <IconButton
108
+ aria-label={i18n._(/* i18n */ 'Increase')}
109
+ size='medium'
110
+ edge='end'
111
+ onClick={() => {
112
+ if (valueAsNumber >= (inputProps.max ?? Infinity)) return
113
+ onChange(valueAsNumber + 1)
114
+ }}
115
+ tabIndex={-1}
116
+ color='inherit'
117
+ {...UpProps}
118
+ className={`${classes.button} ${UpProps.className ?? ''}`}
119
+ >
120
+ {UpProps.children ?? <IconSvg src={iconPlus} size='small' />}
121
+ </IconButton>
122
+ ),
123
+ }}
124
+ inputProps={{
125
+ ...inputProps,
126
+ 'aria-label': i18n._(/* i18n */ 'Number'),
127
+ className: `${inputProps?.className ?? ''} ${classes.quantityInput}`,
128
+ sx: {
129
+ textAlign: 'center',
130
+ '&::-webkit-inner-spin-button,&::-webkit-outer-spin-button': {
131
+ appearance: 'none',
132
+ },
133
+ },
134
+ }}
135
+ />
136
+ )
117
137
  }}
118
138
  />
119
139
  )
@@ -4,6 +4,7 @@ import {
4
4
  FieldError,
5
5
  FieldValues,
6
6
  } from '@graphcommerce/react-hook-form'
7
+ import { i18n } from '@lingui/core'
7
8
  import { TextField, TextFieldProps } from '@mui/material'
8
9
 
9
10
  export type TextFieldElementProps<T extends FieldValues = FieldValues> = Omit<
@@ -26,7 +27,7 @@ export function TextFieldElement<TFieldValues extends FieldValues = FieldValues>
26
27
  ...rest
27
28
  }: TextFieldElementProps<TFieldValues>): JSX.Element {
28
29
  if (required && !validation.required) {
29
- validation.required = 'This field is required'
30
+ validation.required = i18n._(/* i18n */ 'This field is required')
30
31
  }
31
32
 
32
33
  if (type === 'email' && !validation.pattern) {
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": "1.5.0",
5
+ "version": "1.5.1",
6
6
  "sideEffects": false,
7
7
  "prettier": "@graphcommerce/prettier-config-pwa",
8
8
  "eslintConfig": {
@@ -14,7 +14,7 @@
14
14
  "dependencies": {
15
15
  "@graphcommerce/graphql": "3.4.8",
16
16
  "@graphcommerce/next-ui": "4.27.0",
17
- "@graphcommerce/react-hook-form": "3.3.3",
17
+ "@graphcommerce/react-hook-form": "3.3.4",
18
18
  "@mui/icons-material": "^5.10.3",
19
19
  "@mui/x-date-pickers": "^5.0.0",
20
20
  "react-hook-form-mui": "^5.7.1"