@availity/mui-controlled-form 0.1.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.
Files changed (50) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +65 -0
  3. package/dist/index.d.mts +20 -0
  4. package/dist/index.d.ts +20 -0
  5. package/dist/index.js +71 -0
  6. package/dist/index.mjs +47 -0
  7. package/docs/propDefinitions.tsx +31 -0
  8. package/introduction.stories.mdx +7 -0
  9. package/jest.config.js +7 -0
  10. package/package.json +66 -0
  11. package/project.json +41 -0
  12. package/src/index.ts +1 -0
  13. package/src/lib/AsyncAutocomplete.stories.tsx +113 -0
  14. package/src/lib/AsyncAutocomplete.test.tsx +162 -0
  15. package/src/lib/AsyncAutocomplete.tsx +92 -0
  16. package/src/lib/Autocomplete.stories.tsx +60 -0
  17. package/src/lib/Autocomplete.test.tsx +70 -0
  18. package/src/lib/Autocomplete.tsx +96 -0
  19. package/src/lib/Checkbox.stories.tsx +67 -0
  20. package/src/lib/Checkbox.test.tsx +73 -0
  21. package/src/lib/Checkbox.tsx +37 -0
  22. package/src/lib/CodesAutocomplete.stories.tsx +79 -0
  23. package/src/lib/CodesAutocomplete.test.tsx +128 -0
  24. package/src/lib/CodesAutocomplete.tsx +76 -0
  25. package/src/lib/ControlledForm.stories.tsx +74 -0
  26. package/src/lib/ControlledForm.test.tsx +77 -0
  27. package/src/lib/ControlledForm.tsx +35 -0
  28. package/src/lib/Datepicker.stories.tsx +63 -0
  29. package/src/lib/Datepicker.test.tsx +73 -0
  30. package/src/lib/Datepicker.tsx +49 -0
  31. package/src/lib/Input.stories.tsx +60 -0
  32. package/src/lib/Input.test.tsx +98 -0
  33. package/src/lib/Input.tsx +54 -0
  34. package/src/lib/OrganizationAutocomplete.stories.tsx +77 -0
  35. package/src/lib/OrganizationAutocomplete.test.tsx +125 -0
  36. package/src/lib/OrganizationAutocomplete.tsx +75 -0
  37. package/src/lib/ProviderAutocomplete.stories.tsx +79 -0
  38. package/src/lib/ProviderAutocomplete.test.tsx +128 -0
  39. package/src/lib/ProviderAutocomplete.tsx +80 -0
  40. package/src/lib/RadioGroup.stories.tsx +63 -0
  41. package/src/lib/RadioGroup.test.tsx +66 -0
  42. package/src/lib/RadioGroup.tsx +68 -0
  43. package/src/lib/Select.stories.tsx +74 -0
  44. package/src/lib/Select.test.tsx +68 -0
  45. package/src/lib/Select.tsx +55 -0
  46. package/src/lib/TextField.stories.tsx +67 -0
  47. package/src/lib/TextField.test.tsx +99 -0
  48. package/src/lib/TextField.tsx +67 -0
  49. package/tsconfig.json +5 -0
  50. package/tsconfig.spec.json +10 -0
@@ -0,0 +1,162 @@
1
+ import { fireEvent, render, waitFor } from '@testing-library/react';
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
+ import AvApi, { ApiConfig } from '@availity/api-axios';
9
+ // eslint-disable-next-line @nx/enforce-module-boundaries
10
+ import { server } from '@availity/mock/src/lib/server';
11
+ import { ControlledForm } from './ControlledForm';
12
+ import { ControlledAsyncAutocomplete } from './AsyncAutocomplete';
13
+
14
+ const api = new AvApi({ name: 'example' } as ApiConfig);
15
+
16
+ type Option = {
17
+ label: string;
18
+ value: number;
19
+ };
20
+
21
+ type ExampleResponse = {
22
+ totalCount: number;
23
+ options: Option[];
24
+ count: number;
25
+ };
26
+
27
+ const getResults = async (offset: number, limit: number) => {
28
+ // const offset = page * limit;
29
+ const resp = await api.post<ExampleResponse>({ offset, limit }, { params: {} });
30
+
31
+ return {
32
+ totalCount: resp.data.totalCount,
33
+ offset,
34
+ limit,
35
+ options: resp.data.options,
36
+ count: resp.data.count,
37
+ };
38
+ };
39
+
40
+ const loadOptions = async (offset: number, limit: number) => {
41
+ const { options, totalCount } = await getResults(offset, limit);
42
+
43
+ return {
44
+ options,
45
+ hasMore: offset + limit < totalCount,
46
+ offset,
47
+ };
48
+ };
49
+
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
+ const onSubmit = jest.fn();
78
+
79
+ describe('ControlledAsyncAutocomplete', () => {
80
+ beforeAll(() => {
81
+ // Start the interception.
82
+ server.listen();
83
+ });
84
+
85
+ afterEach(() => {
86
+ // Remove any handlers you may have added
87
+ // in individual tests (runtime handlers).
88
+ server.resetHandlers();
89
+ jest.restoreAllMocks();
90
+ });
91
+
92
+ const client = new QueryClient({
93
+ defaultOptions: {
94
+ queries: {
95
+ refetchOnWindowFocus: false,
96
+ },
97
+ },
98
+ });
99
+
100
+ test('should loadOptions successfully', async () => {
101
+ const screen = render(
102
+ <QueryClientProvider client={client}>
103
+ <ControlledForm values={{ controlledAutocomplete: undefined }} onSubmit={(data) => data}>
104
+ <ControlledAsyncAutocomplete
105
+ name="controlledAsyncAutocomplete"
106
+ FieldProps={{ label: 'Async Select', helperText: 'Helper Text', fullWidth: false }}
107
+ getOptionLabel={(val: Option) => val.label}
108
+ loadOptions={loadOptions}
109
+ limit={10}
110
+ queryKey="example"
111
+ />
112
+ <Actions />
113
+ <SubmittedValues />
114
+ </ControlledForm>
115
+ </QueryClientProvider>
116
+ );
117
+
118
+ const dropdown = screen.getByRole('combobox');
119
+ fireEvent.click(dropdown);
120
+ fireEvent.keyDown(dropdown, { key: 'ArrowDown' });
121
+
122
+ await waitFor(() => expect(screen.getByText('Option 1')).toBeDefined());
123
+ });
124
+
125
+ test('should set the value and submit the form data', async () => {
126
+ const screen = render(
127
+ <QueryClientProvider client={client}>
128
+ <ControlledForm values={{ controlledAutocomplete: undefined }} onSubmit={onSubmit}>
129
+ <ControlledAsyncAutocomplete
130
+ name="controlledAsyncAutocomplete"
131
+ FieldProps={{ label: 'Async Select', helperText: 'Helper Text', fullWidth: false }}
132
+ getOptionLabel={(val: Option) => val.label}
133
+ loadOptions={loadOptions}
134
+ limit={10}
135
+ queryKey="example"
136
+ />
137
+ <Actions />
138
+ <SubmittedValues />
139
+ </ControlledForm>
140
+ </QueryClientProvider>
141
+ );
142
+
143
+ const dropdown = screen.getByRole('combobox');
144
+ fireEvent.click(dropdown);
145
+ fireEvent.keyDown(dropdown, { key: 'ArrowDown' });
146
+
147
+ await waitFor(() => screen.getByText('Option 1'));
148
+
149
+ fireEvent.click(screen.getByText('Option 1'));
150
+
151
+ fireEvent.click(screen.getByText('Submit'));
152
+
153
+ await waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(1));
154
+ const result = screen.getByTestId('result');
155
+ await waitFor(() => {
156
+ const controlledAsyncAutocompleteValue = JSON.parse(result.innerHTML).controlledAsyncAutocomplete;
157
+ expect(controlledAsyncAutocompleteValue.label).toBe('Option 1');
158
+ expect(controlledAsyncAutocompleteValue.value).toBe(1);
159
+ expect(controlledAsyncAutocompleteValue.id).toBeDefined(); // This is a unique id
160
+ });
161
+ });
162
+ });
@@ -0,0 +1,92 @@
1
+ import { AsyncAutocomplete, AsyncAutocompleteProps } from '@availity/mui-autocomplete';
2
+ import { useFormContext, RegisterOptions, FieldValues, Controller, ControllerProps } from 'react-hook-form';
3
+ import { ChipTypeMap } from '@mui/material/Chip';
4
+
5
+ type ControlledAsyncAutocompleteProps<
6
+ Option,
7
+ Multiple extends boolean | undefined,
8
+ DisableClearable extends boolean | undefined,
9
+ FreeSolo extends boolean | undefined,
10
+ ChipComponent extends React.ElementType = ChipTypeMap['defaultComponent'],
11
+ > = Omit<AsyncAutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>, 'name'> &
12
+ Omit<RegisterOptions<FieldValues, string>, 'disabled' | 'valueAsNumber' | 'valueAsDate' | 'setValueAs'> &
13
+ Pick<ControllerProps, 'defaultValue' | 'shouldUnregister' | 'name'>;
14
+
15
+ export const ControlledAsyncAutocomplete = <
16
+ Option,
17
+ Multiple extends boolean | undefined = false,
18
+ DisableClearable extends boolean | undefined = false,
19
+ FreeSolo extends boolean | undefined = false,
20
+ ChipComponent extends React.ElementType = ChipTypeMap['defaultComponent'],
21
+ >({
22
+ name,
23
+ deps,
24
+ max,
25
+ maxLength,
26
+ min,
27
+ minLength,
28
+ onBlur,
29
+ onChange,
30
+ pattern,
31
+ required,
32
+ shouldUnregister,
33
+ validate,
34
+ value,
35
+ FieldProps,
36
+ ...rest
37
+ }: ControlledAsyncAutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>) => {
38
+ const {
39
+ control,
40
+ formState: { errors },
41
+ } = useFormContext();
42
+ const errorMessage = errors[name]?.message;
43
+ return (
44
+ <Controller
45
+ name={name}
46
+ control={control}
47
+ defaultValue={rest.defaultValue}
48
+ rules={{
49
+ deps,
50
+ max,
51
+ maxLength,
52
+ min,
53
+ minLength,
54
+ onBlur,
55
+ onChange,
56
+ pattern,
57
+ required,
58
+ shouldUnregister,
59
+ validate,
60
+ value,
61
+ }}
62
+ shouldUnregister={shouldUnregister}
63
+ render={({ field: { onChange, value, onBlur } }) => (
64
+ <AsyncAutocomplete
65
+ {...rest}
66
+ FieldProps={{
67
+ ...FieldProps,
68
+ error: !!errorMessage,
69
+ helperText:
70
+ errorMessage && typeof errorMessage === 'string' ? (
71
+ <>
72
+ {errorMessage}
73
+ <br />
74
+ {FieldProps?.helperText}
75
+ </>
76
+ ) : (
77
+ FieldProps?.helperText
78
+ ),
79
+ }}
80
+ onChange={(event, value, reason) => {
81
+ if (reason === 'clear') {
82
+ onChange(null);
83
+ }
84
+ onChange(value);
85
+ }}
86
+ onBlur={onBlur}
87
+ value={value || null}
88
+ />
89
+ )}
90
+ />
91
+ );
92
+ };
@@ -0,0 +1,60 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { ControlledAutocomplete } from './Autocomplete';
3
+ import { ControlledForm } from './ControlledForm';
4
+ import { Button } from '@availity/mui-button';
5
+ import { useFormContext } from 'react-hook-form';
6
+ import { Paper } from '@availity/mui-paper';
7
+ import { Typography } from '@availity/mui-typography';
8
+ import { Grid } from '@availity/mui-layout';
9
+
10
+ const meta: Meta<typeof ControlledAutocomplete> = {
11
+ title: 'Form Components/Controlled Form/Autocomplete/ControlledAutocomplete',
12
+ component: ControlledAutocomplete,
13
+ tags: ['autodocs'],
14
+ };
15
+
16
+ export default meta;
17
+
18
+ export const _ControlledAutoComplete: StoryObj<typeof ControlledAutocomplete> = {
19
+ render: (args) => {
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>{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
+ return (
47
+ <ControlledForm values={{ controlledAutocomplete: undefined }} onSubmit={(data) => data}>
48
+ <ControlledAutocomplete {...args} />
49
+ <Actions />
50
+ <SubmittedValues />
51
+ </ControlledForm>
52
+ );
53
+ },
54
+ args: {
55
+ name: 'controlledAutocomplete',
56
+ options: ['Option 1', 'Option 2'],
57
+ required: 'This is required.',
58
+ FieldProps: { label: 'Autocomplete Label' },
59
+ },
60
+ };
@@ -0,0 +1,70 @@
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
+ import { ControlledAutocomplete } from './Autocomplete';
9
+
10
+ const onSubmit = jest.fn();
11
+
12
+ describe('ControlledAsyncAutocomplete', () => {
13
+ afterEach(() => {
14
+ // Remove any handlers you may have added
15
+ // in individual tests (runtime handlers).
16
+ jest.restoreAllMocks();
17
+ });
18
+
19
+ 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
+ const screen = render(
47
+ <ControlledForm values={{ controlledAutocomplete: undefined }} onSubmit={onSubmit}>
48
+ <ControlledAutocomplete name="controlledAutocomplete" options={['Option 1', 'Option 2']} />
49
+ <Actions />
50
+ <SubmittedValues />
51
+ </ControlledForm>
52
+ );
53
+
54
+ const dropdown = screen.getByRole('combobox');
55
+ fireEvent.click(dropdown);
56
+ fireEvent.keyDown(dropdown, { key: 'ArrowDown' });
57
+
58
+ fireEvent.click(screen.getByText('Option 1'));
59
+
60
+ fireEvent.click(screen.getByText('Submit'));
61
+
62
+ await waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(1));
63
+
64
+ const result = screen.getByTestId('result');
65
+ await waitFor(() => {
66
+ const controlledAutocompleteValue = JSON.parse(result.innerHTML).controlledAutocomplete;
67
+ expect(controlledAutocompleteValue).toBe('Option 1');
68
+ });
69
+ });
70
+ });
@@ -0,0 +1,96 @@
1
+ import { Autocomplete, AutocompleteProps } from '@availity/mui-autocomplete';
2
+ import { useFormContext, RegisterOptions, FieldValues, Controller, ControllerProps } from 'react-hook-form';
3
+ import { ChipTypeMap } from '@mui/material/Chip';
4
+
5
+ export type ControlledAutocompleteProps<
6
+ T,
7
+ Multiple extends boolean | undefined,
8
+ DisableClearable extends boolean | undefined,
9
+ FreeSolo extends boolean | undefined,
10
+ ChipComponent extends React.ElementType = ChipTypeMap['defaultComponent'],
11
+ > = Omit<
12
+ AutocompleteProps<T, Multiple, DisableClearable, FreeSolo, ChipComponent>,
13
+ 'onChange' | 'onBlur' | 'value' | 'name'
14
+ > &
15
+ Omit<RegisterOptions<FieldValues, string>, 'disabled' | 'valueAsNumber' | 'valueAsDate' | 'setValueAs'> &
16
+ Pick<ControllerProps, 'defaultValue' | 'shouldUnregister' | 'name'>;
17
+
18
+ export const ControlledAutocomplete = <
19
+ T,
20
+ Multiple extends boolean | undefined = false,
21
+ DisableClearable extends boolean | undefined = false,
22
+ FreeSolo extends boolean | undefined = false,
23
+ ChipComponent extends React.ElementType = ChipTypeMap['defaultComponent'],
24
+ >({
25
+ name,
26
+ FieldProps,
27
+ defaultValue,
28
+ deps,
29
+ max,
30
+ maxLength,
31
+ min,
32
+ minLength,
33
+ onBlur,
34
+ onChange,
35
+ pattern,
36
+ required,
37
+ shouldUnregister,
38
+ validate,
39
+ value,
40
+ ...rest
41
+ }: ControlledAutocompleteProps<T, Multiple, DisableClearable, FreeSolo, ChipComponent>) => {
42
+ const {
43
+ control,
44
+ formState: { errors },
45
+ } = useFormContext();
46
+ const errorMessage = errors[name]?.message;
47
+ return (
48
+ <Controller
49
+ control={control}
50
+ name={name}
51
+ defaultValue={defaultValue}
52
+ rules={{
53
+ deps,
54
+ max,
55
+ maxLength,
56
+ min,
57
+ minLength,
58
+ onBlur,
59
+ onChange,
60
+ pattern,
61
+ required,
62
+ shouldUnregister,
63
+ validate,
64
+ value,
65
+ }}
66
+ shouldUnregister={shouldUnregister}
67
+ render={({ field: { onChange, value, onBlur } }) => (
68
+ <Autocomplete
69
+ {...rest}
70
+ FieldProps={{
71
+ ...FieldProps,
72
+ error: !!errorMessage,
73
+ helperText:
74
+ errorMessage && typeof errorMessage === 'string' ? (
75
+ <>
76
+ {errorMessage}
77
+ <br />
78
+ {FieldProps?.helperText}
79
+ </>
80
+ ) : (
81
+ FieldProps?.helperText
82
+ ),
83
+ }}
84
+ onChange={(event, value, reason) => {
85
+ if (reason === 'clear') {
86
+ onChange(null);
87
+ }
88
+ onChange(value);
89
+ }}
90
+ onBlur={onBlur}
91
+ value={value || null}
92
+ />
93
+ )}
94
+ />
95
+ );
96
+ };
@@ -0,0 +1,67 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { ControlledCheckbox } from './Checkbox';
3
+ import { ControlledForm } from './ControlledForm';
4
+ import { Button } from '@availity/mui-button';
5
+ import { useFormContext } from 'react-hook-form';
6
+ import { Paper } from '@availity/mui-paper';
7
+ import { Typography } from '@availity/mui-typography';
8
+ import { FormControlLabel, FormGroup, FormControl, FormLabel } from '@availity/mui-form-utils';
9
+ import { Grid } from '@availity/mui-layout';
10
+
11
+ const meta: Meta<typeof ControlledCheckbox> = {
12
+ title: 'Form Components/Controlled Form/ControlledCheckbox',
13
+ component: ControlledCheckbox,
14
+ tags: ['autodocs'],
15
+ };
16
+
17
+ export default meta;
18
+
19
+ export const _ControlledCheckbox: StoryObj<typeof ControlledCheckbox> = {
20
+ render: () => {
21
+ const SubmittedValues = () => {
22
+ const {
23
+ getValues,
24
+ formState: { isSubmitSuccessful },
25
+ } = useFormContext();
26
+
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 disabled={!isSubmitSuccessful} children="Reset" color="secondary" onClick={() => reset()} />
43
+ <Button type="submit" disabled={isSubmitSuccessful} children="Submit" />
44
+ </Grid>
45
+ );
46
+ };
47
+ return (
48
+ <ControlledForm onSubmit={(data) => data} values={{ controlledCheckbox: undefined }}>
49
+ <FormControl>
50
+ <FormLabel id="radio-group" component="div">
51
+ Radio Group
52
+ </FormLabel>
53
+ <FormGroup>
54
+ <FormControlLabel label="Option 1" control={<ControlledCheckbox name="Option 1" />} />
55
+ <FormControlLabel label="Option 2" control={<ControlledCheckbox name="Option 2" />} />
56
+ <FormControlLabel label="Option 3" control={<ControlledCheckbox name="Option 3" />} />
57
+ </FormGroup>
58
+ </FormControl>
59
+ <Actions />
60
+ <SubmittedValues />
61
+ </ControlledForm>
62
+ );
63
+ },
64
+ args: {
65
+ name: 'controlledCheckbox',
66
+ },
67
+ };
@@ -0,0 +1,73 @@
1
+ import { render, fireEvent, waitFor } from '@testing-library/react';
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
+ 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
+ };
35
+
36
+ const onSubmit = jest.fn();
37
+
38
+ describe('ControlledCheckbox', () => {
39
+ test('should set the value and submit the form data', async () => {
40
+ const screen = render(
41
+ <ControlledForm onSubmit={onSubmit} values={{ controlledCheckbox: undefined }}>
42
+ <FormControl>
43
+ <FormLabel id="radio-group" component="div">
44
+ Radio Group
45
+ </FormLabel>
46
+ <FormGroup>
47
+ <FormControlLabel label="Option 1" control={<ControlledCheckbox name="option1" />} />
48
+ <FormControlLabel label="Option 2" control={<ControlledCheckbox name="option2" />} />
49
+ <FormControlLabel label="Option 3" control={<ControlledCheckbox name="option3" />} />
50
+ </FormGroup>
51
+ </FormControl>
52
+ <Actions />
53
+ <SubmittedValues />
54
+ </ControlledForm>
55
+ );
56
+
57
+ const option1 = screen.getByText('Option 1');
58
+
59
+ fireEvent.click(option1);
60
+
61
+ fireEvent.click(screen.getByText('Submit'));
62
+
63
+ await waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(1));
64
+
65
+ const result = screen.getByTestId('result');
66
+ await waitFor(() => {
67
+ const formValues = JSON.parse(result.innerHTML);
68
+ expect(formValues.option1).toBeTruthy();
69
+ expect(formValues.option2).toBeFalsy();
70
+ expect(formValues.option3).toBeFalsy();
71
+ });
72
+ });
73
+ });
@@ -0,0 +1,37 @@
1
+ import { Checkbox, CheckboxProps } from '@availity/mui-checkbox';
2
+ import { useFormContext, RegisterOptions, FieldValues } from 'react-hook-form';
3
+
4
+ type ControlledCheckboxProps = CheckboxProps & {
5
+ name: string;
6
+ } & Omit<
7
+ RegisterOptions<FieldValues, string>,
8
+ 'required' | 'max' | 'maxLength' | 'min' | 'minLength' | 'pattern' | 'validate'
9
+ >;
10
+
11
+ export const ControlledCheckbox = ({
12
+ name,
13
+ setValueAs,
14
+ disabled,
15
+ onChange,
16
+ onBlur,
17
+ value,
18
+ shouldUnregister,
19
+ deps,
20
+ ...rest
21
+ }: ControlledCheckboxProps) => {
22
+ const { register } = useFormContext();
23
+ return (
24
+ <Checkbox
25
+ {...rest}
26
+ {...register(name, {
27
+ setValueAs,
28
+ disabled,
29
+ onChange,
30
+ onBlur,
31
+ value,
32
+ shouldUnregister,
33
+ deps,
34
+ })}
35
+ />
36
+ );
37
+ };