@availity/mui-controlled-form 0.3.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,28 +1,18 @@
1
1
  import { CodesAutocomplete, CodesAutocompleteProps } from '@availity/mui-autocomplete';
2
2
  import { Controller, RegisterOptions, FieldValues } from 'react-hook-form';
3
- import { ControllerProps, DeprecatedRulesProps } from './Types';
3
+ import { ControllerProps } from './Types';
4
4
 
5
- export type ControlledCodesAutocompleteProps = Omit<CodesAutocompleteProps,
6
- 'onBlur' | 'onChange' | 'value' | 'name'
7
- > & Pick<RegisterOptions<FieldValues, string>,
8
- 'onBlur' | 'onChange' | 'value'
9
- > & ControllerProps
10
- //TODO v1 - remove deprecated props
11
- & DeprecatedRulesProps;
5
+ export type ControlledCodesAutocompleteProps = Omit<CodesAutocompleteProps, 'onBlur' | 'onChange' | 'value' | 'name'> &
6
+ Pick<RegisterOptions<FieldValues, string>, 'onBlur' | 'onChange' | 'value'> &
7
+ ControllerProps;
12
8
 
13
9
  export const ControlledCodesAutocomplete = ({
14
10
  name,
15
11
  defaultValue,
16
- deps,
17
- max,
18
- maxLength,
19
12
  onBlur,
20
13
  onChange,
21
- pattern,
22
- required,
23
14
  rules = {},
24
15
  shouldUnregister,
25
- validate,
26
16
  value,
27
17
  FieldProps,
28
18
  ...rest
@@ -32,15 +22,9 @@ export const ControlledCodesAutocomplete = ({
32
22
  name={name}
33
23
  defaultValue={defaultValue}
34
24
  rules={{
35
- deps,
36
- max,
37
- maxLength,
38
25
  onBlur,
39
26
  onChange,
40
- pattern,
41
- required,
42
27
  shouldUnregister,
43
- validate,
44
28
  value,
45
29
  ...rules,
46
30
  }}
@@ -49,6 +33,7 @@ export const ControlledCodesAutocomplete = ({
49
33
  <CodesAutocomplete
50
34
  {...rest}
51
35
  FieldProps={{
36
+ required: typeof rules.required === 'object' ? rules.required.value : !!rules.required,
52
37
  ...FieldProps,
53
38
  error: !!error,
54
39
  helperText: error?.message ? (
@@ -60,7 +45,7 @@ export const ControlledCodesAutocomplete = ({
60
45
  ) : (
61
46
  FieldProps?.helperText
62
47
  ),
63
- inputRef:ref
48
+ inputRef: ref,
64
49
  }}
65
50
  onChange={(event, value, reason) => {
66
51
  if (reason === 'clear') {
@@ -10,7 +10,7 @@ describe('Datepicker', () => {
10
10
  test('should render successfully and submit selection', async () => {
11
11
  const screen = render(
12
12
  <ThemeProvider>
13
- <TestForm UseFormOptions={{values: { controlledDatepicker: null}}} onSubmit={onSubmit}>
13
+ <TestForm UseFormOptions={{ values: { controlledDatepicker: null } }} onSubmit={onSubmit}>
14
14
  <ControlledDatepicker
15
15
  name="controlledDatepicker"
16
16
  FieldProps={{
@@ -38,4 +38,71 @@ describe('Datepicker', () => {
38
38
  expect(dayjs(controlledDatepickerValue).isValid()).toBeTruthy();
39
39
  });
40
40
  }, 10000);
41
+
42
+ describe('when using rules', () => {
43
+ describe('when required', () => {
44
+ test('should indicate it is required when passing a string', async () => {
45
+ const screen = render(
46
+ <ThemeProvider>
47
+ <TestForm UseFormOptions={{ values: { controlledDatepicker: null } }} onSubmit={onSubmit}>
48
+ <ControlledDatepicker
49
+ name="controlledDatepicker"
50
+ FieldProps={{
51
+ fullWidth: false,
52
+ helperText: 'Help text for the field',
53
+ helpTopicId: '1234',
54
+ label: 'Date',
55
+ }}
56
+ rules={{ required: 'This field is required' }}
57
+ />
58
+ </TestForm>
59
+ </ThemeProvider>
60
+ );
61
+
62
+ expect(screen.getByText('*')).toBeDefined();
63
+ });
64
+
65
+ test('should indicate it is required when passing an object with true', async () => {
66
+ const screen = render(
67
+ <ThemeProvider>
68
+ <TestForm UseFormOptions={{ values: { controlledDatepicker: null } }} onSubmit={onSubmit}>
69
+ <ControlledDatepicker
70
+ name="controlledDatepicker"
71
+ FieldProps={{
72
+ fullWidth: false,
73
+ helperText: 'Help text for the field',
74
+ helpTopicId: '1234',
75
+ label: 'Date',
76
+ }}
77
+ rules={{ required: { value: true, message: 'This field is required' } }}
78
+ />
79
+ </TestForm>
80
+ </ThemeProvider>
81
+ );
82
+
83
+ expect(screen.getByText('*')).toBeDefined();
84
+ });
85
+
86
+ test('should not indicate it is required when passing an object with false', async () => {
87
+ const screen = render(
88
+ <ThemeProvider>
89
+ <TestForm UseFormOptions={{ values: { controlledDatepicker: null } }} onSubmit={onSubmit}>
90
+ <ControlledDatepicker
91
+ name="controlledDatepicker"
92
+ FieldProps={{
93
+ fullWidth: false,
94
+ helperText: 'Help text for the field',
95
+ helpTopicId: '1234',
96
+ label: 'Date',
97
+ }}
98
+ rules={{ required: { value: false, message: 'This field is required' } }}
99
+ />
100
+ </TestForm>
101
+ </ThemeProvider>
102
+ );
103
+
104
+ expect(screen.queryByText('*')).toBeNull();
105
+ });
106
+ });
107
+ });
41
108
  });
@@ -1,30 +1,18 @@
1
1
  import { Datepicker, DatepickerProps } from '@availity/mui-datepicker';
2
2
  import { RegisterOptions, FieldValues, Controller } from 'react-hook-form';
3
- import { ControllerProps, DeprecatedRulesProps } from './Types';
3
+ import { ControllerProps } from './Types';
4
4
 
5
- export type ControlledDatepickerProps = Omit<DatepickerProps,
6
- 'onBlur' | 'onChange' | 'value' | 'name'
7
- > & Pick<RegisterOptions<FieldValues, string>,
8
- 'onBlur' | 'onChange' | 'value'
9
- > & ControllerProps
10
- //TODO v1 - remove deprecated props
11
- & DeprecatedRulesProps;
5
+ export type ControlledDatepickerProps = Omit<DatepickerProps, 'onBlur' | 'onChange' | 'value' | 'name'> &
6
+ Pick<RegisterOptions<FieldValues, string>, 'onBlur' | 'onChange' | 'value'> &
7
+ ControllerProps;
12
8
 
13
9
  export const ControlledDatepicker = ({
14
10
  name,
15
11
  defaultValue,
16
- deps,
17
- max,
18
- maxLength,
19
- min,
20
- minLength,
21
12
  onBlur,
22
13
  onChange,
23
- pattern,
24
- required,
25
14
  rules = {},
26
15
  shouldUnregister,
27
- validate,
28
16
  value,
29
17
  FieldProps,
30
18
  ...rest
@@ -34,17 +22,9 @@ export const ControlledDatepicker = ({
34
22
  name={name}
35
23
  defaultValue={defaultValue}
36
24
  rules={{
37
- deps,
38
- max,
39
- maxLength,
40
- min,
41
- minLength,
42
25
  onBlur,
43
26
  onChange,
44
- pattern,
45
- required,
46
27
  shouldUnregister,
47
- validate,
48
28
  value,
49
29
  ...rules,
50
30
  }}
@@ -53,6 +33,7 @@ export const ControlledDatepicker = ({
53
33
  <Datepicker
54
34
  {...rest}
55
35
  FieldProps={{
36
+ required: typeof rules.required === 'object' ? rules.required.value : !!rules.required,
56
37
  ...FieldProps,
57
38
  error: !!error,
58
39
  helperText: error ? (
@@ -66,7 +47,7 @@ export const ControlledDatepicker = ({
66
47
  ),
67
48
  inputRef: ref,
68
49
  inputProps: {
69
- onBlur: onBlur
50
+ onBlur: onBlur,
70
51
  },
71
52
  }}
72
53
  onChange={onChange}
@@ -1,103 +1,10 @@
1
1
  import { render, fireEvent, waitFor } from '@testing-library/react';
2
- import { Paper } from '@availity/mui-paper';
3
- import { Typography } from '@availity/mui-typography';
4
- import { Grid } from '@availity/mui-layout';
5
- import { Button } from '@availity/mui-button';
6
2
  import { ControlledInput } from './Input';
7
- import { useFormContext } from '..';
8
- import { ControlledForm } from './ControlledForm';
9
3
  import { TestForm } from './UtilComponents';
10
4
 
11
- const SubmittedValues = () => {
12
- const {
13
- getValues,
14
- formState: { isSubmitSuccessful },
15
- } = useFormContext();
16
-
17
- return isSubmitSuccessful ? (
18
- <Paper sx={{ padding: '1.5rem', marginTop: '1.5rem' }}>
19
- <Typography variant="h2">Submitted Values</Typography>
20
- <pre data-testid="result">{JSON.stringify(getValues(), null, 2)}</pre>
21
- </Paper>
22
- ) : null;
23
- };
24
-
25
- const Actions = () => {
26
- const {
27
- formState: { isSubmitSuccessful },
28
- } = useFormContext();
29
- return (
30
- <Grid container direction="row" justifyContent="space-between">
31
- <Button type="submit" disabled={isSubmitSuccessful} children="Submit" />
32
- </Grid>
33
- );
34
- };
35
-
36
5
  const onSubmit = jest.fn();
37
- const onSubmitDeprecated = jest.fn();
38
6
 
39
7
  describe('ControlledInput', () => {
40
- test('Deprecation Check: should render the error styling if an error is returned', async () => {
41
- const screen = render(
42
- <ControlledForm values={{ controlledInput: undefined }} onSubmit={onSubmitDeprecated}>
43
- <ControlledInput
44
- data-testid="controlledInputWrapper"
45
- name="controlledInput"
46
- required="This field is required."
47
- maxLength={{ value: 10, message: 'Too long' }}
48
- inputProps={{
49
- 'data-testid': 'testInput',
50
- }}
51
- />
52
- <Actions />
53
- <SubmittedValues />
54
- </ControlledForm>
55
- );
56
-
57
- const input = screen.getByTestId('testInput');
58
-
59
- fireEvent.change(input, { target: { value: 'This is way too much text' } });
60
-
61
- fireEvent.click(screen.getByText('Submit'));
62
-
63
- await waitFor(() => expect(onSubmitDeprecated).toHaveBeenCalledTimes(0));
64
-
65
- const muiInputBase = screen.getByTestId('controlledInputWrapper');
66
-
67
- await waitFor(() => expect(muiInputBase.classList).toContain('Mui-error'));
68
- });
69
-
70
- test('DeprecatedCheck: should not render the error styling if no error is returned', async () => {
71
- const screen = render(
72
- <ControlledForm values={{ controlledInput: undefined }} onSubmit={onSubmitDeprecated}>
73
- <ControlledInput
74
- name="controlledInput"
75
- required="This field is required."
76
- maxLength={{ value: 10, message: 'Too long' }}
77
- inputProps={{
78
- 'data-testid': 'testInput',
79
- }}
80
- />
81
- <Actions />
82
- <SubmittedValues />
83
- </ControlledForm>
84
- );
85
-
86
- const input = screen.getByTestId('testInput');
87
-
88
- fireEvent.change(input, { target: { value: 'Input Text' } });
89
-
90
- fireEvent.click(screen.getByText('Submit'));
91
-
92
- await waitFor(() => expect(onSubmitDeprecated).toHaveBeenCalledTimes(1));
93
-
94
- const result = screen.getByTestId('result');
95
- await waitFor(() => {
96
- const formValues = JSON.parse(result.innerHTML).controlledInput;
97
- expect(formValues).toBe('Input Text');
98
- });
99
- });
100
-
101
8
  test('should render the error styling if an error is returned', async () => {
102
9
  const screen = render(
103
10
  <TestForm UseFormOptions={{values: { controlledInput: undefined }}} onSubmit={onSubmit}>
package/src/lib/Input.tsx CHANGED
@@ -1,39 +1,19 @@
1
1
  import { Input, InputProps } from '@availity/mui-form-utils';
2
2
  import { RegisterOptions, FieldValues, Controller } from 'react-hook-form';
3
- import { ControllerProps, DeprecatedRulesProps } from './Types';
3
+ import { ControllerProps } from './Types';
4
4
 
5
- export type ControlledInputProps = Omit<InputProps,
6
- 'onBlur' | 'onChange' | 'value' | 'name' | 'required'
7
- > & Pick<RegisterOptions<FieldValues, string>,
8
- 'onBlur' | 'onChange' | 'value'
9
- > & ControllerProps
10
- //TODO v1 - remove deprecated props
11
- & Omit<DeprecatedRulesProps, 'required'> & {
12
- /** If `true`, will add `aria-required` to `input`.
13
- *
14
- * @deprecated There has been a collision of properties. The boolean value
15
- * to mark the input as required will remain in future versions, but the
16
- * required object for `react-hook-form` has been moved to the `rules` prop.
17
- */
18
- required?: boolean | RegisterOptions['required'];
19
- };
5
+ export type ControlledInputProps = Omit<InputProps, 'onBlur' | 'onChange' | 'value' | 'name'> &
6
+ Pick<RegisterOptions<FieldValues, string>, 'onBlur' | 'onChange' | 'value'> &
7
+ ControllerProps;
20
8
 
21
9
  export const ControlledInput = ({
22
10
  name,
23
11
  defaultValue,
24
- deps,
25
12
  disabled,
26
- max,
27
- maxLength,
28
- min,
29
- minLength,
30
13
  onBlur,
31
14
  onChange,
32
- pattern,
33
- required,
34
15
  rules = {},
35
16
  shouldUnregister,
36
- validate,
37
17
  value,
38
18
  ...rest
39
19
  }: ControlledInputProps) => {
@@ -43,27 +23,19 @@ export const ControlledInput = ({
43
23
  defaultValue={defaultValue}
44
24
  disabled={disabled}
45
25
  rules={{
46
- required: typeof required === 'boolean' ? undefined : required,
47
- maxLength,
48
- minLength,
49
- max,
50
- min,
51
- pattern,
52
- validate,
53
26
  onChange,
54
27
  onBlur,
55
28
  value,
56
29
  shouldUnregister,
57
- deps,
58
30
  ...rules,
59
31
  }}
60
32
  shouldUnregister={shouldUnregister}
61
33
  render={({ field, fieldState: { error } }) => (
62
34
  <Input
35
+ required={typeof rules.required === 'object' ? rules.required.value : !!rules.required}
63
36
  {...rest}
64
37
  {...field}
65
38
  error={!!error}
66
- required={!!required}
67
39
  />
68
40
  )}
69
41
  />
@@ -32,15 +32,15 @@ describe('ControlledOrganizationAutocomplete', () => {
32
32
  const screen = render(
33
33
  <QueryClientProvider client={client}>
34
34
  <TestForm onSubmit={onSubmit}>
35
- <ControlledOrganizationAutocomplete
36
- name="controlledOrganizationAutocomplete"
37
- FieldProps={{
38
- label: 'Organization Select',
39
- helperText: 'Select an Organization from the list',
40
- placeholder: 'Select...',
41
- fullWidth: false,
42
- }}
43
- />
35
+ <ControlledOrganizationAutocomplete
36
+ name="controlledOrganizationAutocomplete"
37
+ FieldProps={{
38
+ label: 'Organization Select',
39
+ helperText: 'Select an Organization from the list',
40
+ placeholder: 'Select...',
41
+ fullWidth: false,
42
+ }}
43
+ />
44
44
  </TestForm>
45
45
  </QueryClientProvider>
46
46
  );
@@ -56,15 +56,15 @@ describe('ControlledOrganizationAutocomplete', () => {
56
56
  const screen = render(
57
57
  <QueryClientProvider client={client}>
58
58
  <TestForm onSubmit={onSubmit}>
59
- <ControlledOrganizationAutocomplete
60
- name="controlledOrganizationAutocomplete"
61
- FieldProps={{
62
- label: 'Organization Select',
63
- helperText: 'Select an Organization from the list',
64
- placeholder: 'Select...',
65
- fullWidth: false,
66
- }}
67
- />
59
+ <ControlledOrganizationAutocomplete
60
+ name="controlledOrganizationAutocomplete"
61
+ FieldProps={{
62
+ label: 'Organization Select',
63
+ helperText: 'Select an Organization from the list',
64
+ placeholder: 'Select...',
65
+ fullWidth: false,
66
+ }}
67
+ />
68
68
  </TestForm>
69
69
  </QueryClientProvider>
70
70
  );
@@ -88,4 +88,71 @@ describe('ControlledOrganizationAutocomplete', () => {
88
88
  expect(controlledCodesAutocompleteValue.id).toBeDefined();
89
89
  });
90
90
  });
91
+
92
+ describe('when using rules', () => {
93
+ describe('when required', () => {
94
+ test('should indicate it is required when passing a string', async () => {
95
+ const screen = render(
96
+ <QueryClientProvider client={client}>
97
+ <TestForm onSubmit={onSubmit}>
98
+ <ControlledOrganizationAutocomplete
99
+ name="controlledOrganizationAutocomplete"
100
+ FieldProps={{
101
+ label: 'Organization Select',
102
+ helperText: 'Select an Organization from the list',
103
+ placeholder: 'Select...',
104
+ fullWidth: false,
105
+ }}
106
+ rules={{ required: 'This field is required' }}
107
+ />
108
+ </TestForm>
109
+ </QueryClientProvider>
110
+ );
111
+
112
+ expect(screen.getByText('*')).toBeDefined();
113
+ });
114
+
115
+ test('should indicate it is required when passing an object with true', async () => {
116
+ const screen = render(
117
+ <QueryClientProvider client={client}>
118
+ <TestForm onSubmit={onSubmit}>
119
+ <ControlledOrganizationAutocomplete
120
+ name="controlledOrganizationAutocomplete"
121
+ FieldProps={{
122
+ label: 'Organization Select',
123
+ helperText: 'Select an Organization from the list',
124
+ placeholder: 'Select...',
125
+ fullWidth: false,
126
+ }}
127
+ rules={{ required: { value: true, message: 'This field is required' } }}
128
+ />
129
+ </TestForm>
130
+ </QueryClientProvider>
131
+ );
132
+
133
+ expect(screen.getByText('*')).toBeDefined();
134
+ });
135
+
136
+ test('should not indicate it is required when passing an object with false', async () => {
137
+ const screen = render(
138
+ <QueryClientProvider client={client}>
139
+ <TestForm onSubmit={onSubmit}>
140
+ <ControlledOrganizationAutocomplete
141
+ name="controlledOrganizationAutocomplete"
142
+ FieldProps={{
143
+ label: 'Organization Select',
144
+ helperText: 'Select an Organization from the list',
145
+ placeholder: 'Select...',
146
+ fullWidth: false,
147
+ }}
148
+ rules={{ required: { value: false, message: 'This field is required' } }}
149
+ />
150
+ </TestForm>
151
+ </QueryClientProvider>
152
+ );
153
+
154
+ expect(screen.queryByText('*')).toBeNull();
155
+ });
156
+ });
157
+ });
91
158
  });
@@ -1,27 +1,18 @@
1
1
  import { OrganizationAutocomplete, OrgAutocompleteProps } from '@availity/mui-autocomplete';
2
2
  import { Controller, RegisterOptions, FieldValues } from 'react-hook-form';
3
- import { ControllerProps, DeprecatedRulesProps } from './Types';
3
+ import { ControllerProps } from './Types';
4
4
 
5
- export type ControlledOrgAutocompleteProps = Omit<OrgAutocompleteProps,
6
- 'onBlur' | 'onChange' | 'value' | 'name'
7
- > & Pick<RegisterOptions<FieldValues, string>,
8
- 'onBlur' | 'onChange' | 'value'
9
- > & ControllerProps
10
- //TODO v1 - remove deprecated props
11
- & Omit<DeprecatedRulesProps, 'max' | 'maxLength' | 'min' | 'minLength'
12
- >;
5
+ export type ControlledOrgAutocompleteProps = Omit<OrgAutocompleteProps, 'onBlur' | 'onChange' | 'value' | 'name'> &
6
+ Pick<RegisterOptions<FieldValues, string>, 'onBlur' | 'onChange' | 'value'> &
7
+ ControllerProps;
13
8
 
14
9
  export const ControlledOrganizationAutocomplete = ({
15
10
  name,
16
11
  defaultValue,
17
- deps,
18
12
  onBlur,
19
13
  onChange,
20
- pattern,
21
- required,
22
14
  rules = {},
23
15
  shouldUnregister,
24
- validate,
25
16
  value,
26
17
  FieldProps,
27
18
  ...rest
@@ -31,13 +22,9 @@ export const ControlledOrganizationAutocomplete = ({
31
22
  name={name}
32
23
  defaultValue={defaultValue}
33
24
  rules={{
34
- deps,
35
25
  onBlur,
36
26
  onChange,
37
- pattern,
38
- required,
39
27
  shouldUnregister,
40
- validate,
41
28
  value,
42
29
  ...rules,
43
30
  }}
@@ -46,6 +33,7 @@ export const ControlledOrganizationAutocomplete = ({
46
33
  <OrganizationAutocomplete
47
34
  {...rest}
48
35
  FieldProps={{
36
+ required: typeof rules.required === 'object' ? rules.required.value : !!rules.required,
49
37
  ...FieldProps,
50
38
  error: !!error,
51
39
  helperText: error?.message ? (
@@ -57,7 +45,7 @@ export const ControlledOrganizationAutocomplete = ({
57
45
  ) : (
58
46
  FieldProps?.helperText
59
47
  ),
60
- inputRef:ref
48
+ inputRef: ref,
61
49
  }}
62
50
  onChange={(event, value, reason) => {
63
51
  if (reason === 'clear') {