@availity/mui-autocomplete 0.7.2 → 0.8.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 CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4
4
 
5
+ ## [0.8.0](https://github.com/Availity/element/compare/@availity/mui-autocomplete@0.7.2...@availity/mui-autocomplete@0.8.0) (2024-08-14)
6
+
7
+
8
+ ### Features
9
+
10
+ * **mui-autocomplete:** add CodesAutocomplete component ([b7da2bc](https://github.com/Availity/element/commit/b7da2bc716920a64c31b09482d8c9e8668b338e4))
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **mui-autocomplete:** remove console.log and add paging logic ([a0bd9dd](https://github.com/Availity/element/commit/a0bd9dd0a4191d29bb004edcf3958f183e71a188))
16
+
5
17
  ## [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
18
 
7
19
  ### 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 <Autocomplete FieldProps={{ label: 'Async Dropdown' }} loadOptions={loadOptions} />;
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 <ProviderAutocomplete customerId="1234" FieldProps={{ label: 'Provider Select', placeholder: 'Select...' }} />;
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.7.2",
3
+ "version": "0.8.0",
4
4
  "description": "Availity MUI Autocomplete Component - part of the @availity/element design system",
5
5
  "keywords": [
6
6
  "react",
@@ -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
+ };