@availity/mui-autocomplete 0.4.6 → 0.5.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 +18 -4
- package/package.json +1 -1
- package/src/lib/AsyncAutocomplete.stories.tsx +85 -0
- package/src/lib/AsyncAutocomplete.test.tsx +66 -41
- package/src/lib/Autocomplete.stories.tsx +0 -114
- package/src/lib/OrganizationAutocomplete.stories.tsx +37 -0
- package/src/lib/OrganizationAutocomplete.test.tsx +31 -0
- package/src/lib/OrganizationAutocomplete.tsx +52 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
## [0.5.0](https://github.com/Availity/element/compare/@availity/mui-autocomplete@0.4.6...@availity/mui-autocomplete@0.5.0) (2024-06-20)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **mui-autocomplete:** add OrganizationAutocomplete component ([9f3ea07](https://github.com/Availity/element/commit/9f3ea0753e0147d7e1aeaf73230716046caba01e))
|
|
11
|
+
|
|
5
12
|
## [0.4.6](https://github.com/Availity/element/compare/@availity/mui-autocomplete@0.4.5...@availity/mui-autocomplete@0.4.6) (2024-06-14)
|
|
6
13
|
|
|
7
14
|
### Dependency Updates
|
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ The `Autcomplete` component can be used standalone or with a form state library
|
|
|
54
54
|
|
|
55
55
|
`Autocomplete` uses the `TextField` component to render the input. You must pass your field related props: `label`, `helperText`, `error`, etc. to the the `FieldProps` prop.
|
|
56
56
|
|
|
57
|
-
```
|
|
57
|
+
```jsx
|
|
58
58
|
import { Autocomplete } from '@availity/element';
|
|
59
59
|
|
|
60
60
|
const MyAutocomplete = () => {
|
|
@@ -74,13 +74,13 @@ const MyAutocomplete = () => {
|
|
|
74
74
|
|
|
75
75
|
#### Direct import
|
|
76
76
|
|
|
77
|
-
```
|
|
77
|
+
```jsx
|
|
78
78
|
import { Autocomplete } from '@availity/mui-autocomplete';
|
|
79
79
|
```
|
|
80
80
|
|
|
81
81
|
#### Usage with `react-hook-form`
|
|
82
82
|
|
|
83
|
-
```
|
|
83
|
+
```jsx
|
|
84
84
|
import { useForm, Controller } from 'react-hook-form';
|
|
85
85
|
import { Autocomplete, Button } from '@availity/element';
|
|
86
86
|
|
|
@@ -123,7 +123,7 @@ const Form = () => {
|
|
|
123
123
|
|
|
124
124
|
An `AsyncAutocomplete` component is exported for use cases that require fetching paginated results from an api. You will need to use the `loadOptions` prop. The `loadOptions` function will be called when the user scrolls to the bottom of the dropdown. It will be passed the current page and limit. The `limit` prop controls what is passed to `loadOptions` and is defaulted to `50`. The `loadOptions` function must return an object that has an array of `options` and a `hasMore` property. `hasMore` tells the `AsyncAutocomplete` component whether or not it should call `loadOptions` again. The returned `options` will be concatenated to the existing options array.
|
|
125
125
|
|
|
126
|
-
```
|
|
126
|
+
```jsx
|
|
127
127
|
import { Autocomplete } from '@availity/element';
|
|
128
128
|
|
|
129
129
|
const Example = () => {
|
|
@@ -139,3 +139,17 @@ const Example = () => {
|
|
|
139
139
|
return <Autocomplete FieldProps={{ label: 'Async Dropdown' }} loadOptions={loadOptions} />;
|
|
140
140
|
};
|
|
141
141
|
```
|
|
142
|
+
|
|
143
|
+
#### `OrganizationAutocomplete` Usage
|
|
144
|
+
|
|
145
|
+
The `OrganizationAutocomplete` component is an extension of the `AsyncAutocomplete` component which calls our Organizations endpoint. The props are the same as `AsyncAutocomplete` except you do not need to pass a function to `loadOptions`. This has already been done for you. The component uses the `name` from the returned organizations as the label for the option. Pass in your own `getOptionLabel` function if you would like to change the label.
|
|
146
|
+
|
|
147
|
+
If you need to add params, headers, or other data to the api call then the `apiConfig` prop is available. This allows for passing in the same options you would to the `getOrganizations`. For example, `permissionIds` or `resourceIds`.
|
|
148
|
+
|
|
149
|
+
```jsx
|
|
150
|
+
import { OrganizationAutocomplete } from '@availity/element';
|
|
151
|
+
|
|
152
|
+
const Example = () => {
|
|
153
|
+
return <OrganizationAutocomplete FieldProps={{ label: 'Organization Select', placeholder: 'Select...' }} />;
|
|
154
|
+
};
|
|
155
|
+
```
|
package/package.json
CHANGED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Each exported component in the package should have its own stories file
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import AvApi, { ApiConfig } from '@availity/api-axios';
|
|
4
|
+
|
|
5
|
+
import { AsyncAutocomplete } from './AsyncAutocomplete';
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof AsyncAutocomplete> = {
|
|
8
|
+
title: 'Form Components/Autocomplete/AsyncAutocomplete',
|
|
9
|
+
component: AsyncAutocomplete,
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
args: {
|
|
12
|
+
id: 'example',
|
|
13
|
+
},
|
|
14
|
+
argTypes: {
|
|
15
|
+
multiple: {
|
|
16
|
+
table: {
|
|
17
|
+
disable: true,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default meta;
|
|
24
|
+
|
|
25
|
+
const api = new AvApi({ name: 'example' } as ApiConfig);
|
|
26
|
+
|
|
27
|
+
type Option = {
|
|
28
|
+
label: string;
|
|
29
|
+
value: number;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type ExampleResponse = {
|
|
33
|
+
totalCount: number;
|
|
34
|
+
options: Option[];
|
|
35
|
+
count: number;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const getResults = async (page: number, limit: number) => {
|
|
39
|
+
const offset = page * limit;
|
|
40
|
+
try {
|
|
41
|
+
const resp = await api.post<ExampleResponse>({ offset, limit }, { params: {} });
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
totalCount: resp.data.totalCount,
|
|
45
|
+
offset,
|
|
46
|
+
limit,
|
|
47
|
+
options: resp.data.options,
|
|
48
|
+
count: resp.data.count,
|
|
49
|
+
};
|
|
50
|
+
} catch {
|
|
51
|
+
return {
|
|
52
|
+
totalCount: 0,
|
|
53
|
+
offset,
|
|
54
|
+
limit,
|
|
55
|
+
options: [],
|
|
56
|
+
count: 0,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const loadOptions = async (page: number, limit: number) => {
|
|
62
|
+
const { options, totalCount, offset } = await getResults(page, limit);
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
options,
|
|
66
|
+
hasMore: offset + limit < totalCount,
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const _Async: StoryObj<typeof AsyncAutocomplete> = {
|
|
71
|
+
render: (args) => {
|
|
72
|
+
return <AsyncAutocomplete {...args} />;
|
|
73
|
+
},
|
|
74
|
+
parameters: {
|
|
75
|
+
controls: {
|
|
76
|
+
exclude: /loading(?!Text)|options/,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
args: {
|
|
80
|
+
FieldProps: { label: 'Async Select', helperText: 'Helper Text', fullWidth: false },
|
|
81
|
+
getOptionLabel: (val: Option) => val.label,
|
|
82
|
+
loadOptions,
|
|
83
|
+
limit: 10,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
@@ -1,28 +1,75 @@
|
|
|
1
1
|
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
2
|
+
import AvApi, { ApiConfig } from '@availity/api-axios';
|
|
3
|
+
/* eslint-disable @nx/enforce-module-boundaries */
|
|
4
|
+
import { server } from '@availity/mock/src/lib/server';
|
|
5
|
+
|
|
2
6
|
import { AsyncAutocomplete } from './AsyncAutocomplete';
|
|
3
7
|
|
|
8
|
+
const api = new AvApi({ name: 'example' } as ApiConfig);
|
|
9
|
+
|
|
10
|
+
type Option = {
|
|
11
|
+
label: string;
|
|
12
|
+
value: number;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type ExampleResponse = {
|
|
16
|
+
totalCount: number;
|
|
17
|
+
options: Option[];
|
|
18
|
+
count: number;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const getResults = async (page: number, limit: number) => {
|
|
22
|
+
const offset = page * limit;
|
|
23
|
+
try {
|
|
24
|
+
const resp = await api.post<ExampleResponse>({ offset, limit }, { params: {} });
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
totalCount: resp.data.totalCount,
|
|
28
|
+
offset,
|
|
29
|
+
limit,
|
|
30
|
+
options: resp.data.options,
|
|
31
|
+
count: resp.data.count,
|
|
32
|
+
};
|
|
33
|
+
} catch {
|
|
34
|
+
return {
|
|
35
|
+
totalCount: 0,
|
|
36
|
+
offset,
|
|
37
|
+
limit,
|
|
38
|
+
options: [],
|
|
39
|
+
count: 0,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const loadOptions = async (page: number, limit: number) => {
|
|
45
|
+
const { options, totalCount, offset } = await getResults(page, limit);
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
options,
|
|
49
|
+
hasMore: offset + limit < totalCount,
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
|
|
4
53
|
describe('AsyncAutocomplete', () => {
|
|
54
|
+
beforeAll(() => {
|
|
55
|
+
// Start the interception.
|
|
56
|
+
server.listen();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
afterEach(() => {
|
|
60
|
+
// Remove any handlers you may have added
|
|
61
|
+
// in individual tests (runtime handlers).
|
|
62
|
+
server.resetHandlers();
|
|
63
|
+
jest.restoreAllMocks();
|
|
64
|
+
});
|
|
65
|
+
|
|
5
66
|
test('should render successfully', () => {
|
|
6
|
-
const { getByLabelText } = render(
|
|
7
|
-
|
|
8
|
-
FieldProps={{ label: 'Test' }}
|
|
9
|
-
loadOptions={async () => ({
|
|
10
|
-
options: ['1', '2', '3'],
|
|
11
|
-
hasMore: false,
|
|
12
|
-
})}
|
|
13
|
-
/>
|
|
14
|
-
);
|
|
67
|
+
const { getByLabelText } = render(<AsyncAutocomplete FieldProps={{ label: 'Test' }} loadOptions={loadOptions} />);
|
|
68
|
+
|
|
15
69
|
expect(getByLabelText('Test')).toBeTruthy();
|
|
16
70
|
});
|
|
17
71
|
|
|
18
72
|
test('options should be available', async () => {
|
|
19
|
-
const loadOptions = () =>
|
|
20
|
-
Promise.resolve({
|
|
21
|
-
options: [{ label: 'Option 1' }],
|
|
22
|
-
getOptionLabel: (option: { label: string }) => option.label,
|
|
23
|
-
hasMore: false,
|
|
24
|
-
});
|
|
25
|
-
|
|
26
73
|
render(<AsyncAutocomplete loadOptions={loadOptions} FieldProps={{ label: 'Test' }} />);
|
|
27
74
|
|
|
28
75
|
const input = screen.getByRole('combobox');
|
|
@@ -41,19 +88,7 @@ describe('AsyncAutocomplete', () => {
|
|
|
41
88
|
});
|
|
42
89
|
|
|
43
90
|
test('should call loadOptions when scroll to the bottom', async () => {
|
|
44
|
-
|
|
45
|
-
loadOptions.mockResolvedValueOnce({
|
|
46
|
-
options: [
|
|
47
|
-
{ label: 'Option 1' },
|
|
48
|
-
{ label: 'Option 2' },
|
|
49
|
-
{ label: 'Option 3' },
|
|
50
|
-
{ label: 'Option 4' },
|
|
51
|
-
{ label: 'Option 5' },
|
|
52
|
-
{ label: 'Option 6' },
|
|
53
|
-
],
|
|
54
|
-
hasMore: true,
|
|
55
|
-
});
|
|
56
|
-
render(<AsyncAutocomplete loadOptions={loadOptions} FieldProps={{ label: 'Test' }} />);
|
|
91
|
+
render(<AsyncAutocomplete loadOptions={loadOptions} limit={10} FieldProps={{ label: 'Test' }} />);
|
|
57
92
|
|
|
58
93
|
const input = screen.getByRole('combobox');
|
|
59
94
|
fireEvent.click(input);
|
|
@@ -63,24 +98,14 @@ describe('AsyncAutocomplete', () => {
|
|
|
63
98
|
expect(screen.getByText('Option 1')).toBeDefined();
|
|
64
99
|
});
|
|
65
100
|
|
|
66
|
-
expect(loadOptions).toHaveBeenCalled();
|
|
67
|
-
expect(loadOptions).toHaveBeenCalledTimes(1);
|
|
68
|
-
expect(loadOptions).toHaveBeenCalledWith(0, 50);
|
|
69
|
-
|
|
70
|
-
loadOptions.mockResolvedValueOnce({
|
|
71
|
-
options: [{ label: 'Option 7' }],
|
|
72
|
-
hasMore: false,
|
|
73
|
-
});
|
|
74
|
-
|
|
75
101
|
await act(async () => {
|
|
76
102
|
const options = await screen.findByRole('listbox');
|
|
77
103
|
fireEvent.scroll(options, { target: { scrollTop: options.scrollHeight } });
|
|
78
104
|
});
|
|
79
105
|
|
|
80
106
|
await waitFor(() => {
|
|
81
|
-
expect(
|
|
82
|
-
expect(
|
|
83
|
-
expect(loadOptions).toHaveBeenLastCalledWith(1, 50);
|
|
107
|
+
expect(screen.getByText('Option 10')).toBeDefined();
|
|
108
|
+
expect(() => screen.getByText('Option 20')).toThrowError();
|
|
84
109
|
});
|
|
85
110
|
});
|
|
86
111
|
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Each exported component in the package should have its own stories file
|
|
2
2
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
3
|
import { Autocomplete } from './Autocomplete';
|
|
4
|
-
import { AsyncAutocomplete } from './AsyncAutocomplete';
|
|
5
4
|
|
|
6
5
|
const meta: Meta<typeof Autocomplete> = {
|
|
7
6
|
title: 'Form Components/Autocomplete/Autocomplete',
|
|
@@ -43,116 +42,3 @@ export const _Multi: StoryObj<typeof Autocomplete> = {
|
|
|
43
42
|
multiple: true,
|
|
44
43
|
},
|
|
45
44
|
};
|
|
46
|
-
|
|
47
|
-
type Org = {
|
|
48
|
-
id: string;
|
|
49
|
-
name: string;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const organizations: Org[] = [
|
|
53
|
-
{
|
|
54
|
-
id: '1',
|
|
55
|
-
name: 'Org 1',
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
id: '2',
|
|
59
|
-
name: 'Org 2',
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
id: '3',
|
|
63
|
-
name: 'Org 3',
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
id: '4',
|
|
67
|
-
name: 'Org 4',
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
id: '5',
|
|
71
|
-
name: 'Org 5',
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
id: '6',
|
|
75
|
-
name: 'Org 6',
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
id: '7',
|
|
79
|
-
name: 'Org 7',
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
id: '8',
|
|
83
|
-
name: 'Org 8',
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
id: '9',
|
|
87
|
-
name: 'Org 9',
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
id: '10',
|
|
91
|
-
name: 'Org 10',
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
id: '11',
|
|
95
|
-
name: 'Org 11',
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
id: '12',
|
|
99
|
-
name: 'Org 12',
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
id: '13',
|
|
103
|
-
name: 'Org 13',
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
id: '14',
|
|
107
|
-
name: 'Org 14',
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
id: '15',
|
|
111
|
-
name: 'Org 15',
|
|
112
|
-
},
|
|
113
|
-
];
|
|
114
|
-
|
|
115
|
-
async function sleep(duration = 2500) {
|
|
116
|
-
await new Promise((resolve) => setTimeout(resolve, duration));
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const getResults = (page: number, limit: number) => {
|
|
120
|
-
const offset = page * limit;
|
|
121
|
-
const orgs = organizations.slice(page * offset, page * offset + limit);
|
|
122
|
-
|
|
123
|
-
return {
|
|
124
|
-
totalCount: organizations.length,
|
|
125
|
-
offset,
|
|
126
|
-
limit,
|
|
127
|
-
orgs,
|
|
128
|
-
count: orgs.length,
|
|
129
|
-
};
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
const loadOptions = async (page: number, limit: number) => {
|
|
133
|
-
await sleep(1000);
|
|
134
|
-
|
|
135
|
-
const { orgs, totalCount, offset } = getResults(page, limit);
|
|
136
|
-
|
|
137
|
-
return {
|
|
138
|
-
options: orgs,
|
|
139
|
-
hasMore: offset + limit < totalCount,
|
|
140
|
-
};
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
export const _Async: StoryObj<typeof AsyncAutocomplete> = {
|
|
144
|
-
render: (args) => {
|
|
145
|
-
return <AsyncAutocomplete {...args} />;
|
|
146
|
-
},
|
|
147
|
-
parameters: {
|
|
148
|
-
controls: {
|
|
149
|
-
exclude: /loading(?!Text)|options/,
|
|
150
|
-
},
|
|
151
|
-
},
|
|
152
|
-
args: {
|
|
153
|
-
FieldProps: { label: 'Async Select', helperText: 'Helper Text', fullWidth: false },
|
|
154
|
-
getOptionLabel: (val: Org) => val.name,
|
|
155
|
-
loadOptions,
|
|
156
|
-
limit: 10,
|
|
157
|
-
},
|
|
158
|
-
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Each exported component in the package should have its own stories file
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
|
|
4
|
+
import { OrganizationAutocomplete } from './OrganizationAutocomplete';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof OrganizationAutocomplete> = {
|
|
7
|
+
title: 'Form Components/Autocomplete/OrganizationAutocomplete',
|
|
8
|
+
component: OrganizationAutocomplete,
|
|
9
|
+
tags: ['autodocs'],
|
|
10
|
+
args: {
|
|
11
|
+
id: 'example',
|
|
12
|
+
},
|
|
13
|
+
argTypes: {
|
|
14
|
+
multiple: {
|
|
15
|
+
table: {
|
|
16
|
+
disable: true,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
|
|
24
|
+
export const _OrganizationAutocomplete: StoryObj<typeof OrganizationAutocomplete> = {
|
|
25
|
+
render: (args) => {
|
|
26
|
+
return <OrganizationAutocomplete {...args} />;
|
|
27
|
+
},
|
|
28
|
+
args: {
|
|
29
|
+
FieldProps: {
|
|
30
|
+
label: 'Organization Select',
|
|
31
|
+
helperText: 'Select an Organization from the list',
|
|
32
|
+
placeholder: 'Select...',
|
|
33
|
+
fullWidth: false,
|
|
34
|
+
},
|
|
35
|
+
limit: 15,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
2
|
+
/* eslint-disable @nx/enforce-module-boundaries */
|
|
3
|
+
import { server } from '@availity/mock/src/lib/server';
|
|
4
|
+
|
|
5
|
+
import { OrganizationAutocomplete } from './OrganizationAutocomplete';
|
|
6
|
+
|
|
7
|
+
describe('OrganizationAutocomplete', () => {
|
|
8
|
+
beforeAll(() => {
|
|
9
|
+
// Start the interception.
|
|
10
|
+
server.listen();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
// Remove any handlers you may have added
|
|
15
|
+
// in individual tests (runtime handlers).
|
|
16
|
+
server.resetHandlers();
|
|
17
|
+
jest.restoreAllMocks();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('organizations are fetched and displayed by name', async () => {
|
|
21
|
+
render(<OrganizationAutocomplete FieldProps={{ label: 'Test' }} />);
|
|
22
|
+
|
|
23
|
+
const input = screen.getByRole('combobox');
|
|
24
|
+
fireEvent.click(input);
|
|
25
|
+
fireEvent.keyDown(input, { key: 'ArrowDown' });
|
|
26
|
+
|
|
27
|
+
await waitFor(() => {
|
|
28
|
+
expect(screen.getByText('Organization 1')).toBeDefined();
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { avOrganizationsApi, ApiConfig } from '@availity/api-axios';
|
|
2
|
+
import type { ChipTypeMap } from '@mui/material/Chip';
|
|
3
|
+
|
|
4
|
+
import { AsyncAutocomplete, AsyncAutocompleteProps } from './AsyncAutocomplete';
|
|
5
|
+
|
|
6
|
+
export type Organization = {
|
|
7
|
+
customerId: string;
|
|
8
|
+
name: string;
|
|
9
|
+
id: string;
|
|
10
|
+
createDate: string;
|
|
11
|
+
links: Record<string, Record<string, string>>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const fetchOrgs = async (config: ApiConfig): Promise<{ options: Organization[]; hasMore: boolean }> => {
|
|
15
|
+
try {
|
|
16
|
+
const resp = await avOrganizationsApi.getOrganizations(config);
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
options: resp.data.organizations as Organization[],
|
|
20
|
+
hasMore: config.params.offset + config.params.limit < resp.data.totalCount,
|
|
21
|
+
};
|
|
22
|
+
} catch {
|
|
23
|
+
return {
|
|
24
|
+
options: [],
|
|
25
|
+
hasMore: false,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type OrgAutocompleteProps<
|
|
31
|
+
Option = Organization,
|
|
32
|
+
Multiple extends boolean | undefined = false,
|
|
33
|
+
DisableClearable extends boolean | undefined = false,
|
|
34
|
+
FreeSolo extends boolean | undefined = false,
|
|
35
|
+
ChipComponent extends React.ElementType = ChipTypeMap['defaultComponent'],
|
|
36
|
+
> = {
|
|
37
|
+
apiConfig?: ApiConfig;
|
|
38
|
+
} & Omit<AsyncAutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>, 'loadOptions'>;
|
|
39
|
+
|
|
40
|
+
export const OrganizationAutocomplete = ({ apiConfig = {}, ...rest }: OrgAutocompleteProps) => {
|
|
41
|
+
const handleLoadOptions = async (page: number, limit: number) => {
|
|
42
|
+
const offset = page * limit;
|
|
43
|
+
|
|
44
|
+
const resp = await fetchOrgs({ ...apiConfig, params: { dropdown: true, ...apiConfig.params, offset, limit } });
|
|
45
|
+
|
|
46
|
+
return resp;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const handleGetOptionLabel = (org: Organization) => org.name;
|
|
50
|
+
|
|
51
|
+
return <AsyncAutocomplete getOptionLabel={handleGetOptionLabel} {...rest} loadOptions={handleLoadOptions} />;
|
|
52
|
+
};
|