@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.
- package/CHANGELOG.md +26 -0
- package/README.md +65 -0
- package/dist/index.d.mts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +71 -0
- package/dist/index.mjs +47 -0
- package/docs/propDefinitions.tsx +31 -0
- package/introduction.stories.mdx +7 -0
- package/jest.config.js +7 -0
- package/package.json +66 -0
- package/project.json +41 -0
- package/src/index.ts +1 -0
- package/src/lib/AsyncAutocomplete.stories.tsx +113 -0
- package/src/lib/AsyncAutocomplete.test.tsx +162 -0
- package/src/lib/AsyncAutocomplete.tsx +92 -0
- package/src/lib/Autocomplete.stories.tsx +60 -0
- package/src/lib/Autocomplete.test.tsx +70 -0
- package/src/lib/Autocomplete.tsx +96 -0
- package/src/lib/Checkbox.stories.tsx +67 -0
- package/src/lib/Checkbox.test.tsx +73 -0
- package/src/lib/Checkbox.tsx +37 -0
- package/src/lib/CodesAutocomplete.stories.tsx +79 -0
- package/src/lib/CodesAutocomplete.test.tsx +128 -0
- package/src/lib/CodesAutocomplete.tsx +76 -0
- package/src/lib/ControlledForm.stories.tsx +74 -0
- package/src/lib/ControlledForm.test.tsx +77 -0
- package/src/lib/ControlledForm.tsx +35 -0
- package/src/lib/Datepicker.stories.tsx +63 -0
- package/src/lib/Datepicker.test.tsx +73 -0
- package/src/lib/Datepicker.tsx +49 -0
- package/src/lib/Input.stories.tsx +60 -0
- package/src/lib/Input.test.tsx +98 -0
- package/src/lib/Input.tsx +54 -0
- package/src/lib/OrganizationAutocomplete.stories.tsx +77 -0
- package/src/lib/OrganizationAutocomplete.test.tsx +125 -0
- package/src/lib/OrganizationAutocomplete.tsx +75 -0
- package/src/lib/ProviderAutocomplete.stories.tsx +79 -0
- package/src/lib/ProviderAutocomplete.test.tsx +128 -0
- package/src/lib/ProviderAutocomplete.tsx +80 -0
- package/src/lib/RadioGroup.stories.tsx +63 -0
- package/src/lib/RadioGroup.test.tsx +66 -0
- package/src/lib/RadioGroup.tsx +68 -0
- package/src/lib/Select.stories.tsx +74 -0
- package/src/lib/Select.test.tsx +68 -0
- package/src/lib/Select.tsx +55 -0
- package/src/lib/TextField.stories.tsx +67 -0
- package/src/lib/TextField.test.tsx +99 -0
- package/src/lib/TextField.tsx +67 -0
- package/tsconfig.json +5 -0
- package/tsconfig.spec.json +10 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { ControlledInput, ControlledInputProps } from './Input';
|
|
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 ControlledInput> = {
|
|
11
|
+
title: 'Form Components/Controlled Form/ControlledInput',
|
|
12
|
+
component: ControlledInput,
|
|
13
|
+
tags: ['autodocs'],
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default meta;
|
|
17
|
+
|
|
18
|
+
export const _ControlledInput: StoryObj<typeof ControlledInput> = {
|
|
19
|
+
render: (args: ControlledInputProps) => {
|
|
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={{ controlledInput: undefined }} onSubmit={(data) => data}>
|
|
48
|
+
<ControlledInput {...args} />
|
|
49
|
+
<Actions />
|
|
50
|
+
<SubmittedValues />
|
|
51
|
+
</ControlledForm>
|
|
52
|
+
);
|
|
53
|
+
},
|
|
54
|
+
args: {
|
|
55
|
+
name: 'controlledInput',
|
|
56
|
+
required: true,
|
|
57
|
+
maxLength: { value: 10, message: 'Too long' },
|
|
58
|
+
inputProps: { 'aria-label': 'Input Label' },
|
|
59
|
+
},
|
|
60
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
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
|
+
import { ControlledForm } from './ControlledForm';
|
|
8
|
+
import { ControlledInput } from './Input';
|
|
9
|
+
|
|
10
|
+
const SubmittedValues = () => {
|
|
11
|
+
const {
|
|
12
|
+
getValues,
|
|
13
|
+
formState: { isSubmitSuccessful },
|
|
14
|
+
} = useFormContext();
|
|
15
|
+
|
|
16
|
+
return isSubmitSuccessful ? (
|
|
17
|
+
<Paper sx={{ padding: '1.5rem', marginTop: '1.5rem' }}>
|
|
18
|
+
<Typography variant="h2">Submitted Values</Typography>
|
|
19
|
+
<pre data-testid="result">{JSON.stringify(getValues(), null, 2)}</pre>
|
|
20
|
+
</Paper>
|
|
21
|
+
) : null;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const Actions = () => {
|
|
25
|
+
const {
|
|
26
|
+
formState: { isSubmitSuccessful },
|
|
27
|
+
} = useFormContext();
|
|
28
|
+
return (
|
|
29
|
+
<Grid container direction="row" justifyContent="space-between">
|
|
30
|
+
<Button type="submit" disabled={isSubmitSuccessful} children="Submit" />
|
|
31
|
+
</Grid>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const onSubmit = jest.fn();
|
|
36
|
+
|
|
37
|
+
describe('ControlledInput', () => {
|
|
38
|
+
test('should render the error styling if an error is returned', async () => {
|
|
39
|
+
const screen = render(
|
|
40
|
+
<ControlledForm values={{ controlledInput: undefined }} onSubmit={(data) => data}>
|
|
41
|
+
<ControlledInput
|
|
42
|
+
data-testid="controlledInputWrapper"
|
|
43
|
+
name="controlledInput"
|
|
44
|
+
required="This field is required."
|
|
45
|
+
maxLength={{ value: 10, message: 'Too long' }}
|
|
46
|
+
inputProps={{
|
|
47
|
+
'data-testid': 'testInput',
|
|
48
|
+
}}
|
|
49
|
+
/>
|
|
50
|
+
<Actions />
|
|
51
|
+
<SubmittedValues />
|
|
52
|
+
</ControlledForm>
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const input = screen.getByTestId('testInput');
|
|
56
|
+
|
|
57
|
+
fireEvent.change(input, { target: { value: 'This is way too much text' } });
|
|
58
|
+
|
|
59
|
+
fireEvent.click(screen.getByText('Submit'));
|
|
60
|
+
|
|
61
|
+
await waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(0));
|
|
62
|
+
|
|
63
|
+
const muiInputBase = screen.getByTestId('controlledInputWrapper');
|
|
64
|
+
|
|
65
|
+
await waitFor(() => expect(muiInputBase.classList).toContain('Mui-error'));
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('should render the error styling if an error is returned', async () => {
|
|
69
|
+
const screen = render(
|
|
70
|
+
<ControlledForm values={{ controlledInput: undefined }} onSubmit={onSubmit}>
|
|
71
|
+
<ControlledInput
|
|
72
|
+
name="controlledInput"
|
|
73
|
+
required="This field is required."
|
|
74
|
+
maxLength={{ value: 10, message: 'Too long' }}
|
|
75
|
+
inputProps={{
|
|
76
|
+
'data-testid': 'testInput',
|
|
77
|
+
}}
|
|
78
|
+
/>
|
|
79
|
+
<Actions />
|
|
80
|
+
<SubmittedValues />
|
|
81
|
+
</ControlledForm>
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const input = screen.getByTestId('testInput');
|
|
85
|
+
|
|
86
|
+
fireEvent.change(input, { target: { value: 'Input Text' } });
|
|
87
|
+
|
|
88
|
+
fireEvent.click(screen.getByText('Submit'));
|
|
89
|
+
|
|
90
|
+
await waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(1));
|
|
91
|
+
|
|
92
|
+
const result = screen.getByTestId('result');
|
|
93
|
+
await waitFor(() => {
|
|
94
|
+
const formValues = JSON.parse(result.innerHTML).controlledInput;
|
|
95
|
+
expect(formValues).toBe('Input Text');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Input, InputProps } from '@availity/mui-form-utils';
|
|
2
|
+
import { useFormContext, RegisterOptions, FieldValues } from 'react-hook-form';
|
|
3
|
+
|
|
4
|
+
export type ControlledInputProps = Omit<InputProps, 'error' | 'name' | 'required'> & { name: string } & RegisterOptions<
|
|
5
|
+
FieldValues,
|
|
6
|
+
string
|
|
7
|
+
>;
|
|
8
|
+
|
|
9
|
+
export const ControlledInput = ({
|
|
10
|
+
name,
|
|
11
|
+
required,
|
|
12
|
+
maxLength,
|
|
13
|
+
minLength,
|
|
14
|
+
max,
|
|
15
|
+
min,
|
|
16
|
+
pattern,
|
|
17
|
+
validate,
|
|
18
|
+
setValueAs,
|
|
19
|
+
disabled,
|
|
20
|
+
onChange,
|
|
21
|
+
onBlur,
|
|
22
|
+
value,
|
|
23
|
+
shouldUnregister,
|
|
24
|
+
deps,
|
|
25
|
+
...rest
|
|
26
|
+
}: ControlledInputProps) => {
|
|
27
|
+
const {
|
|
28
|
+
register,
|
|
29
|
+
formState: { errors },
|
|
30
|
+
} = useFormContext();
|
|
31
|
+
return (
|
|
32
|
+
<Input
|
|
33
|
+
{...rest}
|
|
34
|
+
error={!!errors[name]}
|
|
35
|
+
required={!!required}
|
|
36
|
+
{...register(name, {
|
|
37
|
+
required,
|
|
38
|
+
maxLength,
|
|
39
|
+
minLength,
|
|
40
|
+
max,
|
|
41
|
+
min,
|
|
42
|
+
pattern,
|
|
43
|
+
validate,
|
|
44
|
+
setValueAs,
|
|
45
|
+
disabled,
|
|
46
|
+
onChange,
|
|
47
|
+
onBlur,
|
|
48
|
+
value,
|
|
49
|
+
shouldUnregister,
|
|
50
|
+
deps,
|
|
51
|
+
})}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { ControlledOrganizationAutocomplete } from './OrganizationAutocomplete';
|
|
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
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
10
|
+
import { missingRHFprops } from '../../docs/propDefinitions';
|
|
11
|
+
|
|
12
|
+
const meta: Meta<typeof ControlledOrganizationAutocomplete> = {
|
|
13
|
+
title: 'Form Components/Controlled Form/Autocomplete/ControlledOrganizationAutocomplete',
|
|
14
|
+
component: ControlledOrganizationAutocomplete,
|
|
15
|
+
tags: ['autodocs'],
|
|
16
|
+
argTypes: missingRHFprops,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default meta;
|
|
20
|
+
|
|
21
|
+
const client = new QueryClient({
|
|
22
|
+
defaultOptions: {
|
|
23
|
+
queries: {
|
|
24
|
+
refetchOnWindowFocus: false,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const _ControlledOrganizationAutoComplete: StoryObj<typeof ControlledOrganizationAutocomplete> = {
|
|
30
|
+
render: (args) => {
|
|
31
|
+
const SubmittedValues = () => {
|
|
32
|
+
const {
|
|
33
|
+
getValues,
|
|
34
|
+
formState: { isSubmitSuccessful },
|
|
35
|
+
} = useFormContext();
|
|
36
|
+
|
|
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
|
+
return (
|
|
58
|
+
<QueryClientProvider client={client}>
|
|
59
|
+
<ControlledForm values={{}} onSubmit={(data) => data}>
|
|
60
|
+
<ControlledOrganizationAutocomplete {...args} />
|
|
61
|
+
<Actions />
|
|
62
|
+
<SubmittedValues />
|
|
63
|
+
</ControlledForm>
|
|
64
|
+
</QueryClientProvider>
|
|
65
|
+
);
|
|
66
|
+
},
|
|
67
|
+
args: {
|
|
68
|
+
name: 'controlledOrganizationAutocomplete',
|
|
69
|
+
required: 'This is required.',
|
|
70
|
+
FieldProps: {
|
|
71
|
+
label: 'Organization Select',
|
|
72
|
+
helperText: 'Select an Organization from the list',
|
|
73
|
+
placeholder: 'Select...',
|
|
74
|
+
fullWidth: false,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
};
|
|
@@ -0,0 +1,125 @@
|
|
|
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
|
+
// eslint-disable-next-line @nx/enforce-module-boundaries
|
|
9
|
+
import { server } from '@availity/mock/src/lib/server';
|
|
10
|
+
import { ControlledForm } from './ControlledForm';
|
|
11
|
+
import { ControlledOrganizationAutocomplete } from './OrganizationAutocomplete';
|
|
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
|
+
};
|
|
37
|
+
|
|
38
|
+
const onSubmit = jest.fn();
|
|
39
|
+
|
|
40
|
+
describe('ControlledOrganizationAutocomplete', () => {
|
|
41
|
+
beforeAll(() => {
|
|
42
|
+
// Start the interception.
|
|
43
|
+
server.listen();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
afterEach(() => {
|
|
47
|
+
// Remove any handlers you may have added
|
|
48
|
+
// in individual tests (runtime handlers).
|
|
49
|
+
server.resetHandlers();
|
|
50
|
+
jest.restoreAllMocks();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const client = new QueryClient({
|
|
54
|
+
defaultOptions: {
|
|
55
|
+
queries: {
|
|
56
|
+
refetchOnWindowFocus: false,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('should loadOptions successfully', async () => {
|
|
62
|
+
const screen = render(
|
|
63
|
+
<QueryClientProvider client={client}>
|
|
64
|
+
<ControlledForm values={{}} onSubmit={onSubmit}>
|
|
65
|
+
<ControlledOrganizationAutocomplete
|
|
66
|
+
name="controlledOrganizationAutocomplete"
|
|
67
|
+
FieldProps={{
|
|
68
|
+
label: 'Organization Select',
|
|
69
|
+
helperText: 'Select an Organization from the list',
|
|
70
|
+
placeholder: 'Select...',
|
|
71
|
+
fullWidth: false,
|
|
72
|
+
}}
|
|
73
|
+
/>
|
|
74
|
+
<Actions />
|
|
75
|
+
<SubmittedValues />
|
|
76
|
+
</ControlledForm>
|
|
77
|
+
</QueryClientProvider>
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const dropdown = screen.getByRole('combobox');
|
|
81
|
+
fireEvent.click(dropdown);
|
|
82
|
+
fireEvent.keyDown(dropdown, { key: 'ArrowDown' });
|
|
83
|
+
|
|
84
|
+
await waitFor(() => expect(screen.getByText('Organization 1')).toBeDefined());
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('should set the value and submit the form data', async () => {
|
|
88
|
+
const screen = render(
|
|
89
|
+
<QueryClientProvider client={client}>
|
|
90
|
+
<ControlledForm values={{}} onSubmit={onSubmit}>
|
|
91
|
+
<ControlledOrganizationAutocomplete
|
|
92
|
+
name="controlledOrganizationAutocomplete"
|
|
93
|
+
FieldProps={{
|
|
94
|
+
label: 'Organization Select',
|
|
95
|
+
helperText: 'Select an Organization from the list',
|
|
96
|
+
placeholder: 'Select...',
|
|
97
|
+
fullWidth: false,
|
|
98
|
+
}}
|
|
99
|
+
/>
|
|
100
|
+
<Actions />
|
|
101
|
+
<SubmittedValues />
|
|
102
|
+
</ControlledForm>
|
|
103
|
+
</QueryClientProvider>
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const dropdown = screen.getByRole('combobox');
|
|
107
|
+
fireEvent.click(dropdown);
|
|
108
|
+
fireEvent.keyDown(dropdown, { key: 'ArrowDown' });
|
|
109
|
+
|
|
110
|
+
await waitFor(() => screen.getByText('Organization 1'));
|
|
111
|
+
|
|
112
|
+
fireEvent.click(screen.getByText('Organization 1'));
|
|
113
|
+
|
|
114
|
+
fireEvent.click(screen.getByText('Submit'));
|
|
115
|
+
|
|
116
|
+
await waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(1));
|
|
117
|
+
const result = screen.getByTestId('result');
|
|
118
|
+
await waitFor(() => {
|
|
119
|
+
const controlledCodesAutocompleteValue = JSON.parse(result.innerHTML).controlledOrganizationAutocomplete;
|
|
120
|
+
expect(controlledCodesAutocompleteValue.customerId).toBe('1');
|
|
121
|
+
expect(controlledCodesAutocompleteValue.name).toBe('Organization 1');
|
|
122
|
+
expect(controlledCodesAutocompleteValue.id).toBeDefined();
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { OrganizationAutocomplete, OrgAutocompleteProps } from '@availity/mui-autocomplete';
|
|
2
|
+
import { useFormContext, Controller, RegisterOptions, FieldValues, ControllerProps } from 'react-hook-form';
|
|
3
|
+
|
|
4
|
+
type ControlledOrgAutocompleteProps = Omit<OrgAutocompleteProps, 'name'> &
|
|
5
|
+
Omit<
|
|
6
|
+
RegisterOptions<FieldValues, string>,
|
|
7
|
+
'disabled' | 'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'max' | 'maxLength' | 'min' | 'minLength'
|
|
8
|
+
> &
|
|
9
|
+
Pick<ControllerProps, 'defaultValue' | 'shouldUnregister' | 'name'>;
|
|
10
|
+
|
|
11
|
+
export const ControlledOrganizationAutocomplete = ({
|
|
12
|
+
name,
|
|
13
|
+
defaultValue,
|
|
14
|
+
deps,
|
|
15
|
+
onBlur,
|
|
16
|
+
onChange,
|
|
17
|
+
pattern,
|
|
18
|
+
required,
|
|
19
|
+
shouldUnregister,
|
|
20
|
+
validate,
|
|
21
|
+
value,
|
|
22
|
+
FieldProps,
|
|
23
|
+
...rest
|
|
24
|
+
}: ControlledOrgAutocompleteProps) => {
|
|
25
|
+
const {
|
|
26
|
+
control,
|
|
27
|
+
formState: { errors },
|
|
28
|
+
} = useFormContext();
|
|
29
|
+
const errorMessage = errors[name]?.message;
|
|
30
|
+
return (
|
|
31
|
+
<Controller
|
|
32
|
+
name={name}
|
|
33
|
+
control={control}
|
|
34
|
+
defaultValue={defaultValue}
|
|
35
|
+
rules={{
|
|
36
|
+
deps,
|
|
37
|
+
onBlur,
|
|
38
|
+
onChange,
|
|
39
|
+
pattern,
|
|
40
|
+
required,
|
|
41
|
+
shouldUnregister,
|
|
42
|
+
validate,
|
|
43
|
+
value,
|
|
44
|
+
}}
|
|
45
|
+
shouldUnregister={shouldUnregister}
|
|
46
|
+
render={({ field: { onChange, value, onBlur } }) => (
|
|
47
|
+
<OrganizationAutocomplete
|
|
48
|
+
{...rest}
|
|
49
|
+
FieldProps={{
|
|
50
|
+
...FieldProps,
|
|
51
|
+
error: !!errorMessage,
|
|
52
|
+
helperText:
|
|
53
|
+
errorMessage && typeof errorMessage === 'string' ? (
|
|
54
|
+
<>
|
|
55
|
+
{errorMessage}
|
|
56
|
+
<br />
|
|
57
|
+
{FieldProps?.helperText}
|
|
58
|
+
</>
|
|
59
|
+
) : (
|
|
60
|
+
FieldProps?.helperText
|
|
61
|
+
),
|
|
62
|
+
}}
|
|
63
|
+
onChange={(event, value, reason) => {
|
|
64
|
+
if (reason === 'clear') {
|
|
65
|
+
onChange(null);
|
|
66
|
+
}
|
|
67
|
+
onChange(value);
|
|
68
|
+
}}
|
|
69
|
+
onBlur={onBlur}
|
|
70
|
+
value={value || null}
|
|
71
|
+
/>
|
|
72
|
+
)}
|
|
73
|
+
/>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { ControlledProviderAutocomplete } from './ProviderAutocomplete';
|
|
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
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
10
|
+
import { missingRHFprops } from '../../docs/propDefinitions';
|
|
11
|
+
|
|
12
|
+
const meta: Meta<typeof ControlledProviderAutocomplete> = {
|
|
13
|
+
title: 'Form Components/Controlled Form/Autocomplete/ControlledProviderAutocomplete',
|
|
14
|
+
component: ControlledProviderAutocomplete,
|
|
15
|
+
tags: ['autodocs'],
|
|
16
|
+
argTypes: missingRHFprops,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default meta;
|
|
20
|
+
|
|
21
|
+
const client = new QueryClient({
|
|
22
|
+
defaultOptions: {
|
|
23
|
+
queries: {
|
|
24
|
+
refetchOnWindowFocus: false,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const _ControlledProviderAutoComplete: StoryObj<typeof ControlledProviderAutocomplete> = {
|
|
30
|
+
render: (args) => {
|
|
31
|
+
const SubmittedValues = () => {
|
|
32
|
+
const {
|
|
33
|
+
getValues,
|
|
34
|
+
formState: { isSubmitSuccessful },
|
|
35
|
+
} = useFormContext();
|
|
36
|
+
|
|
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
|
+
return (
|
|
58
|
+
<QueryClientProvider client={client}>
|
|
59
|
+
<ControlledForm values={{ controlledAutocomplete: undefined }} onSubmit={(data) => data}>
|
|
60
|
+
<ControlledProviderAutocomplete {...args} />
|
|
61
|
+
<Actions />
|
|
62
|
+
<SubmittedValues />
|
|
63
|
+
</ControlledForm>
|
|
64
|
+
</QueryClientProvider>
|
|
65
|
+
);
|
|
66
|
+
},
|
|
67
|
+
args: {
|
|
68
|
+
name: 'controlledProviderAutocomplete',
|
|
69
|
+
FieldProps: {
|
|
70
|
+
label: 'Provider Select',
|
|
71
|
+
helperText: 'Select a Provider from the list',
|
|
72
|
+
placeholder: 'Select...',
|
|
73
|
+
fullWidth: false,
|
|
74
|
+
},
|
|
75
|
+
limit: 10,
|
|
76
|
+
customerId: '1234',
|
|
77
|
+
required: 'This is required.',
|
|
78
|
+
},
|
|
79
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
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
|
+
// eslint-disable-next-line @nx/enforce-module-boundaries
|
|
9
|
+
import { server } from '@availity/mock/src/lib/server';
|
|
10
|
+
import { ControlledForm } from './ControlledForm';
|
|
11
|
+
import { ControlledProviderAutocomplete } from './ProviderAutocomplete';
|
|
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
|
+
};
|
|
37
|
+
|
|
38
|
+
const onSubmit = jest.fn();
|
|
39
|
+
|
|
40
|
+
describe('ControlledProviderAutocomplete', () => {
|
|
41
|
+
beforeAll(() => {
|
|
42
|
+
// Start the interception.
|
|
43
|
+
server.listen();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
afterEach(() => {
|
|
47
|
+
// Remove any handlers you may have added
|
|
48
|
+
// in individual tests (runtime handlers).
|
|
49
|
+
server.resetHandlers();
|
|
50
|
+
jest.restoreAllMocks();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const client = new QueryClient({
|
|
54
|
+
defaultOptions: {
|
|
55
|
+
queries: {
|
|
56
|
+
refetchOnWindowFocus: false,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('should loadOptions successfully', async () => {
|
|
62
|
+
const screen = render(
|
|
63
|
+
<QueryClientProvider client={client}>
|
|
64
|
+
<ControlledForm values={{ controlledAutocomplete: undefined }} onSubmit={(data) => data}>
|
|
65
|
+
<ControlledProviderAutocomplete
|
|
66
|
+
name="controlledProviderAutocomplete"
|
|
67
|
+
FieldProps={{
|
|
68
|
+
label: 'Provider Select',
|
|
69
|
+
helperText: 'Select a Provider from the list',
|
|
70
|
+
placeholder: 'Select...',
|
|
71
|
+
fullWidth: false,
|
|
72
|
+
}}
|
|
73
|
+
limit={10}
|
|
74
|
+
customerId="1234"
|
|
75
|
+
/>
|
|
76
|
+
<Actions />
|
|
77
|
+
<SubmittedValues />
|
|
78
|
+
</ControlledForm>
|
|
79
|
+
</QueryClientProvider>
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const dropdown = screen.getByRole('combobox');
|
|
83
|
+
fireEvent.click(dropdown);
|
|
84
|
+
fireEvent.keyDown(dropdown, { key: 'ArrowDown' });
|
|
85
|
+
|
|
86
|
+
await waitFor(() => expect(screen.getByText('Provider 1')).toBeDefined());
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('should set the value and submit the form data', async () => {
|
|
90
|
+
const screen = render(
|
|
91
|
+
<QueryClientProvider client={client}>
|
|
92
|
+
<ControlledForm values={{ controlledAutocomplete: undefined }} onSubmit={onSubmit}>
|
|
93
|
+
<ControlledProviderAutocomplete
|
|
94
|
+
name="controlledProviderAutocomplete"
|
|
95
|
+
FieldProps={{
|
|
96
|
+
label: 'Provider Select',
|
|
97
|
+
helperText: 'Select a Provider from the list',
|
|
98
|
+
placeholder: 'Select...',
|
|
99
|
+
fullWidth: false,
|
|
100
|
+
}}
|
|
101
|
+
limit={10}
|
|
102
|
+
customerId="1234"
|
|
103
|
+
/>
|
|
104
|
+
<Actions />
|
|
105
|
+
<SubmittedValues />
|
|
106
|
+
</ControlledForm>
|
|
107
|
+
</QueryClientProvider>
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const dropdown = screen.getByRole('combobox');
|
|
111
|
+
fireEvent.click(dropdown);
|
|
112
|
+
fireEvent.keyDown(dropdown, { key: 'ArrowDown' });
|
|
113
|
+
|
|
114
|
+
await waitFor(() => screen.getByText('Provider 1'));
|
|
115
|
+
|
|
116
|
+
fireEvent.click(screen.getByText('Provider 1'));
|
|
117
|
+
|
|
118
|
+
fireEvent.click(screen.getByText('Submit'));
|
|
119
|
+
|
|
120
|
+
await waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(1));
|
|
121
|
+
const result = screen.getByTestId('result');
|
|
122
|
+
await waitFor(() => {
|
|
123
|
+
const controlledProviderAutocompleteValue = JSON.parse(result.innerHTML).controlledProviderAutocomplete;
|
|
124
|
+
expect(controlledProviderAutocompleteValue.uiDisplayName).toBe('Provider 1');
|
|
125
|
+
expect(controlledProviderAutocompleteValue.id).toBeDefined();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
});
|