@availity/mui-controlled-form 0.2.6 → 0.3.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.
- package/CHANGELOG.md +7 -0
- package/README.md +2 -2
- package/dist/index.d.mts +122 -26
- package/dist/index.d.ts +122 -26
- package/dist/index.js +174 -132
- package/dist/index.mjs +174 -131
- package/package.json +2 -2
- package/src/index.ts +47 -0
- package/src/lib/AsyncAutocomplete.stories.tsx +21 -36
- package/src/lib/AsyncAutocomplete.test.tsx +17 -53
- package/src/lib/AsyncAutocomplete.tsx +13 -6
- package/src/lib/Autocomplete.stories.tsx +20 -33
- package/src/lib/Autocomplete.test.tsx +7 -37
- package/src/lib/Autocomplete.tsx +21 -16
- package/src/lib/Checkbox.stories.tsx +48 -46
- package/src/lib/Checkbox.test.tsx +14 -46
- package/src/lib/Checkbox.tsx +19 -16
- package/src/lib/CodesAutocomplete.stories.tsx +21 -35
- package/src/lib/CodesAutocomplete.test.tsx +20 -54
- package/src/lib/CodesAutocomplete.tsx +13 -6
- package/src/lib/ControlledForm.stories.tsx +1 -0
- package/src/lib/ControlledForm.tsx +7 -3
- package/src/lib/Datepicker.stories.tsx +19 -32
- package/src/lib/Datepicker.test.tsx +3 -35
- package/src/lib/Datepicker.tsx +18 -8
- package/src/lib/Input.stories.tsx +32 -38
- package/src/lib/Input.test.tsx +71 -7
- package/src/lib/Input.tsx +30 -15
- package/src/lib/OrganizationAutocomplete.stories.tsx +30 -35
- package/src/lib/OrganizationAutocomplete.test.tsx +23 -57
- package/src/lib/OrganizationAutocomplete.tsx +14 -9
- package/src/lib/ProviderAutocomplete.stories.tsx +20 -35
- package/src/lib/ProviderAutocomplete.test.tsx +29 -63
- package/src/lib/ProviderAutocomplete.tsx +11 -5
- package/src/lib/RadioGroup.stories.tsx +41 -36
- package/src/lib/RadioGroup.test.tsx +3 -35
- package/src/lib/RadioGroup.tsx +31 -20
- package/src/lib/Select.stories.tsx +66 -93
- package/src/lib/Select.test.tsx +8 -36
- package/src/lib/Select.tsx +30 -15
- package/src/lib/TextField.stories.tsx +26 -39
- package/src/lib/TextField.test.tsx +71 -5
- package/src/lib/TextField.tsx +32 -17
- package/src/lib/Types.tsx +2489 -0
- package/src/lib/UtilComponents.tsx +52 -0
- package/docs/propDefinitions.tsx +0 -31
package/src/index.ts
CHANGED
|
@@ -10,3 +10,50 @@ export * from './lib/ProviderAutocomplete';
|
|
|
10
10
|
export * from './lib/RadioGroup';
|
|
11
11
|
export * from './lib/Select';
|
|
12
12
|
export * from './lib/TextField';
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
FormProvider,
|
|
16
|
+
/** Custom react-hook-form hook to manage the entire form.
|
|
17
|
+
*
|
|
18
|
+
* ```tsx
|
|
19
|
+
* function App() {
|
|
20
|
+
* const { handleSubmit, methods, formState: { isSubmitting } } = useForm();
|
|
21
|
+
* const schema = yup.object({
|
|
22
|
+
* textField: yup
|
|
23
|
+
* .string(),
|
|
24
|
+
* requiredTextField: yup
|
|
25
|
+
* .string()
|
|
26
|
+
* .required('This field is required.'),
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* type FormInputsType = yup.InferType<typeof schema>;
|
|
30
|
+
*
|
|
31
|
+
* const onSubmit: SubmitHandler<FormInputsType> = (data) => console.log(data)
|
|
32
|
+
*
|
|
33
|
+
* const defaultValues = {
|
|
34
|
+
* textField: "",
|
|
35
|
+
* requiredTextField: ""
|
|
36
|
+
* }
|
|
37
|
+
*
|
|
38
|
+
* return (
|
|
39
|
+
* <FormProvider {...methods}>
|
|
40
|
+
* <form onSubmit={methods.handleSubmit(onSubmit)}>
|
|
41
|
+
* <TextField name="textField" />
|
|
42
|
+
* <TextField name="requiredTextField" />
|
|
43
|
+
* <LoadingButton loading={isSubmitting}>Submit</LoadingButton>
|
|
44
|
+
* </form>
|
|
45
|
+
* </FormProvider>
|
|
46
|
+
* );
|
|
47
|
+
* }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
useForm,
|
|
51
|
+
useFormContext
|
|
52
|
+
} from 'react-hook-form';
|
|
53
|
+
|
|
54
|
+
export type {
|
|
55
|
+
SubmitHandler,
|
|
56
|
+
/** react-hook-form `useForm` options, like `defaultValues` and validation `mode`. For more information see the [react-hook-form useForm docs](https://react-hook-form.com/docs/useform) */
|
|
57
|
+
UseFormProps,
|
|
58
|
+
UseFormReturn,
|
|
59
|
+
} from 'react-hook-form';
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
import { ControlledAsyncAutocomplete } from './AsyncAutocomplete';
|
|
3
|
-
import { ControlledForm } from './ControlledForm';
|
|
4
3
|
import { Button } from '@availity/mui-button';
|
|
5
|
-
import {
|
|
4
|
+
import { FormProvider, useForm } from 'react-hook-form';
|
|
6
5
|
import { Paper } from '@availity/mui-paper';
|
|
7
6
|
import { Typography } from '@availity/mui-typography';
|
|
8
7
|
import { Grid } from '@availity/mui-layout';
|
|
9
8
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
10
9
|
import AvApi, { ApiConfig } from '@availity/api-axios';
|
|
11
|
-
import {
|
|
10
|
+
import { AllControllerPropertiesCategorized, AsyncAutocompletePropsCategorized } from './Types';
|
|
12
11
|
|
|
13
12
|
const meta: Meta<typeof ControlledAsyncAutocomplete> = {
|
|
14
13
|
title: 'Form Components/Controlled Form/Autocomplete/ControlledAsyncAutocomplete',
|
|
15
14
|
component: ControlledAsyncAutocomplete,
|
|
16
15
|
tags: ['autodocs'],
|
|
17
|
-
argTypes:
|
|
16
|
+
argTypes: {...AllControllerPropertiesCategorized, ...AsyncAutocompletePropsCategorized}
|
|
18
17
|
};
|
|
19
18
|
|
|
20
19
|
export default meta;
|
|
@@ -65,49 +64,35 @@ const client = new QueryClient({
|
|
|
65
64
|
|
|
66
65
|
export const _ControlledAsyncAutoComplete: StoryObj<typeof ControlledAsyncAutocomplete> = {
|
|
67
66
|
render: (args) => {
|
|
68
|
-
const
|
|
69
|
-
const {
|
|
70
|
-
getValues,
|
|
71
|
-
formState: { isSubmitSuccessful },
|
|
72
|
-
} = useFormContext();
|
|
67
|
+
const methods = useForm();
|
|
73
68
|
|
|
74
|
-
return isSubmitSuccessful ? (
|
|
75
|
-
<Paper sx={{ padding: '1.5rem', marginTop: '1.5rem' }}>
|
|
76
|
-
<Typography variant="h2">Submitted Values</Typography>
|
|
77
|
-
<pre>{JSON.stringify(getValues(), null, 2)}</pre>
|
|
78
|
-
</Paper>
|
|
79
|
-
) : null;
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const Actions = () => {
|
|
83
|
-
const {
|
|
84
|
-
reset,
|
|
85
|
-
formState: { isSubmitSuccessful },
|
|
86
|
-
} = useFormContext();
|
|
87
|
-
return (
|
|
88
|
-
<Grid container direction="row" justifyContent="space-between" marginTop={1}>
|
|
89
|
-
<Button disabled={!isSubmitSuccessful} children="Reset" color="secondary" onClick={() => reset()} />
|
|
90
|
-
<Button type="submit" disabled={isSubmitSuccessful} children="Submit" />
|
|
91
|
-
</Grid>
|
|
92
|
-
);
|
|
93
|
-
};
|
|
94
69
|
return (
|
|
95
70
|
<QueryClientProvider client={client}>
|
|
96
|
-
<
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
71
|
+
<FormProvider {...methods}>
|
|
72
|
+
<form onSubmit={methods.handleSubmit((data) => data)}>
|
|
73
|
+
<ControlledAsyncAutocomplete {...args} />
|
|
74
|
+
<Grid container direction="row" justifyContent="space-between" marginTop={1}>
|
|
75
|
+
<Button disabled={!methods?.formState?.isSubmitSuccessful} children="Reset" color="secondary" onClick={() => methods.reset()} />
|
|
76
|
+
<Button type="submit" disabled={methods?.formState?.isSubmitSuccessful} children="Submit" />
|
|
77
|
+
</Grid>
|
|
78
|
+
{ methods?.formState?.isSubmitSuccessful ? (
|
|
79
|
+
<Paper sx={{ padding: '1.5rem', marginTop: '1.5rem' }}>
|
|
80
|
+
<Typography variant="h2">Submitted Values</Typography>
|
|
81
|
+
<pre data-testid="result">{JSON.stringify(methods.getValues(), null, 2)}</pre>
|
|
82
|
+
</Paper>
|
|
83
|
+
) : null }
|
|
84
|
+
</form>
|
|
85
|
+
</FormProvider>
|
|
101
86
|
</QueryClientProvider>
|
|
102
87
|
);
|
|
103
88
|
},
|
|
104
89
|
args: {
|
|
105
90
|
name: 'controlledAsyncAutocomplete',
|
|
106
|
-
FieldProps: { label: 'Async Select', helperText: 'Helper Text', fullWidth: false },
|
|
91
|
+
FieldProps: { label: 'Async Select', helperText: 'Helper Text', fullWidth: false, required: true },
|
|
107
92
|
getOptionLabel: (val: Option) => val.label,
|
|
108
93
|
loadOptions,
|
|
109
94
|
limit: 10,
|
|
110
95
|
queryKey: 'example',
|
|
111
|
-
required:
|
|
96
|
+
rules: { required:'This is required.' },
|
|
112
97
|
},
|
|
113
98
|
};
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
import { fireEvent, render, waitFor } from '@testing-library/react';
|
|
2
2
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
3
|
-
import { Paper } from '@availity/mui-paper';
|
|
4
|
-
import { Typography } from '@availity/mui-typography';
|
|
5
|
-
import { useFormContext } from 'react-hook-form';
|
|
6
|
-
import { Grid } from '@availity/mui-layout';
|
|
7
|
-
import { Button } from '@availity/mui-button';
|
|
8
3
|
import AvApi, { ApiConfig } from '@availity/api-axios';
|
|
9
4
|
// eslint-disable-next-line @nx/enforce-module-boundaries
|
|
10
5
|
import { server } from '@availity/mock/src/lib/server';
|
|
11
|
-
import { ControlledForm } from './ControlledForm';
|
|
12
6
|
import { ControlledAsyncAutocomplete } from './AsyncAutocomplete';
|
|
7
|
+
import { TestForm } from './UtilComponents';
|
|
13
8
|
|
|
14
9
|
const api = new AvApi({ name: 'example' } as ApiConfig);
|
|
15
10
|
|
|
@@ -47,33 +42,6 @@ const loadOptions = async (offset: number, limit: number) => {
|
|
|
47
42
|
};
|
|
48
43
|
};
|
|
49
44
|
|
|
50
|
-
const SubmittedValues = () => {
|
|
51
|
-
const {
|
|
52
|
-
getValues,
|
|
53
|
-
formState: { isSubmitSuccessful },
|
|
54
|
-
} = useFormContext();
|
|
55
|
-
|
|
56
|
-
return isSubmitSuccessful ? (
|
|
57
|
-
<Paper sx={{ padding: '1.5rem', marginTop: '1.5rem' }}>
|
|
58
|
-
<Typography variant="h2">Submitted Values</Typography>
|
|
59
|
-
<pre data-testid="result">{JSON.stringify(getValues(), null, 2)}</pre>
|
|
60
|
-
</Paper>
|
|
61
|
-
) : null;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const Actions = () => {
|
|
65
|
-
const {
|
|
66
|
-
reset,
|
|
67
|
-
formState: { isSubmitSuccessful },
|
|
68
|
-
} = useFormContext();
|
|
69
|
-
return (
|
|
70
|
-
<Grid container direction="row" justifyContent="space-between">
|
|
71
|
-
<Button disabled={!isSubmitSuccessful} children="Reset" color="secondary" onClick={() => reset()} />
|
|
72
|
-
<Button type="submit" disabled={isSubmitSuccessful} children="Submit" />
|
|
73
|
-
</Grid>
|
|
74
|
-
);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
45
|
const onSubmit = jest.fn();
|
|
78
46
|
|
|
79
47
|
describe('ControlledAsyncAutocomplete', () => {
|
|
@@ -98,20 +66,18 @@ describe('ControlledAsyncAutocomplete', () => {
|
|
|
98
66
|
});
|
|
99
67
|
|
|
100
68
|
test('should loadOptions successfully', async () => {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
<SubmittedValues />
|
|
114
|
-
</ControlledForm>
|
|
69
|
+
const screen = render(
|
|
70
|
+
<QueryClientProvider client={client}>
|
|
71
|
+
<TestForm UseFormOptions={{values: { controlledAutocomplete: undefined }}} onSubmit={onSubmit}>
|
|
72
|
+
<ControlledAsyncAutocomplete
|
|
73
|
+
name="controlledAsyncAutocomplete"
|
|
74
|
+
FieldProps={{ label: 'Async Select', helperText: 'Helper Text', fullWidth: false }}
|
|
75
|
+
getOptionLabel={(val: Option) => val.label}
|
|
76
|
+
loadOptions={loadOptions}
|
|
77
|
+
limit={10}
|
|
78
|
+
queryKey="example"
|
|
79
|
+
/>
|
|
80
|
+
</TestForm>
|
|
115
81
|
</QueryClientProvider>
|
|
116
82
|
);
|
|
117
83
|
|
|
@@ -125,7 +91,7 @@ describe('ControlledAsyncAutocomplete', () => {
|
|
|
125
91
|
test('should set the value and submit the form data', async () => {
|
|
126
92
|
const screen = render(
|
|
127
93
|
<QueryClientProvider client={client}>
|
|
128
|
-
<
|
|
94
|
+
<TestForm UseFormOptions={{values: { controlledAutocomplete: undefined }}} onSubmit={onSubmit}>
|
|
129
95
|
<ControlledAsyncAutocomplete
|
|
130
96
|
name="controlledAsyncAutocomplete"
|
|
131
97
|
FieldProps={{ label: 'Async Select', helperText: 'Helper Text', fullWidth: false }}
|
|
@@ -134,11 +100,9 @@ describe('ControlledAsyncAutocomplete', () => {
|
|
|
134
100
|
limit={10}
|
|
135
101
|
queryKey="example"
|
|
136
102
|
/>
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
</QueryClientProvider>
|
|
141
|
-
);
|
|
103
|
+
</TestForm>
|
|
104
|
+
</QueryClientProvider>
|
|
105
|
+
);
|
|
142
106
|
|
|
143
107
|
const dropdown = screen.getByRole('combobox');
|
|
144
108
|
fireEvent.click(dropdown);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AsyncAutocomplete, AsyncAutocompleteProps } from '@availity/mui-autocomplete';
|
|
2
|
-
import { RegisterOptions, FieldValues, Controller
|
|
2
|
+
import { RegisterOptions, FieldValues, Controller } from 'react-hook-form';
|
|
3
3
|
import { ChipTypeMap } from '@mui/material/Chip';
|
|
4
|
+
import { ControllerProps, DeprecatedRulesProps } from './Types';
|
|
4
5
|
|
|
5
6
|
export type ControlledAsyncAutocompleteProps<
|
|
6
7
|
Option,
|
|
@@ -8,9 +9,13 @@ export type ControlledAsyncAutocompleteProps<
|
|
|
8
9
|
DisableClearable extends boolean | undefined,
|
|
9
10
|
FreeSolo extends boolean | undefined,
|
|
10
11
|
ChipComponent extends React.ElementType = ChipTypeMap['defaultComponent'],
|
|
11
|
-
> = Omit<AsyncAutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>,
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
> = Omit<AsyncAutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>,
|
|
13
|
+
'onBlur' | 'onChange' | 'value' | 'name'
|
|
14
|
+
> & Pick<RegisterOptions<FieldValues, string>,
|
|
15
|
+
'onBlur' | 'onChange' | 'value'
|
|
16
|
+
> & ControllerProps
|
|
17
|
+
//TODO v1 - remove deprecated props
|
|
18
|
+
& DeprecatedRulesProps;
|
|
14
19
|
|
|
15
20
|
export const ControlledAsyncAutocomplete = <
|
|
16
21
|
Option,
|
|
@@ -29,6 +34,7 @@ export const ControlledAsyncAutocomplete = <
|
|
|
29
34
|
onChange,
|
|
30
35
|
pattern,
|
|
31
36
|
required,
|
|
37
|
+
rules = {},
|
|
32
38
|
shouldUnregister,
|
|
33
39
|
validate,
|
|
34
40
|
value,
|
|
@@ -52,14 +58,14 @@ export const ControlledAsyncAutocomplete = <
|
|
|
52
58
|
shouldUnregister,
|
|
53
59
|
validate,
|
|
54
60
|
value,
|
|
61
|
+
...rules,
|
|
55
62
|
}}
|
|
56
63
|
shouldUnregister={shouldUnregister}
|
|
57
|
-
render={({ field: { onChange, value, onBlur }, fieldState: { error } }) => (
|
|
64
|
+
render={({ field: { onChange, value, onBlur, ref }, fieldState: { error } }) => (
|
|
58
65
|
<AsyncAutocomplete
|
|
59
66
|
{...rest}
|
|
60
67
|
FieldProps={{
|
|
61
68
|
...FieldProps,
|
|
62
|
-
required: typeof required === 'object' ? required.value : !!required,
|
|
63
69
|
error: !!error,
|
|
64
70
|
helperText: error?.message ? (
|
|
65
71
|
<>
|
|
@@ -70,6 +76,7 @@ export const ControlledAsyncAutocomplete = <
|
|
|
70
76
|
) : (
|
|
71
77
|
FieldProps?.helperText
|
|
72
78
|
),
|
|
79
|
+
inputRef:ref
|
|
73
80
|
}}
|
|
74
81
|
onChange={(event, value, reason) => {
|
|
75
82
|
if (reason === 'clear') {
|
|
@@ -1,60 +1,47 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
import { ControlledAutocomplete } from './Autocomplete';
|
|
3
|
-
import { ControlledForm } from './ControlledForm';
|
|
4
3
|
import { Button } from '@availity/mui-button';
|
|
5
|
-
import {
|
|
4
|
+
import { FormProvider, useForm } from '..';
|
|
6
5
|
import { Paper } from '@availity/mui-paper';
|
|
7
6
|
import { Typography } from '@availity/mui-typography';
|
|
8
7
|
import { Grid } from '@availity/mui-layout';
|
|
8
|
+
import { AllControllerPropertiesCategorized, AutocompletePropsCategorized } from './Types';
|
|
9
9
|
|
|
10
10
|
const meta: Meta<typeof ControlledAutocomplete> = {
|
|
11
11
|
title: 'Form Components/Controlled Form/Autocomplete/ControlledAutocomplete',
|
|
12
12
|
component: ControlledAutocomplete,
|
|
13
13
|
tags: ['autodocs'],
|
|
14
|
+
argTypes: {...AllControllerPropertiesCategorized, ...AutocompletePropsCategorized}
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
export default meta;
|
|
17
18
|
|
|
18
19
|
export const _ControlledAutoComplete: StoryObj<typeof ControlledAutocomplete> = {
|
|
19
20
|
render: (args) => {
|
|
20
|
-
const
|
|
21
|
-
const {
|
|
22
|
-
getValues,
|
|
23
|
-
formState: { isSubmitSuccessful },
|
|
24
|
-
} = useFormContext();
|
|
21
|
+
const methods = useForm();
|
|
25
22
|
|
|
26
|
-
return isSubmitSuccessful ? (
|
|
27
|
-
<Paper sx={{ padding: '1.5rem', marginTop: '1.5rem' }}>
|
|
28
|
-
<Typography variant="h2">Submitted Values</Typography>
|
|
29
|
-
<pre>{JSON.stringify(getValues(), null, 2)}</pre>
|
|
30
|
-
</Paper>
|
|
31
|
-
) : null;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const Actions = () => {
|
|
35
|
-
const {
|
|
36
|
-
reset,
|
|
37
|
-
formState: { isSubmitSuccessful },
|
|
38
|
-
} = useFormContext();
|
|
39
|
-
return (
|
|
40
|
-
<Grid container direction="row" justifyContent="space-between" marginTop={1}>
|
|
41
|
-
<Button disabled={!isSubmitSuccessful} children="Reset" color="secondary" onClick={() => reset()} />
|
|
42
|
-
<Button type="submit" disabled={isSubmitSuccessful} children="Submit" />
|
|
43
|
-
</Grid>
|
|
44
|
-
);
|
|
45
|
-
};
|
|
46
23
|
return (
|
|
47
|
-
<
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
24
|
+
<FormProvider {...methods}>
|
|
25
|
+
<form onSubmit={methods.handleSubmit((data) => data)}>
|
|
26
|
+
<ControlledAutocomplete {...args} />
|
|
27
|
+
<Grid container direction="row" justifyContent="space-between" marginTop={1}>
|
|
28
|
+
<Button disabled={!methods?.formState?.isSubmitSuccessful} children="Reset" color="secondary" onClick={() => methods.reset()} />
|
|
29
|
+
<Button type="submit" disabled={methods?.formState?.isSubmitSuccessful} children="Submit" />
|
|
30
|
+
</Grid>
|
|
31
|
+
{ methods?.formState?.isSubmitSuccessful ? (
|
|
32
|
+
<Paper sx={{ padding: '1.5rem', marginTop: '1.5rem' }}>
|
|
33
|
+
<Typography variant="h2">Submitted Values</Typography>
|
|
34
|
+
<pre data-testid="result">{JSON.stringify(methods.getValues(), null, 2)}</pre>
|
|
35
|
+
</Paper>
|
|
36
|
+
) : null }
|
|
37
|
+
</form>
|
|
38
|
+
</FormProvider>
|
|
52
39
|
);
|
|
53
40
|
},
|
|
54
41
|
args: {
|
|
55
42
|
name: 'controlledAutocomplete',
|
|
56
43
|
options: ['Option 1', 'Option 2'],
|
|
57
|
-
required: 'This is required.',
|
|
44
|
+
rules: { required: 'This is required.' },
|
|
58
45
|
FieldProps: { label: 'Autocomplete Label' },
|
|
59
46
|
},
|
|
60
47
|
};
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { fireEvent, render, waitFor } from '@testing-library/react';
|
|
2
|
-
import { useFormContext } from 'react-hook-form';
|
|
3
|
-
import { Paper } from '@availity/mui-paper';
|
|
4
|
-
import { Typography } from '@availity/mui-typography';
|
|
5
|
-
import { Grid } from '@availity/mui-layout';
|
|
6
|
-
import { Button } from '@availity/mui-button';
|
|
7
|
-
import { ControlledForm } from './ControlledForm';
|
|
8
2
|
import { ControlledAutocomplete } from './Autocomplete';
|
|
3
|
+
import { TestForm } from './UtilComponents';
|
|
9
4
|
|
|
10
5
|
const onSubmit = jest.fn();
|
|
11
6
|
|
|
@@ -17,38 +12,13 @@ describe('ControlledAsyncAutocomplete', () => {
|
|
|
17
12
|
});
|
|
18
13
|
|
|
19
14
|
test('should set the value and submit the form', async () => {
|
|
20
|
-
const SubmittedValues = () => {
|
|
21
|
-
const {
|
|
22
|
-
getValues,
|
|
23
|
-
formState: { isSubmitSuccessful },
|
|
24
|
-
} = useFormContext();
|
|
25
|
-
|
|
26
|
-
return isSubmitSuccessful ? (
|
|
27
|
-
<Paper sx={{ padding: '1.5rem', marginTop: '1.5rem' }}>
|
|
28
|
-
<Typography variant="h2">Submitted Values</Typography>
|
|
29
|
-
<pre data-testid="result">{JSON.stringify(getValues(), null, 2)}</pre>
|
|
30
|
-
</Paper>
|
|
31
|
-
) : null;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const Actions = () => {
|
|
35
|
-
const {
|
|
36
|
-
reset,
|
|
37
|
-
formState: { isSubmitSuccessful },
|
|
38
|
-
} = useFormContext();
|
|
39
|
-
return (
|
|
40
|
-
<Grid container direction="row" justifyContent="space-between">
|
|
41
|
-
<Button disabled={!isSubmitSuccessful} children="Reset" color="secondary" onClick={() => reset()} />
|
|
42
|
-
<Button type="submit" disabled={isSubmitSuccessful} children="Submit" />
|
|
43
|
-
</Grid>
|
|
44
|
-
);
|
|
45
|
-
};
|
|
46
15
|
const screen = render(
|
|
47
|
-
<
|
|
48
|
-
<ControlledAutocomplete
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
16
|
+
<TestForm UseFormOptions={{values: { controlledAutocomplete: null}}} onSubmit={onSubmit}>
|
|
17
|
+
<ControlledAutocomplete
|
|
18
|
+
name="controlledAutocomplete"
|
|
19
|
+
options={['Option 1', 'Option 2']}
|
|
20
|
+
/>
|
|
21
|
+
</TestForm>
|
|
52
22
|
);
|
|
53
23
|
|
|
54
24
|
const dropdown = screen.getByRole('combobox');
|
package/src/lib/Autocomplete.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Autocomplete, AutocompleteProps } from '@availity/mui-autocomplete';
|
|
2
|
-
import { RegisterOptions, FieldValues, Controller
|
|
2
|
+
import { RegisterOptions, FieldValues, Controller } from 'react-hook-form';
|
|
3
3
|
import { ChipTypeMap } from '@mui/material/Chip';
|
|
4
|
+
import { ControllerProps, DeprecatedRulesProps } from './Types';
|
|
4
5
|
|
|
5
6
|
export type ControlledAutocompleteProps<
|
|
6
7
|
T,
|
|
@@ -9,11 +10,13 @@ export type ControlledAutocompleteProps<
|
|
|
9
10
|
FreeSolo extends boolean | undefined,
|
|
10
11
|
ChipComponent extends React.ElementType = ChipTypeMap['defaultComponent'],
|
|
11
12
|
> = Omit<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
> &
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
AutocompleteProps<T, Multiple, DisableClearable, FreeSolo, ChipComponent>,
|
|
14
|
+
'onBlur' | 'onChange' | 'value' | 'name'
|
|
15
|
+
> & Pick<RegisterOptions<FieldValues, string>,
|
|
16
|
+
'onBlur' | 'onChange' | 'value'
|
|
17
|
+
> & ControllerProps
|
|
18
|
+
//TODO v1 - remove deprecated props
|
|
19
|
+
& DeprecatedRulesProps;
|
|
17
20
|
|
|
18
21
|
export const ControlledAutocomplete = <
|
|
19
22
|
T,
|
|
@@ -25,6 +28,7 @@ export const ControlledAutocomplete = <
|
|
|
25
28
|
name,
|
|
26
29
|
FieldProps,
|
|
27
30
|
defaultValue,
|
|
31
|
+
rules = {},
|
|
28
32
|
deps,
|
|
29
33
|
max,
|
|
30
34
|
maxLength,
|
|
@@ -56,24 +60,25 @@ export const ControlledAutocomplete = <
|
|
|
56
60
|
shouldUnregister,
|
|
57
61
|
validate,
|
|
58
62
|
value,
|
|
63
|
+
...rules,
|
|
59
64
|
}}
|
|
60
65
|
shouldUnregister={shouldUnregister}
|
|
61
|
-
render={({ field: { onChange, value, onBlur }, fieldState: { error } }) => (
|
|
66
|
+
render={({ field: { onChange, value, onBlur, ref }, fieldState: { error } }) => (
|
|
62
67
|
<Autocomplete
|
|
63
68
|
{...rest}
|
|
64
69
|
FieldProps={{
|
|
65
70
|
...FieldProps,
|
|
66
|
-
required: typeof required === 'object' ? required.value : !!required,
|
|
67
71
|
error: !!error,
|
|
68
72
|
helperText: error?.message ? (
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
<>
|
|
74
|
+
{error.message}
|
|
75
|
+
<br />
|
|
76
|
+
{FieldProps?.helperText}
|
|
77
|
+
</>
|
|
78
|
+
) : (
|
|
79
|
+
FieldProps?.helperText
|
|
80
|
+
),
|
|
81
|
+
inputRef: ref
|
|
77
82
|
}}
|
|
78
83
|
onChange={(event, value, reason) => {
|
|
79
84
|
if (reason === 'clear') {
|
|
@@ -1,69 +1,71 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
import { ControlledCheckbox, ControlledCheckboxProps } from './Checkbox';
|
|
3
|
-
import { ControlledForm } from './ControlledForm';
|
|
4
3
|
import { Button } from '@availity/mui-button';
|
|
5
|
-
import {
|
|
4
|
+
import { FormProvider, useForm } from 'react-hook-form';
|
|
6
5
|
import { Paper } from '@availity/mui-paper';
|
|
7
6
|
import { Typography } from '@availity/mui-typography';
|
|
8
7
|
import { FormControlLabel, FormGroup, FormControl, FormLabel } from '@availity/mui-form-utils';
|
|
9
8
|
import { Grid } from '@availity/mui-layout';
|
|
9
|
+
import { AllControllerPropertiesCategorized, CheckboxPropsCategorized } from './Types';
|
|
10
10
|
|
|
11
11
|
const meta: Meta<typeof ControlledCheckbox> = {
|
|
12
12
|
title: 'Form Components/Controlled Form/ControlledCheckbox',
|
|
13
13
|
component: ControlledCheckbox,
|
|
14
14
|
tags: ['autodocs'],
|
|
15
|
+
argTypes: {...AllControllerPropertiesCategorized, ...CheckboxPropsCategorized},
|
|
16
|
+
parameters: {
|
|
17
|
+
controls: {
|
|
18
|
+
exclude: [
|
|
19
|
+
'form',
|
|
20
|
+
'formAction',
|
|
21
|
+
'formEncType',
|
|
22
|
+
'formMethod',
|
|
23
|
+
'formNoValidate',
|
|
24
|
+
'formTarget',
|
|
25
|
+
'key',
|
|
26
|
+
'max',
|
|
27
|
+
'maxLength',
|
|
28
|
+
'min',
|
|
29
|
+
'minLength',
|
|
30
|
+
'pattern',
|
|
31
|
+
'style',
|
|
32
|
+
'tabIndex'
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
15
36
|
};
|
|
16
37
|
|
|
17
38
|
export default meta;
|
|
18
39
|
|
|
19
40
|
export const _ControlledCheckbox: StoryObj<typeof ControlledCheckbox> = {
|
|
20
41
|
render: (args: ControlledCheckboxProps) => {
|
|
21
|
-
const
|
|
22
|
-
const {
|
|
23
|
-
getValues,
|
|
24
|
-
formState: { isSubmitSuccessful },
|
|
25
|
-
} = useFormContext();
|
|
42
|
+
const methods = useForm();
|
|
26
43
|
|
|
27
|
-
return isSubmitSuccessful ? (
|
|
28
|
-
<Paper sx={{ padding: '1.5rem', marginTop: '1.5rem' }}>
|
|
29
|
-
<Typography variant="h2">Submitted Values</Typography>
|
|
30
|
-
<pre>{JSON.stringify(getValues(), null, 2)}</pre>
|
|
31
|
-
</Paper>
|
|
32
|
-
) : null;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const Actions = () => {
|
|
36
|
-
const {
|
|
37
|
-
reset,
|
|
38
|
-
formState: { isSubmitSuccessful },
|
|
39
|
-
} = useFormContext();
|
|
40
|
-
return (
|
|
41
|
-
<Grid container direction="row" justifyContent="space-between" marginTop={1}>
|
|
42
|
-
<Button
|
|
43
|
-
disabled={!isSubmitSuccessful}
|
|
44
|
-
children="Reset"
|
|
45
|
-
color="secondary"
|
|
46
|
-
onClick={() => reset({ [args.name]: false })}
|
|
47
|
-
/>
|
|
48
|
-
<Button type="submit" disabled={isSubmitSuccessful} children="Submit" />
|
|
49
|
-
</Grid>
|
|
50
|
-
);
|
|
51
|
-
};
|
|
52
44
|
return (
|
|
53
|
-
<
|
|
54
|
-
<
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
45
|
+
<FormProvider {...methods}>
|
|
46
|
+
<form onSubmit={methods.handleSubmit((data) => data)}>
|
|
47
|
+
<FormControl>
|
|
48
|
+
<FormLabel id="radio-group" component="div">
|
|
49
|
+
Radio Group
|
|
50
|
+
</FormLabel>
|
|
51
|
+
<FormGroup>
|
|
52
|
+
<FormControlLabel label="Option 1" control={<ControlledCheckbox {...args} />} />
|
|
53
|
+
<FormControlLabel label="Option 2" control={<ControlledCheckbox name="Option 2" />} />
|
|
54
|
+
<FormControlLabel label="Option 3" control={<ControlledCheckbox name="Option 3" />} />
|
|
55
|
+
</FormGroup>
|
|
56
|
+
</FormControl>
|
|
57
|
+
<Grid container direction="row" justifyContent="space-between" marginTop={1}>
|
|
58
|
+
<Button disabled={!methods?.formState?.isSubmitSuccessful} children="Reset" color="secondary" onClick={() => methods.reset()} />
|
|
59
|
+
<Button type="submit" disabled={methods?.formState?.isSubmitSuccessful} children="Submit" />
|
|
60
|
+
</Grid>
|
|
61
|
+
{ methods?.formState?.isSubmitSuccessful ? (
|
|
62
|
+
<Paper sx={{ padding: '1.5rem', marginTop: '1.5rem' }}>
|
|
63
|
+
<Typography variant="h2">Submitted Values</Typography>
|
|
64
|
+
<pre data-testid="result">{JSON.stringify(methods.getValues(), null, 2)}</pre>
|
|
65
|
+
</Paper>
|
|
66
|
+
) : null }
|
|
67
|
+
</form>
|
|
68
|
+
</FormProvider>
|
|
67
69
|
);
|
|
68
70
|
},
|
|
69
71
|
args: {
|