@ultraviolet/form 2.0.0-next.4 → 2.0.0-next.6

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.
@@ -1,14 +1,12 @@
1
1
  import { Checkbox } from '@ultraviolet/ui';
2
- import { forwardRef } from 'react';
2
+ import { useController } from 'react-hook-form';
3
3
  import { jsx } from '@emotion/react/jsx-runtime';
4
4
  import { useErrors } from '../../providers/ErrorContext/index.js';
5
- import { useFormField } from '../../hooks/useFormField.js';
6
5
 
7
- const CheckboxField = /*#__PURE__*/forwardRef((_ref, ref) => {
6
+ const CheckboxField = _ref => {
8
7
  let {
9
- validate,
10
8
  name,
11
- label = '',
9
+ label,
12
10
  size,
13
11
  progress,
14
12
  disabled,
@@ -18,58 +16,55 @@ const CheckboxField = /*#__PURE__*/forwardRef((_ref, ref) => {
18
16
  onChange,
19
17
  onBlur,
20
18
  onFocus,
21
- value,
19
+ rules,
22
20
  helper,
23
21
  tooltip,
24
- 'data-testid': dataTestId
22
+ 'data-testid': dataTestId,
23
+ value
25
24
  } = _ref;
26
25
  const {
27
26
  getError
28
27
  } = useErrors();
29
28
  const {
30
- input,
31
- meta
32
- } = useFormField(name, {
33
- disabled,
34
- required,
35
- type: 'checkbox',
36
- validate,
37
- value
38
- });
39
- const error = getError({
40
- label,
41
- meta: meta,
29
+ field,
30
+ fieldState: {
31
+ error
32
+ }
33
+ } = useController({
42
34
  name,
43
- value: input.value ?? input.checked
35
+ disabled,
36
+ rules: {
37
+ required,
38
+ ...rules
39
+ }
44
40
  });
45
41
  return jsx(Checkbox, {
46
- name: input.name,
42
+ name: field.name,
47
43
  onChange: event => {
48
- input.onChange(event);
49
- onChange?.(event);
44
+ field.onChange(value ? [...(field.value ?? []), value] : event.target.checked);
45
+ onChange?.(event.target.checked);
50
46
  },
51
47
  onBlur: event => {
52
- input.onBlur(event);
48
+ field.onBlur();
53
49
  onBlur?.(event);
54
50
  },
55
- onFocus: event => {
56
- input.onFocus(event);
57
- onFocus?.(event);
58
- },
51
+ onFocus: onFocus,
59
52
  size: size,
60
53
  progress: progress,
61
- disabled: disabled,
62
- checked: input.checked,
63
- error: error,
64
- helper: helper,
65
- ref: ref,
54
+ disabled: field.disabled,
55
+ checked: Array.isArray(field.value) ? field.value.includes(value) : !!field.value,
56
+ error: getError({
57
+ label: label ?? ''
58
+ }, error),
59
+ ref: field.ref,
66
60
  className: className,
67
- value: input.value,
68
61
  required: required,
69
62
  "data-testid": dataTestId,
63
+ helper: helper,
70
64
  tooltip: tooltip,
65
+ value: value,
71
66
  children: children
72
67
  });
73
- });
68
+ };
74
69
 
75
70
  export { CheckboxField };
@@ -1,17 +1,17 @@
1
1
  import { CheckboxGroup } from '@ultraviolet/ui';
2
- import { useFieldArray } from 'react-final-form-arrays';
2
+ import { useController } from 'react-hook-form';
3
3
  import { jsx } from '@emotion/react/jsx-runtime';
4
4
  import { useErrors } from '../../providers/ErrorContext/index.js';
5
5
 
6
6
  const CheckboxGroupField = _ref => {
7
7
  let {
8
8
  legend,
9
- value,
10
9
  className,
11
10
  helper,
12
11
  direction,
13
12
  children,
14
13
  onChange,
14
+ label = '',
15
15
  error: customError,
16
16
  name,
17
17
  required = false
@@ -20,32 +20,29 @@ const CheckboxGroupField = _ref => {
20
20
  getError
21
21
  } = useErrors();
22
22
  const {
23
- fields,
24
- meta
25
- } = useFieldArray(name, {
26
- type: 'checkbox',
27
- value,
28
- validate: localValue => required && localValue?.length === 0 ? 'Required' : undefined
29
- });
30
- const error = getError({
31
- label: legend,
32
- meta,
33
- value: fields.value,
23
+ field,
24
+ fieldState: {
25
+ error
26
+ }
27
+ } = useController({
34
28
  name
35
29
  });
36
30
  return jsx(CheckboxGroup, {
37
31
  legend: legend,
38
- name: fields.name,
39
- value: fields.value,
32
+ name: name,
33
+ value: field.value,
40
34
  onChange: event => {
41
- if (fields.value?.includes(event.currentTarget.value)) {
42
- fields.remove(fields.value.indexOf(event.currentTarget?.value));
35
+ const fieldValue = field.value;
36
+ if (fieldValue?.includes(event.currentTarget.value)) {
37
+ field.onChange(fieldValue?.filter(currentValue => currentValue !== event.currentTarget.value));
43
38
  } else {
44
- fields.push(event.currentTarget.value);
39
+ field.onChange([...field.value, event.currentTarget.value]);
45
40
  }
46
- onChange?.(event);
41
+ onChange?.(event.currentTarget.value);
47
42
  },
48
- error: error ?? customError,
43
+ error: getError({
44
+ label
45
+ }, error) ?? customError,
49
46
  className: className,
50
47
  direction: direction,
51
48
  helper: helper,
@@ -1,7 +1,9 @@
1
1
  import { DateInput } from '@ultraviolet/ui';
2
+ import { useController } from 'react-hook-form';
3
+ import { maxDateValidator } from '../../validators/maxDate.js';
4
+ import { minDateValidator } from '../../validators/minDate.js';
2
5
  import { jsx } from '@emotion/react/jsx-runtime';
3
6
  import { useErrors } from '../../providers/ErrorContext/index.js';
4
- import { useFormField } from '../../hooks/useFormField.js';
5
7
 
6
8
  const parseDate = value => typeof value === 'string' ? new Date(value) : value;
7
9
  const isEmpty = value => typeof value === 'string' ? value === '' : value === undefined;
@@ -10,18 +12,15 @@ const DateField = _ref => {
10
12
  required,
11
13
  name,
12
14
  label = '',
13
- validate,
14
15
  format,
15
16
  locale,
16
17
  maxDate,
17
18
  minDate,
18
- initialValue,
19
19
  disabled,
20
- value: inputVal,
21
20
  onChange,
22
21
  onBlur,
23
22
  onFocus,
24
- formatOnBlur,
23
+ rules,
25
24
  autoFocus = false,
26
25
  excludeDates,
27
26
  selectsRange,
@@ -31,66 +30,63 @@ const DateField = _ref => {
31
30
  getError
32
31
  } = useErrors();
33
32
  const {
34
- input,
35
- meta
36
- } = useFormField(name, {
37
- disabled,
38
- formatOnBlur,
39
- initialValue,
40
- maxDate,
41
- minDate,
42
- required,
43
- validate,
44
- value: inputVal
45
- });
46
- const error = getError({
47
- label,
48
- maxDate,
49
- meta: meta,
50
- minDate,
33
+ field,
34
+ fieldState: {
35
+ error
36
+ }
37
+ } = useController({
51
38
  name,
52
- value: input.value
39
+ rules: {
40
+ ...rules,
41
+ validate: {
42
+ ...rules?.validate,
43
+ minDate: minDateValidator(minDate),
44
+ maxDate: maxDateValidator(maxDate)
45
+ },
46
+ required
47
+ }
53
48
  });
54
49
  return jsx(DateInput, {
50
+ name: field.name,
55
51
  label: label,
52
+ value: field.value,
56
53
  format: format || (value => value ? parseDate(value).toLocaleDateString() : ''),
57
54
  locale: locale,
58
55
  required: required,
59
- value: input.value,
60
56
  onChange: val => {
61
57
  if (val && val instanceof Date) {
62
58
  onChange?.(val);
63
59
  const newDate = parseDate(val);
64
- if (isEmpty(input.value)) {
65
- input.onChange(newDate);
60
+ if (isEmpty(field.value)) {
61
+ field.onChange(newDate);
66
62
  return;
67
63
  }
68
- const currentDate = parseDate(input.value);
64
+ const currentDate = parseDate(field.value);
69
65
  newDate.setHours(currentDate.getHours(), currentDate.getMinutes());
70
- input.onChange(newDate);
66
+ field.onChange(newDate);
71
67
  } else if (Array.isArray(val)) {
72
- input.onChange(val);
68
+ field.onChange(val);
73
69
  }
74
70
  },
75
71
  onBlur: e => {
76
- input.onBlur(e);
72
+ field.onBlur();
77
73
  onBlur?.(e);
78
74
  },
79
- onFocus: e => {
80
- input.onFocus(e);
81
- onFocus?.(e);
82
- },
75
+ onFocus: onFocus,
83
76
  maxDate: maxDate,
84
77
  minDate: minDate,
85
- startDate: selectsRange ? input.value[0] : undefined,
86
- endDate: selectsRange ? input.value[1] : undefined,
87
- error: error,
78
+ error: getError({
79
+ minDate,
80
+ maxDate,
81
+ label
82
+ }, error),
88
83
  disabled: disabled,
89
84
  autoFocus: autoFocus,
90
- name: input.name,
91
- "data-testid": dataTestId,
92
85
  excludeDates: excludeDates,
93
- selectsRange: selectsRange
86
+ selectsRange: selectsRange,
87
+ "data-testid": dataTestId,
88
+ startDate: selectsRange && Array.isArray(field.value) ? field.value[0] : undefined,
89
+ endDate: selectsRange && Array.isArray(field.value) ? field.value[1] : undefined
94
90
  });
95
91
  };
96
92
 
@@ -1,43 +1,83 @@
1
- import arrayMutators from 'final-form-arrays';
2
- import { Form as Form$1 } from 'react-final-form';
1
+ import React, { useMemo } from 'react';
2
+ import { useForm, FormProvider } from 'react-hook-form';
3
+ import { FORM_ERROR } from '../../constants.js';
4
+ import { defaultErrors } from './defaultErrors.js';
3
5
  import { jsx } from '@emotion/react/jsx-runtime';
4
6
  import { ErrorProvider } from '../../providers/ErrorContext/index.js';
5
7
 
8
+ const FormSubmitContext = /*#__PURE__*/React.createContext({});
6
9
  const Form = _ref => {
7
10
  let {
8
11
  children,
9
- onRawSubmit,
10
- errors,
12
+ methods: methodsProp,
11
13
  initialValues,
12
- validateOnBlur,
13
- validate,
14
- name,
15
- render,
16
- mutators,
17
- keepDirtyOnReinitialize,
18
- className
14
+ errors,
15
+ onRawSubmit,
16
+ name
19
17
  } = _ref;
20
- return jsx(Form$1, {
21
- initialValues: initialValues,
22
- validateOnBlur: validateOnBlur,
23
- validate: validate,
24
- mutators: {
25
- ...arrayMutators,
26
- ...mutators
27
- },
28
- onSubmit: onRawSubmit,
29
- render: render ?? (renderProps => jsx(ErrorProvider, {
30
- errors: errors,
31
- children: jsx("form", {
32
- noValidate: true,
33
- name: name,
34
- onSubmit: renderProps.handleSubmit,
35
- className: className,
36
- children: typeof children === 'function' ? children(renderProps) : children
18
+ const methodsHook = useForm({
19
+ defaultValues: initialValues,
20
+ mode: 'onChange'
21
+ });
22
+ const methods = !methodsProp ? methodsHook : methodsProp;
23
+ const handleSubmit = methods.handleSubmit(async values => {
24
+ const result = await onRawSubmit(values, {
25
+ reset: methods.reset,
26
+ resetFieldState: methods.resetField,
27
+ restart: () => methods.reset(initialValues),
28
+ change: methods.setValue
29
+ });
30
+ if (result === null) {
31
+ methods.setError('root.submit', {
32
+ type: 'custom'
33
+ });
34
+ return;
35
+ }
36
+ if (result && typeof result !== 'boolean' && typeof result !== 'number') {
37
+ methods.setError('root.submit', {
38
+ type: 'custom',
39
+ message: typeof result === 'object' ? result[FORM_ERROR] : result
40
+ });
41
+ }
42
+ });
43
+ const formSubmitContextValue = useMemo(() => ({
44
+ handleSubmit
45
+ }), [handleSubmit]);
46
+ return jsx(FormProvider, {
47
+ ...methods,
48
+ children: jsx(FormSubmitContext.Provider, {
49
+ value: formSubmitContextValue,
50
+ children: jsx(ErrorProvider, {
51
+ errors: {
52
+ ...defaultErrors,
53
+ ...errors
54
+ },
55
+ children: jsx("form", {
56
+ onSubmit: async e => {
57
+ e.preventDefault();
58
+ e.stopPropagation();
59
+ await handleSubmit(e);
60
+ },
61
+ name: name,
62
+ children: typeof children === 'function' ? children({
63
+ values: methods.watch(),
64
+ hasValidationErrors: !methods.formState.isValid,
65
+ errors: methods.formState.errors,
66
+ submitting: methods.formState.isSubmitting,
67
+ pristine: !methods.formState.isDirty,
68
+ handleSubmit,
69
+ submitError: !!methods.formState.errors?.root?.['submit'],
70
+ valid: methods.formState.isValid,
71
+ form: {
72
+ change: methods.setValue,
73
+ reset: methods.reset,
74
+ submit: handleSubmit
75
+ }
76
+ }) : children
77
+ })
37
78
  })
38
- })),
39
- keepDirtyOnReinitialize: keepDirtyOnReinitialize
79
+ })
40
80
  });
41
81
  };
42
82
 
43
- export { Form };
83
+ export { Form, FormSubmitContext };
@@ -1,6 +1,7 @@
1
1
  import { NumberInput } from '@ultraviolet/ui';
2
+ import { useController } from 'react-hook-form';
2
3
  import { jsx } from '@emotion/react/jsx-runtime';
3
- import { useFormField } from '../../hooks/useFormField.js';
4
+ import { useErrors } from '../../providers/ErrorContext/index.js';
4
5
 
5
6
  const NumberInputField = _ref => {
6
7
  let {
@@ -17,38 +18,41 @@ const NumberInputField = _ref => {
17
18
  size,
18
19
  step,
19
20
  text,
20
- validate,
21
- value,
21
+ rules,
22
22
  className,
23
- 'data-testid': dataTestId
23
+ label
24
24
  } = _ref;
25
25
  const {
26
- input
27
- } = useFormField(name, {
28
- disabled,
29
- required,
30
- type: 'number',
31
- validate,
32
- value,
33
- defaultValue: 0,
34
- max: maxValue,
35
- min: minValue
26
+ getError
27
+ } = useErrors();
28
+ const {
29
+ field,
30
+ fieldState: {
31
+ error
32
+ }
33
+ } = useController({
34
+ name,
35
+ rules: {
36
+ max: maxValue,
37
+ min: minValue,
38
+ required,
39
+ ...rules
40
+ }
36
41
  });
37
42
  return jsx(NumberInput, {
38
- name: name,
43
+ name: field.name,
44
+ value: field.value,
39
45
  disabled: disabled,
40
46
  onBlur: event => {
41
- input.onBlur(event);
47
+ field.onBlur();
42
48
  onBlur?.(event);
43
49
  },
44
50
  onChange: event => {
45
- input.onChange(event);
51
+ // React hook form doesnt allow undefined values after definition https://react-hook-form.com/docs/usecontroller/controller (that make sense)
52
+ field.onChange(event ?? null);
46
53
  onChange?.(event);
47
54
  },
48
- onFocus: event => {
49
- input.onFocus(event);
50
- onFocus?.(event);
51
- },
55
+ onFocus: onFocus,
52
56
  maxValue: maxValue,
53
57
  minValue: minValue,
54
58
  onMinCrossed: onMinCrossed,
@@ -56,9 +60,13 @@ const NumberInputField = _ref => {
56
60
  size: size,
57
61
  step: step,
58
62
  text: text,
59
- value: input.value,
60
63
  className: className,
61
- "data-testid": dataTestId
64
+ label: label,
65
+ error: getError({
66
+ label: label ?? '',
67
+ max: maxValue,
68
+ min: minValue
69
+ }, error)
62
70
  });
63
71
  };
64
72
 
@@ -1,7 +1,7 @@
1
1
  import { Radio } from '@ultraviolet/ui';
2
+ import { useController } from 'react-hook-form';
2
3
  import { jsx } from '@emotion/react/jsx-runtime';
3
4
  import { useErrors } from '../../providers/ErrorContext/index.js';
4
- import { useFormField } from '../../hooks/useFormField.js';
5
5
 
6
6
  const RadioField = _ref => {
7
7
  let {
@@ -9,57 +9,52 @@ const RadioField = _ref => {
9
9
  'data-testid': dataTestId,
10
10
  disabled,
11
11
  id,
12
- label = '',
13
12
  name,
14
13
  onBlur,
14
+ label = '',
15
15
  onChange,
16
16
  onFocus,
17
17
  required,
18
- validate,
19
18
  value,
19
+ rules,
20
20
  tooltip
21
21
  } = _ref;
22
22
  const {
23
23
  getError
24
24
  } = useErrors();
25
25
  const {
26
- input,
27
- meta
28
- } = useFormField(name, {
29
- required,
30
- type: 'radio',
31
- validate,
32
- value
33
- });
34
- const error = getError({
35
- disabled,
36
- label: label,
37
- meta: meta,
26
+ field,
27
+ fieldState: {
28
+ error
29
+ }
30
+ } = useController({
38
31
  name,
39
- value: input.value
32
+ rules: {
33
+ required,
34
+ ...rules
35
+ }
40
36
  });
41
37
  return jsx(Radio, {
42
- checked: input.checked,
38
+ name: field.name,
39
+ checked: field.value === value,
43
40
  className: className,
44
41
  "data-testid": dataTestId,
45
42
  disabled: disabled,
46
- error: error,
43
+ error: getError({
44
+ label: typeof label === 'string' ? label : ''
45
+ }, error),
47
46
  id: id,
48
- name: input.name,
49
- onChange: event => {
50
- input.onChange(event);
51
- onChange?.(event);
47
+ onChange: () => {
48
+ field.onChange(value);
49
+ onChange?.(value);
52
50
  },
53
51
  onBlur: event => {
54
- input.onBlur(event);
52
+ field.onBlur();
55
53
  onBlur?.(event);
56
54
  },
57
- onFocus: event => {
58
- input.onFocus(event);
59
- onFocus?.(event);
60
- },
55
+ onFocus: onFocus,
61
56
  required: required,
62
- value: input.value,
57
+ value: value ?? '',
63
58
  label: label,
64
59
  tooltip: tooltip
65
60
  });
@@ -1,7 +1,7 @@
1
1
  import { RadioGroup } from '@ultraviolet/ui';
2
+ import { useController } from 'react-hook-form';
2
3
  import { jsx } from '@emotion/react/jsx-runtime';
3
4
  import { useErrors } from '../../providers/ErrorContext/index.js';
4
- import { useFormField } from '../../hooks/useFormField.js';
5
5
 
6
6
  const RadioGroupField = _ref => {
7
7
  let {
@@ -10,9 +10,9 @@ const RadioGroupField = _ref => {
10
10
  name,
11
11
  onChange,
12
12
  required,
13
- validate,
14
- value,
13
+ rules,
15
14
  children,
15
+ label = '',
16
16
  error: customError,
17
17
  helper,
18
18
  direction
@@ -21,30 +21,27 @@ const RadioGroupField = _ref => {
21
21
  getError
22
22
  } = useErrors();
23
23
  const {
24
- input,
25
- meta
26
- } = useFormField(name, {
27
- required,
28
- validate,
29
- value
30
- });
31
- const error = getError({
32
- label: legend,
33
- meta: meta,
24
+ field,
25
+ fieldState: {
26
+ error
27
+ }
28
+ } = useController({
34
29
  name,
35
- value: input.value
30
+ rules
36
31
  });
37
32
  return jsx(RadioGroup, {
38
33
  className: className,
39
- name: input.name,
34
+ name: field.name,
40
35
  onChange: event => {
41
- input.onChange(event);
42
- onChange?.(event);
36
+ field.onChange(event);
37
+ onChange?.(event.target.value);
43
38
  },
44
39
  required: required,
45
- value: input.value,
40
+ value: field.value,
46
41
  legend: legend,
47
- error: error ?? customError,
42
+ error: getError({
43
+ label
44
+ }, error) ?? customError,
48
45
  helper: helper,
49
46
  direction: direction,
50
47
  children: children