@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
|
@@ -1,57 +1,25 @@
|
|
|
1
1
|
import { render, fireEvent, waitFor } from '@testing-library/react';
|
|
2
2
|
import { FormControl, FormLabel, FormControlLabel, FormGroup } from '@availity/mui-form-utils';
|
|
3
|
-
import { useFormContext } from 'react-hook-form';
|
|
4
|
-
import { Paper } from '@availity/mui-paper';
|
|
5
|
-
import { Typography } from '@availity/mui-typography';
|
|
6
|
-
import { Grid } from '@availity/mui-layout';
|
|
7
|
-
import { Button } from '@availity/mui-button';
|
|
8
|
-
import { ControlledForm } from './ControlledForm';
|
|
9
3
|
import { ControlledCheckbox } from './Checkbox';
|
|
10
|
-
|
|
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
|
-
};
|
|
4
|
+
import { TestForm } from './UtilComponents';
|
|
35
5
|
|
|
36
6
|
const onSubmit = jest.fn();
|
|
37
7
|
|
|
38
8
|
describe('ControlledCheckbox', () => {
|
|
39
9
|
test('should set the value and submit the form data', async () => {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<SubmittedValues />
|
|
54
|
-
</ControlledForm>
|
|
10
|
+
const screen = render(
|
|
11
|
+
<TestForm onSubmit={onSubmit} UseFormOptions={{values: { option1: undefined, option2: undefined, option3: undefined }}}>
|
|
12
|
+
<FormControl>
|
|
13
|
+
<FormLabel id="radio-group" component="div">
|
|
14
|
+
Radio Group
|
|
15
|
+
</FormLabel>
|
|
16
|
+
<FormGroup>
|
|
17
|
+
<FormControlLabel label="Option 1" control={<ControlledCheckbox name="option1" />} />
|
|
18
|
+
<FormControlLabel label="Option 2" control={<ControlledCheckbox name="option2" />} />
|
|
19
|
+
<FormControlLabel label="Option 3" control={<ControlledCheckbox name="option3" />} />
|
|
20
|
+
</FormGroup>
|
|
21
|
+
</FormControl>
|
|
22
|
+
</TestForm>
|
|
55
23
|
);
|
|
56
24
|
|
|
57
25
|
const option1 = screen.getByText('Option 1');
|
package/src/lib/Checkbox.tsx
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
import { Checkbox, CheckboxProps } from '@availity/mui-checkbox';
|
|
2
|
-
import { RegisterOptions, FieldValues, Controller
|
|
2
|
+
import { RegisterOptions, FieldValues, Controller } from 'react-hook-form';
|
|
3
|
+
import { ControllerProps, DeprecatedRulesProps } from './Types';
|
|
3
4
|
|
|
4
|
-
export type ControlledCheckboxProps = CheckboxProps
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
> &
|
|
18
|
-
Pick<ControllerProps, 'defaultValue' | 'shouldUnregister' | 'name'>;
|
|
5
|
+
export type ControlledCheckboxProps = Omit<CheckboxProps,
|
|
6
|
+
'disabled' | 'onBlur' | 'onChange' | 'value' | 'name'
|
|
7
|
+
> & Pick<RegisterOptions<FieldValues, string>,
|
|
8
|
+
'disabled' | 'onBlur' | 'onChange' | 'value'
|
|
9
|
+
> & ControllerProps
|
|
10
|
+
//TODO v1 - remove deprecated props
|
|
11
|
+
& Omit<DeprecatedRulesProps,
|
|
12
|
+
| 'required'
|
|
13
|
+
| 'max'
|
|
14
|
+
| 'maxLength'
|
|
15
|
+
| 'min'
|
|
16
|
+
| 'minLength'
|
|
17
|
+
| 'pattern'>;
|
|
19
18
|
|
|
20
19
|
export const ControlledCheckbox = ({
|
|
21
20
|
name,
|
|
@@ -24,8 +23,10 @@ export const ControlledCheckbox = ({
|
|
|
24
23
|
onBlur,
|
|
25
24
|
value,
|
|
26
25
|
defaultValue = false,
|
|
26
|
+
rules = {},
|
|
27
27
|
shouldUnregister,
|
|
28
28
|
deps,
|
|
29
|
+
validate,
|
|
29
30
|
...rest
|
|
30
31
|
}: ControlledCheckboxProps) => {
|
|
31
32
|
return (
|
|
@@ -39,6 +40,8 @@ export const ControlledCheckbox = ({
|
|
|
39
40
|
value,
|
|
40
41
|
shouldUnregister,
|
|
41
42
|
deps,
|
|
43
|
+
validate,
|
|
44
|
+
...rules,
|
|
42
45
|
}}
|
|
43
46
|
shouldUnregister={shouldUnregister}
|
|
44
47
|
render={({ field }) => (
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
import { ControlledCodesAutocomplete } from './CodesAutocomplete';
|
|
3
|
-
import { ControlledForm } from './ControlledForm';
|
|
4
3
|
import { Button } from '@availity/mui-button';
|
|
5
|
-
import { useFormContext } from 'react-hook-form';
|
|
6
4
|
import { Paper } from '@availity/mui-paper';
|
|
7
5
|
import { Typography } from '@availity/mui-typography';
|
|
8
6
|
import { Grid } from '@availity/mui-layout';
|
|
9
7
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
10
|
-
import {
|
|
8
|
+
import { AllControllerPropertiesCategorized, CodesAutocompletePropsCategorized } from './Types';
|
|
9
|
+
import { FormProvider, useForm } from '..';
|
|
11
10
|
|
|
12
11
|
const meta: Meta<typeof ControlledCodesAutocomplete> = {
|
|
13
12
|
title: 'Form Components/Controlled Form/Autocomplete/ControlledCodesAutocomplete',
|
|
14
13
|
component: ControlledCodesAutocomplete,
|
|
15
14
|
tags: ['autodocs'],
|
|
16
|
-
argTypes:
|
|
15
|
+
argTypes: {...AllControllerPropertiesCategorized, ...CodesAutocompletePropsCategorized}
|
|
17
16
|
};
|
|
18
17
|
|
|
19
18
|
export default meta;
|
|
@@ -28,39 +27,25 @@ const client = new QueryClient({
|
|
|
28
27
|
|
|
29
28
|
export const _ControlledCodesAutoComplete: StoryObj<typeof ControlledCodesAutocomplete> = {
|
|
30
29
|
render: (args) => {
|
|
31
|
-
const
|
|
32
|
-
const {
|
|
33
|
-
getValues,
|
|
34
|
-
formState: { isSubmitSuccessful },
|
|
35
|
-
} = useFormContext();
|
|
30
|
+
const methods = useForm();
|
|
36
31
|
|
|
37
|
-
return isSubmitSuccessful ? (
|
|
38
|
-
<Paper sx={{ padding: '1.5rem', marginTop: '1.5rem' }}>
|
|
39
|
-
<Typography variant="h2">Submitted Values</Typography>
|
|
40
|
-
<pre>{JSON.stringify(getValues(), null, 2)}</pre>
|
|
41
|
-
</Paper>
|
|
42
|
-
) : null;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const Actions = () => {
|
|
46
|
-
const {
|
|
47
|
-
reset,
|
|
48
|
-
formState: { isSubmitSuccessful },
|
|
49
|
-
} = useFormContext();
|
|
50
|
-
return (
|
|
51
|
-
<Grid container direction="row" justifyContent="space-between" marginTop={1}>
|
|
52
|
-
<Button disabled={!isSubmitSuccessful} children="Reset" color="secondary" onClick={() => reset()} />
|
|
53
|
-
<Button type="submit" disabled={isSubmitSuccessful} children="Submit" />
|
|
54
|
-
</Grid>
|
|
55
|
-
);
|
|
56
|
-
};
|
|
57
32
|
return (
|
|
58
33
|
<QueryClientProvider client={client}>
|
|
59
|
-
<
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
34
|
+
<FormProvider {...methods}>
|
|
35
|
+
<form onSubmit={methods.handleSubmit((data) => data)}>
|
|
36
|
+
<ControlledCodesAutocomplete {...args} />
|
|
37
|
+
<Grid container direction="row" justifyContent="space-between" marginTop={1}>
|
|
38
|
+
<Button disabled={!methods?.formState?.isSubmitSuccessful} children="Reset" color="secondary" onClick={() => methods.reset()} />
|
|
39
|
+
<Button type="submit" disabled={methods?.formState?.isSubmitSuccessful} children="Submit" />
|
|
40
|
+
</Grid>
|
|
41
|
+
{ methods?.formState?.isSubmitSuccessful ? (
|
|
42
|
+
<Paper sx={{ padding: '1.5rem', marginTop: '1.5rem' }}>
|
|
43
|
+
<Typography variant="h2">Submitted Values</Typography>
|
|
44
|
+
<pre data-testid="result">{JSON.stringify(methods.getValues(), null, 2)}</pre>
|
|
45
|
+
</Paper>
|
|
46
|
+
) : null }
|
|
47
|
+
</form>
|
|
48
|
+
</FormProvider>
|
|
64
49
|
</QueryClientProvider>
|
|
65
50
|
);
|
|
66
51
|
},
|
|
@@ -72,8 +57,9 @@ export const _ControlledCodesAutoComplete: StoryObj<typeof ControlledCodesAutoco
|
|
|
72
57
|
helperText: 'Select a code from the list',
|
|
73
58
|
placeholder: 'Select...',
|
|
74
59
|
fullWidth: false,
|
|
60
|
+
required: true
|
|
75
61
|
},
|
|
76
62
|
limit: 15,
|
|
77
|
-
required:
|
|
63
|
+
rules: { required:'This is required.' },
|
|
78
64
|
},
|
|
79
65
|
};
|
|
@@ -1,39 +1,9 @@
|
|
|
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
|
// eslint-disable-next-line @nx/enforce-module-boundaries
|
|
9
4
|
import { server } from '@availity/mock/src/lib/server';
|
|
10
|
-
import { ControlledForm } from './ControlledForm';
|
|
11
5
|
import { ControlledCodesAutocomplete } from './CodesAutocomplete';
|
|
12
|
-
|
|
13
|
-
const SubmittedValues = () => {
|
|
14
|
-
const {
|
|
15
|
-
getValues,
|
|
16
|
-
formState: { isSubmitSuccessful },
|
|
17
|
-
} = useFormContext();
|
|
18
|
-
|
|
19
|
-
return isSubmitSuccessful ? (
|
|
20
|
-
<Paper sx={{ padding: '1.5rem', marginTop: '1.5rem' }}>
|
|
21
|
-
<Typography variant="h2">Submitted Values</Typography>
|
|
22
|
-
<pre data-testid="result">{JSON.stringify(getValues(), null, 2)}</pre>
|
|
23
|
-
</Paper>
|
|
24
|
-
) : null;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const Actions = () => {
|
|
28
|
-
const {
|
|
29
|
-
formState: { isSubmitSuccessful },
|
|
30
|
-
} = useFormContext();
|
|
31
|
-
return (
|
|
32
|
-
<Grid container direction="row" justifyContent="space-between">
|
|
33
|
-
<Button type="submit" disabled={isSubmitSuccessful} children="Submit" />
|
|
34
|
-
</Grid>
|
|
35
|
-
);
|
|
36
|
-
};
|
|
6
|
+
import { TestForm } from './UtilComponents';
|
|
37
7
|
|
|
38
8
|
const onSubmit = jest.fn();
|
|
39
9
|
|
|
@@ -61,7 +31,7 @@ describe('ControlledAsyncAutocomplete', () => {
|
|
|
61
31
|
test('should loadOptions successfully', async () => {
|
|
62
32
|
const screen = render(
|
|
63
33
|
<QueryClientProvider client={client}>
|
|
64
|
-
<
|
|
34
|
+
<TestForm onSubmit={onSubmit}>
|
|
65
35
|
<ControlledCodesAutocomplete
|
|
66
36
|
name="controlledCodesAutocomplete"
|
|
67
37
|
list="ABC"
|
|
@@ -73,11 +43,9 @@ describe('ControlledAsyncAutocomplete', () => {
|
|
|
73
43
|
}}
|
|
74
44
|
limit={15}
|
|
75
45
|
/>
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
</QueryClientProvider>
|
|
80
|
-
);
|
|
46
|
+
</TestForm>
|
|
47
|
+
</QueryClientProvider>
|
|
48
|
+
);
|
|
81
49
|
|
|
82
50
|
const dropdown = screen.getByRole('combobox');
|
|
83
51
|
fireEvent.click(dropdown);
|
|
@@ -87,23 +55,21 @@ describe('ControlledAsyncAutocomplete', () => {
|
|
|
87
55
|
});
|
|
88
56
|
|
|
89
57
|
test('should set the value and submit the form data', async () => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
<SubmittedValues />
|
|
106
|
-
</ControlledForm>
|
|
58
|
+
const screen = render(
|
|
59
|
+
<QueryClientProvider client={client}>
|
|
60
|
+
<TestForm onSubmit={onSubmit}>
|
|
61
|
+
<ControlledCodesAutocomplete
|
|
62
|
+
name="controlledCodesAutocomplete"
|
|
63
|
+
list="ABC"
|
|
64
|
+
FieldProps={{
|
|
65
|
+
label: 'Code Select',
|
|
66
|
+
helperText: 'Select a code from the list',
|
|
67
|
+
placeholder: 'Select...',
|
|
68
|
+
fullWidth: false,
|
|
69
|
+
}}
|
|
70
|
+
limit={15}
|
|
71
|
+
/>
|
|
72
|
+
</TestForm>
|
|
107
73
|
</QueryClientProvider>
|
|
108
74
|
);
|
|
109
75
|
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { CodesAutocomplete, CodesAutocompleteProps } from '@availity/mui-autocomplete';
|
|
2
|
-
import { Controller, RegisterOptions,
|
|
2
|
+
import { Controller, RegisterOptions, FieldValues } from 'react-hook-form';
|
|
3
|
+
import { ControllerProps, DeprecatedRulesProps } from './Types';
|
|
3
4
|
|
|
4
|
-
export type ControlledCodesAutocompleteProps = Omit<CodesAutocompleteProps,
|
|
5
|
-
|
|
6
|
-
|
|
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;
|
|
7
12
|
|
|
8
13
|
export const ControlledCodesAutocomplete = ({
|
|
9
14
|
name,
|
|
@@ -15,6 +20,7 @@ export const ControlledCodesAutocomplete = ({
|
|
|
15
20
|
onChange,
|
|
16
21
|
pattern,
|
|
17
22
|
required,
|
|
23
|
+
rules = {},
|
|
18
24
|
shouldUnregister,
|
|
19
25
|
validate,
|
|
20
26
|
value,
|
|
@@ -36,14 +42,14 @@ export const ControlledCodesAutocomplete = ({
|
|
|
36
42
|
shouldUnregister,
|
|
37
43
|
validate,
|
|
38
44
|
value,
|
|
45
|
+
...rules,
|
|
39
46
|
}}
|
|
40
47
|
shouldUnregister={shouldUnregister}
|
|
41
|
-
render={({ field: { onChange, value, onBlur }, fieldState: { error } }) => (
|
|
48
|
+
render={({ field: { onChange, value, onBlur, ref }, fieldState: { error } }) => (
|
|
42
49
|
<CodesAutocomplete
|
|
43
50
|
{...rest}
|
|
44
51
|
FieldProps={{
|
|
45
52
|
...FieldProps,
|
|
46
|
-
required: typeof required === 'object' ? required.value : !!required,
|
|
47
53
|
error: !!error,
|
|
48
54
|
helperText: error?.message ? (
|
|
49
55
|
<>
|
|
@@ -54,6 +60,7 @@ export const ControlledCodesAutocomplete = ({
|
|
|
54
60
|
) : (
|
|
55
61
|
FieldProps?.helperText
|
|
56
62
|
),
|
|
63
|
+
inputRef:ref
|
|
57
64
|
}}
|
|
58
65
|
onChange={(event, value, reason) => {
|
|
59
66
|
if (reason === 'clear') {
|
|
@@ -11,6 +11,7 @@ import { ControlledTextField } from './TextField';
|
|
|
11
11
|
import * as yup from 'yup';
|
|
12
12
|
import { yupResolver } from '@hookform/resolvers/yup';
|
|
13
13
|
|
|
14
|
+
/** Deprecated. Use `FormProvider` and `useForm` directly. */
|
|
14
15
|
const meta: Meta<typeof ControlledForm> = {
|
|
15
16
|
title: 'Form Components/Controlled Form/ControlledForm',
|
|
16
17
|
component: ControlledForm,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FormHTMLAttributes } from 'react';
|
|
2
|
-
import { useForm, SubmitHandler, FormProvider, Resolver } from 'react-hook-form';
|
|
2
|
+
import { useForm, SubmitHandler, FormProvider, Resolver, UseFormProps } from 'react-hook-form';
|
|
3
3
|
|
|
4
|
+
/** @deprecated Use `UseFormProps` directly with `useForm` and `FormProvider` */
|
|
4
5
|
export type ControlledFormProps = {
|
|
5
6
|
/** This function will receive the form data if form validation is successful. */
|
|
6
7
|
onSubmit: SubmitHandler<any>;
|
|
@@ -13,6 +14,8 @@ export type ControlledFormProps = {
|
|
|
13
14
|
* found here: https://github.com/react-hook-form/resolvers#quickstart
|
|
14
15
|
*/
|
|
15
16
|
validationResolver?: (schema: unknown) => Resolver;
|
|
17
|
+
/** Additional 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) */
|
|
18
|
+
additionalUseFormOptions?: Omit<UseFormProps, 'values' | 'resolver'>;
|
|
16
19
|
} & FormHTMLAttributes<HTMLFormElement>;
|
|
17
20
|
|
|
18
21
|
type UseFormOptions = {
|
|
@@ -20,8 +23,9 @@ type UseFormOptions = {
|
|
|
20
23
|
resolver?: Resolver;
|
|
21
24
|
};
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
/** @deprecated Use `FormProvider` and `useForm` directly. */
|
|
27
|
+
export const ControlledForm = ({ onSubmit, values, schema, validationResolver, additionalUseFormOptions = {mode: 'onBlur'}, ...rest }: ControlledFormProps) => {
|
|
28
|
+
const useFormOptions: UseFormOptions = { values, ...additionalUseFormOptions };
|
|
25
29
|
if (schema && validationResolver) {
|
|
26
30
|
useFormOptions.resolver = validationResolver(schema);
|
|
27
31
|
}
|
|
@@ -1,54 +1,41 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
import { ControlledDatepicker, ControlledDatepickerProps } from './Datepicker';
|
|
3
|
-
import { ControlledForm } from './ControlledForm';
|
|
4
3
|
import { Button } from '@availity/mui-button';
|
|
5
|
-
import { useFormContext } from 'react-hook-form';
|
|
6
4
|
import { Paper } from '@availity/mui-paper';
|
|
7
5
|
import { Typography } from '@availity/mui-typography';
|
|
8
6
|
import { Grid } from '@availity/mui-layout';
|
|
7
|
+
import { AllControllerPropertiesCategorized, DatepickerPropsCategorized } from './Types';
|
|
8
|
+
import { FormProvider, useForm } from '..';
|
|
9
9
|
|
|
10
10
|
const meta: Meta<typeof ControlledDatepicker> = {
|
|
11
11
|
title: 'Form Components/Controlled Form/ControlledDatepicker',
|
|
12
12
|
component: ControlledDatepicker,
|
|
13
13
|
tags: ['autodocs'],
|
|
14
|
+
argTypes: {...AllControllerPropertiesCategorized, ...DatepickerPropsCategorized}
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
export default meta;
|
|
17
18
|
|
|
18
19
|
export const _ControlledInput: StoryObj<typeof ControlledDatepicker> = {
|
|
19
20
|
render: (args: ControlledDatepickerProps) => {
|
|
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
|
+
<ControlledDatepicker {...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: {
|
|
@@ -1,38 +1,8 @@
|
|
|
1
1
|
import { render, fireEvent, 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
2
|
import { ThemeProvider } from '@availity/theme-provider';
|
|
8
3
|
import dayjs from 'dayjs';
|
|
9
|
-
import { ControlledForm } from './ControlledForm';
|
|
10
4
|
import { ControlledDatepicker } from './Datepicker';
|
|
11
|
-
|
|
12
|
-
const SubmittedValues = () => {
|
|
13
|
-
const {
|
|
14
|
-
getValues,
|
|
15
|
-
formState: { isSubmitSuccessful },
|
|
16
|
-
} = useFormContext();
|
|
17
|
-
|
|
18
|
-
return isSubmitSuccessful ? (
|
|
19
|
-
<Paper sx={{ padding: '1.5rem', marginTop: '1.5rem' }}>
|
|
20
|
-
<Typography variant="h2">Submitted Values</Typography>
|
|
21
|
-
<pre data-testid="result">{JSON.stringify(getValues(), null, 2)}</pre>
|
|
22
|
-
</Paper>
|
|
23
|
-
) : null;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const Actions = () => {
|
|
27
|
-
const {
|
|
28
|
-
formState: { isSubmitSuccessful },
|
|
29
|
-
} = useFormContext();
|
|
30
|
-
return (
|
|
31
|
-
<Grid container direction="row" justifyContent="space-between">
|
|
32
|
-
<Button type="submit" disabled={isSubmitSuccessful} children="Submit" />
|
|
33
|
-
</Grid>
|
|
34
|
-
);
|
|
35
|
-
};
|
|
5
|
+
import { TestForm } from './UtilComponents';
|
|
36
6
|
|
|
37
7
|
const onSubmit = jest.fn();
|
|
38
8
|
|
|
@@ -40,7 +10,7 @@ describe('Datepicker', () => {
|
|
|
40
10
|
test('should render successfully and submit selection', async () => {
|
|
41
11
|
const screen = render(
|
|
42
12
|
<ThemeProvider>
|
|
43
|
-
<
|
|
13
|
+
<TestForm UseFormOptions={{values: { controlledDatepicker: null}}} onSubmit={onSubmit}>
|
|
44
14
|
<ControlledDatepicker
|
|
45
15
|
name="controlledDatepicker"
|
|
46
16
|
FieldProps={{
|
|
@@ -50,9 +20,7 @@ describe('Datepicker', () => {
|
|
|
50
20
|
label: 'Date',
|
|
51
21
|
}}
|
|
52
22
|
/>
|
|
53
|
-
|
|
54
|
-
<SubmittedValues />
|
|
55
|
-
</ControlledForm>
|
|
23
|
+
</TestForm>
|
|
56
24
|
</ThemeProvider>
|
|
57
25
|
);
|
|
58
26
|
expect(screen.getAllByText('Date')).toBeTruthy();
|
package/src/lib/Datepicker.tsx
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { Datepicker, DatepickerProps } from '@availity/mui-datepicker';
|
|
2
|
-
import { RegisterOptions, FieldValues, Controller
|
|
2
|
+
import { RegisterOptions, FieldValues, Controller } from 'react-hook-form';
|
|
3
|
+
import { ControllerProps, DeprecatedRulesProps } from './Types';
|
|
3
4
|
|
|
4
|
-
export type ControlledDatepickerProps = DatepickerProps
|
|
5
|
-
|
|
6
|
-
|
|
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;
|
|
7
12
|
|
|
8
13
|
export const ControlledDatepicker = ({
|
|
9
14
|
name,
|
|
@@ -17,6 +22,7 @@ export const ControlledDatepicker = ({
|
|
|
17
22
|
onChange,
|
|
18
23
|
pattern,
|
|
19
24
|
required,
|
|
25
|
+
rules = {},
|
|
20
26
|
shouldUnregister,
|
|
21
27
|
validate,
|
|
22
28
|
value,
|
|
@@ -40,24 +46,28 @@ export const ControlledDatepicker = ({
|
|
|
40
46
|
shouldUnregister,
|
|
41
47
|
validate,
|
|
42
48
|
value,
|
|
49
|
+
...rules,
|
|
43
50
|
}}
|
|
44
51
|
shouldUnregister={shouldUnregister}
|
|
45
|
-
render={({ field: { onChange, value }, fieldState: { error } }) => (
|
|
52
|
+
render={({ field: { onChange, value, onBlur, ref }, fieldState: { error } }) => (
|
|
46
53
|
<Datepicker
|
|
47
54
|
{...rest}
|
|
48
55
|
FieldProps={{
|
|
49
56
|
...FieldProps,
|
|
50
|
-
required: typeof required === 'object' ? required.value : !!required,
|
|
51
57
|
error: !!error,
|
|
52
|
-
helperText: error
|
|
58
|
+
helperText: error ? (
|
|
53
59
|
<>
|
|
54
|
-
{error
|
|
60
|
+
{error.message}
|
|
55
61
|
<br />
|
|
56
62
|
{FieldProps?.helperText}
|
|
57
63
|
</>
|
|
58
64
|
) : (
|
|
59
65
|
FieldProps?.helperText
|
|
60
66
|
),
|
|
67
|
+
inputRef: ref,
|
|
68
|
+
inputProps: {
|
|
69
|
+
onBlur: onBlur
|
|
70
|
+
},
|
|
61
71
|
}}
|
|
62
72
|
onChange={onChange}
|
|
63
73
|
value={value || null}
|