@availity/mui-autocomplete 0.7.2 → 0.8.1
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 +18 -0
- package/README.md +45 -5
- package/package.json +5 -5
- package/src/lib/CodesAutocomplete.stories.tsx +51 -0
- package/src/lib/CodesAutocomplete.test.tsx +55 -0
- package/src/lib/CodesAutocomplete.tsx +66 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
## [0.8.1](https://github.com/Availity/element/compare/@availity/mui-autocomplete@0.8.0...@availity/mui-autocomplete@0.8.1) (2024-08-16)
|
|
6
|
+
|
|
7
|
+
### Dependency Updates
|
|
8
|
+
|
|
9
|
+
* `mui-form-utils` updated to version `0.8.0`
|
|
10
|
+
* `mui-textfield` updated to version `0.8.0`
|
|
11
|
+
## [0.8.0](https://github.com/Availity/element/compare/@availity/mui-autocomplete@0.7.2...@availity/mui-autocomplete@0.8.0) (2024-08-14)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* **mui-autocomplete:** add CodesAutocomplete component ([b7da2bc](https://github.com/Availity/element/commit/b7da2bc716920a64c31b09482d8c9e8668b338e4))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* **mui-autocomplete:** remove console.log and add paging logic ([a0bd9dd](https://github.com/Availity/element/commit/a0bd9dd0a4191d29bb004edcf3958f183e71a188))
|
|
22
|
+
|
|
5
23
|
## [0.7.2](https://github.com/Availity/element/compare/@availity/mui-autocomplete@0.7.1...@availity/mui-autocomplete@0.7.2) (2024-08-08)
|
|
6
24
|
|
|
7
25
|
### Dependency Updates
|
package/README.md
CHANGED
|
@@ -123,8 +123,6 @@ An `AsyncAutocomplete` component is exported for use cases that require fetching
|
|
|
123
123
|
|
|
124
124
|
The `loadOptions` function will be called when the user scrolls to the bottom of the dropdown. It will be passed the current offset 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`, a boolean `hasMore` property, and the `offset`. The returned `options` will be concatenated to the existing options array. `hasMore` tells the `AsyncAutocomplete` component whether or not it should call `loadOptions` again. Finally, the returned `offset` will be passed in the subsequent call to get the next set of options.
|
|
125
125
|
|
|
126
|
-
The `queryOptions` prop is available for passing in options to the `useInfiniteQuery` hook. One example of how this can be used is by using the `enabled` property. This can be used in cases where you would like to render the autocomplete, but are waiting on fetching the options. For example, if you need the user to fill out a section of the form before fetching the options for the autocomplete.
|
|
127
|
-
|
|
128
126
|
```jsx
|
|
129
127
|
import { Autocomplete } from '@availity/element';
|
|
130
128
|
import { callApi } from '../api';
|
|
@@ -139,8 +137,44 @@ const Example = () => {
|
|
|
139
137
|
offset,
|
|
140
138
|
};
|
|
141
139
|
};
|
|
140
|
+
return <Autocomplete FieldProps={{ label: 'Async Dropdown' }} loadOptions={loadOptions} />;
|
|
141
|
+
};
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The `queryOptions` prop is available for passing in options to the `useInfiniteQuery` hook. One example of how this can be used is by using the `enabled` property. This can be used in cases where you would like to render the autocomplete, but are waiting on fetching the options. For example, if you need the user to fill out a section of the form before fetching the options for the autocomplete.
|
|
145
|
+
|
|
146
|
+
`AsyncAutocomplete` uses `react-query` which means we can take advantage of the `queryKey` to fetch new options as needed. This functionality is exposed via the `watchParams` prop. This prop accepts an object. Whenever a value in the object is changed then page is reset to 0 and the api is called again.
|
|
147
|
+
|
|
148
|
+
```jsx
|
|
149
|
+
import { useState } from 'react';
|
|
150
|
+
import { Autocomplete, Button } from '@availity/element';
|
|
151
|
+
|
|
152
|
+
import { callApi } from '../api';
|
|
153
|
+
|
|
154
|
+
const Example = () => {
|
|
155
|
+
const [id, setId] = useState('');
|
|
156
|
+
|
|
157
|
+
const loadOptions = async (offset: number, limit: number) => {
|
|
158
|
+
const response = await callApi(id, offset, limit);
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
options: repsonse.data,
|
|
162
|
+
hasMore: response.data.totalCount > response.data.count,
|
|
163
|
+
offset,
|
|
164
|
+
};
|
|
165
|
+
};
|
|
142
166
|
|
|
143
|
-
return
|
|
167
|
+
return (
|
|
168
|
+
<>
|
|
169
|
+
<Button onClick={() => setId('123')}>Set ID</Button>
|
|
170
|
+
<Autocomplete
|
|
171
|
+
FieldProps={{ label: 'Async Dropdown' }}
|
|
172
|
+
loadOptions={loadOptions}
|
|
173
|
+
queryOptions={{ enabled: !!id }}
|
|
174
|
+
watchParams={{ id }}
|
|
175
|
+
/>
|
|
176
|
+
</>
|
|
177
|
+
);
|
|
144
178
|
};
|
|
145
179
|
```
|
|
146
180
|
|
|
@@ -171,7 +205,13 @@ The `queryKey` by default is `prov-autocomplete`.
|
|
|
171
205
|
```jsx
|
|
172
206
|
import { ProviderAutocomplete } from '@availity/element';
|
|
173
207
|
|
|
174
|
-
const Example = () => {
|
|
175
|
-
return
|
|
208
|
+
const Example = ({ customerId }: { customerId: string }) => {
|
|
209
|
+
return (
|
|
210
|
+
<ProviderAutocomplete
|
|
211
|
+
customerId={customerId}
|
|
212
|
+
FieldProps={{ label: 'Provider Select', placeholder: 'Select...' }}
|
|
213
|
+
watchParams={{ customerId }}
|
|
214
|
+
/>
|
|
215
|
+
);
|
|
176
216
|
};
|
|
177
217
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@availity/mui-autocomplete",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Availity MUI Autocomplete Component - part of the @availity/element design system",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@availity/api-axios": "^9.0.0",
|
|
40
|
-
"@availity/mui-form-utils": "^0.12.
|
|
41
|
-
"@availity/mui-textfield": "^0.6.
|
|
40
|
+
"@availity/mui-form-utils": "^0.12.5",
|
|
41
|
+
"@availity/mui-textfield": "^0.6.5",
|
|
42
42
|
"@mui/material": "^5.15.15",
|
|
43
43
|
"@tanstack/react-query": "^4.36.1",
|
|
44
44
|
"react": "18.2.0",
|
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"@availity/api-axios": "^8.0.7",
|
|
51
|
-
"@availity/mui-form-utils": "^0.12.
|
|
52
|
-
"@availity/mui-textfield": "^0.6.
|
|
51
|
+
"@availity/mui-form-utils": "^0.12.5",
|
|
52
|
+
"@availity/mui-textfield": "^0.6.5",
|
|
53
53
|
"@mui/material": "^5.11.9",
|
|
54
54
|
"@tanstack/react-query": "^4.36.1",
|
|
55
55
|
"react": ">=16.3.0"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Each exported component in the package should have its own stories file
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
4
|
+
|
|
5
|
+
import { CodesAutocomplete } from './CodesAutocomplete';
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof CodesAutocomplete> = {
|
|
8
|
+
title: 'Form Components/Autocomplete/CodesAutocomplete',
|
|
9
|
+
component: CodesAutocomplete,
|
|
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 client = new QueryClient({
|
|
26
|
+
defaultOptions: {
|
|
27
|
+
queries: {
|
|
28
|
+
refetchOnWindowFocus: false,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export const _CodesAutocomplete: StoryObj<typeof CodesAutocomplete> = {
|
|
34
|
+
render: (args) => {
|
|
35
|
+
return (
|
|
36
|
+
<QueryClientProvider client={client}>
|
|
37
|
+
<CodesAutocomplete {...args} />
|
|
38
|
+
</QueryClientProvider>
|
|
39
|
+
);
|
|
40
|
+
},
|
|
41
|
+
args: {
|
|
42
|
+
list: 'ABC',
|
|
43
|
+
FieldProps: {
|
|
44
|
+
label: 'Code Select',
|
|
45
|
+
helperText: 'Select a code from the list',
|
|
46
|
+
placeholder: 'Select...',
|
|
47
|
+
fullWidth: false,
|
|
48
|
+
},
|
|
49
|
+
limit: 15,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
5
|
+
|
|
6
|
+
import { CodesAutocomplete } from './CodesAutocomplete';
|
|
7
|
+
|
|
8
|
+
const client = new QueryClient();
|
|
9
|
+
|
|
10
|
+
describe('CodesAutocomplete', () => {
|
|
11
|
+
beforeAll(() => {
|
|
12
|
+
// Start the interception.
|
|
13
|
+
server.listen();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
// Remove any handlers you may have added
|
|
18
|
+
// in individual tests (runtime handlers).
|
|
19
|
+
server.resetHandlers();
|
|
20
|
+
jest.restoreAllMocks();
|
|
21
|
+
client.clear();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('providers are fetched and displayed by name', async () => {
|
|
25
|
+
render(
|
|
26
|
+
<QueryClientProvider client={client}>
|
|
27
|
+
<CodesAutocomplete list="123" FieldProps={{ label: 'Test' }} />
|
|
28
|
+
</QueryClientProvider>
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const input = screen.getByRole('combobox');
|
|
32
|
+
fireEvent.click(input);
|
|
33
|
+
fireEvent.keyDown(input, { key: 'ArrowDown' });
|
|
34
|
+
|
|
35
|
+
await waitFor(() => {
|
|
36
|
+
expect(screen.getByText('171100000X - Acupuncturist')).toBeDefined();
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('codes are not fetched when list is not present', async () => {
|
|
41
|
+
render(
|
|
42
|
+
<QueryClientProvider client={client}>
|
|
43
|
+
<CodesAutocomplete list="" FieldProps={{ label: 'Test' }} />
|
|
44
|
+
</QueryClientProvider>
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const input = screen.getByRole('combobox');
|
|
48
|
+
fireEvent.click(input);
|
|
49
|
+
fireEvent.keyDown(input, { key: 'ArrowDown' });
|
|
50
|
+
|
|
51
|
+
await waitFor(() => {
|
|
52
|
+
expect(screen.getByText('No options')).toBeDefined();
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { avCodesApi, ApiConfig } from '@availity/api-axios';
|
|
2
|
+
import type { ChipTypeMap } from '@mui/material/Chip';
|
|
3
|
+
|
|
4
|
+
import { AsyncAutocomplete, AsyncAutocompleteProps } from './AsyncAutocomplete';
|
|
5
|
+
import type { Optional } from './util';
|
|
6
|
+
|
|
7
|
+
type Code = {
|
|
8
|
+
code: string;
|
|
9
|
+
value: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const fetchCodes = async (config: ApiConfig) => {
|
|
13
|
+
const resp = await avCodesApi.query(config);
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
options: resp.data.codes as Code[],
|
|
17
|
+
hasMore: config.params.offset + config.params.limit < resp.data.totalCount,
|
|
18
|
+
offset: config.params.offset,
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export interface CodesAutocompleteProps<
|
|
23
|
+
Option = Code,
|
|
24
|
+
Multiple extends boolean | undefined = false,
|
|
25
|
+
DisableClearable extends boolean | undefined = false,
|
|
26
|
+
FreeSolo extends boolean | undefined = false,
|
|
27
|
+
ChipComponent extends React.ElementType = ChipTypeMap['defaultComponent'],
|
|
28
|
+
> extends Omit<
|
|
29
|
+
Optional<AsyncAutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>, 'queryKey'>,
|
|
30
|
+
'loadOptions'
|
|
31
|
+
> {
|
|
32
|
+
list: string;
|
|
33
|
+
/** Config passed to the AvCodesApi.query function */
|
|
34
|
+
apiConfig?: ApiConfig;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const CodesAutocomplete = ({
|
|
38
|
+
apiConfig = {},
|
|
39
|
+
queryOptions,
|
|
40
|
+
queryKey = 'codes-autocomplete',
|
|
41
|
+
list,
|
|
42
|
+
watchParams,
|
|
43
|
+
...rest
|
|
44
|
+
}: CodesAutocompleteProps) => {
|
|
45
|
+
const handleLoadOptions = async (offset: number, limit: number, inputValue: string) => {
|
|
46
|
+
const resp = await fetchCodes({
|
|
47
|
+
...apiConfig,
|
|
48
|
+
params: { ...apiConfig.params, list, offset, limit, q: inputValue },
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return resp;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const handleGetOptionLabel = (option: Code) => [option.code, option.value].filter(Boolean).join(' - ');
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<AsyncAutocomplete
|
|
58
|
+
getOptionLabel={handleGetOptionLabel}
|
|
59
|
+
queryKey={queryKey}
|
|
60
|
+
queryOptions={{ enabled: !!list, ...queryOptions }}
|
|
61
|
+
watchParams={{ list, ...watchParams }}
|
|
62
|
+
{...rest}
|
|
63
|
+
loadOptions={handleLoadOptions}
|
|
64
|
+
/>
|
|
65
|
+
);
|
|
66
|
+
};
|