@availity/mui-autocomplete 0.6.3 → 0.7.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,24 @@
2
2
 
3
3
  This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4
4
 
5
+ ## [0.7.0](https://github.com/Availity/element/compare/@availity/mui-autocomplete@0.6.4...@availity/mui-autocomplete@0.7.0) (2024-08-01)
6
+
7
+
8
+ ### Features
9
+
10
+ * **mui-autocomplete:** add watchParams and inputValue ([1479d65](https://github.com/Availity/element/commit/1479d65830e2878207283639948423cf04fc5bd3))
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **mui-autocomplete:** fix unit test ([b8f73ea](https://github.com/Availity/element/commit/b8f73eaf38fad08049dd384fc7f13985b999dd8b))
16
+
17
+ ## [0.6.4](https://github.com/Availity/element/compare/@availity/mui-autocomplete@0.6.3...@availity/mui-autocomplete@0.6.4) (2024-08-01)
18
+
19
+ ### Dependency Updates
20
+
21
+ * `mui-form-utils` updated to version `0.6.3`
22
+ * `mui-textfield` updated to version `0.6.3`
5
23
  ## [0.6.3](https://github.com/Availity/element/compare/@availity/mui-autocomplete@0.6.2...@availity/mui-autocomplete@0.6.3) (2024-07-22)
6
24
 
7
25
  ### Dependency Updates
package/dist/index.d.mts CHANGED
@@ -15,7 +15,7 @@ declare const Autocomplete: <T, Multiple extends boolean | undefined = false, Di
15
15
 
16
16
  interface AsyncAutocompleteProps<Option, Multiple extends boolean | undefined, DisableClearable extends boolean | undefined, FreeSolo extends boolean | undefined, ChipComponent extends React.ElementType = ChipTypeMap['defaultComponent']> extends Omit<AutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>, 'options' | 'disableListWrap' | 'loading'> {
17
17
  /** Function that is called to fetch the options for the list. Returns a promise with options, hasMore, and offset */
18
- loadOptions: (offset: number, limit: number) => Promise<{
18
+ loadOptions: (offset: number, limit: number, inputValue?: string) => Promise<{
19
19
  options: Option[];
20
20
  hasMore: boolean;
21
21
  offset: number;
@@ -31,8 +31,12 @@ interface AsyncAutocompleteProps<Option, Multiple extends boolean | undefined, D
31
31
  hasMore: boolean;
32
32
  offset: number;
33
33
  }>;
34
+ /** Object of parameters used for the cacheKey. Options are re-reftched when a value in the object changes */
35
+ watchParams?: Record<string, unknown>;
36
+ /** Time to wait before searching with the input value typed into the component */
37
+ debounceTimeout?: number;
34
38
  }
35
- declare const AsyncAutocomplete: <Option, Multiple extends boolean | undefined = false, DisableClearable extends boolean | undefined = false, FreeSolo extends boolean | undefined = false, ChipComponent extends react.ElementType<any, keyof react.JSX.IntrinsicElements> = "div">({ loadOptions, limit, queryKey, ListboxProps, queryOptions, ...rest }: AsyncAutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>) => react_jsx_runtime.JSX.Element;
39
+ declare const AsyncAutocomplete: <Option, Multiple extends boolean | undefined = false, DisableClearable extends boolean | undefined = false, FreeSolo extends boolean | undefined = false, ChipComponent extends react.ElementType<any, keyof react.JSX.IntrinsicElements> = "div">({ loadOptions, limit, queryKey, ListboxProps, queryOptions, watchParams, debounceTimeout, FieldProps, ...rest }: AsyncAutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>) => react_jsx_runtime.JSX.Element;
36
40
 
37
41
  type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
38
42
 
@@ -52,10 +56,51 @@ type Provider = {
52
56
  id: string;
53
57
  businessName: string;
54
58
  uiDisplayName: string;
55
- aytypical: boolean;
59
+ lastName?: string;
60
+ firstName?: string;
61
+ payerAssignedIdentifiers?: {
62
+ payerId: string;
63
+ identifier: string;
64
+ }[];
65
+ atypical: boolean;
66
+ npi: string;
67
+ customerIds: string[];
68
+ roles: {
69
+ code: string;
70
+ value: string;
71
+ }[];
72
+ primaryPhone: {
73
+ internationalCellularCode: string;
74
+ areaCode: string;
75
+ phoneNumber: string;
76
+ };
77
+ primaryFax: {
78
+ internationalCellularCode: string;
79
+ areaCode: string;
80
+ phoneNumber: string;
81
+ };
82
+ taxId?: string;
83
+ ssn?: string;
84
+ primaryAddress: {
85
+ line1: string;
86
+ line2: string;
87
+ city: string;
88
+ state: string;
89
+ stateCode: string;
90
+ zip: {
91
+ code: string;
92
+ addon: string;
93
+ };
94
+ };
95
+ primarySpecialty?: {
96
+ code: string;
97
+ value: string;
98
+ };
56
99
  };
57
100
  interface ProviderAutocompleteProps<Option = Provider, Multiple extends boolean | undefined = false, DisableClearable extends boolean | undefined = false, FreeSolo extends boolean | undefined = false, ChipComponent extends React.ElementType = ChipTypeMap['defaultComponent']> extends Omit<Optional<AsyncAutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>, 'queryKey'>, 'loadOptions'> {
101
+ /** Customer ID of the Organization you are requesting the providers for */
58
102
  customerId: string;
103
+ /** Config passed to the AvProvidersApi.getProviders function */
59
104
  apiConfig?: ApiConfig;
60
105
  }
61
106
  declare const ProviderAutocomplete: ({ apiConfig, customerId, queryKey, ...rest }: ProviderAutocompleteProps) => react_jsx_runtime.JSX.Element;
package/dist/index.d.ts CHANGED
@@ -15,7 +15,7 @@ declare const Autocomplete: <T, Multiple extends boolean | undefined = false, Di
15
15
 
16
16
  interface AsyncAutocompleteProps<Option, Multiple extends boolean | undefined, DisableClearable extends boolean | undefined, FreeSolo extends boolean | undefined, ChipComponent extends React.ElementType = ChipTypeMap['defaultComponent']> extends Omit<AutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>, 'options' | 'disableListWrap' | 'loading'> {
17
17
  /** Function that is called to fetch the options for the list. Returns a promise with options, hasMore, and offset */
18
- loadOptions: (offset: number, limit: number) => Promise<{
18
+ loadOptions: (offset: number, limit: number, inputValue?: string) => Promise<{
19
19
  options: Option[];
20
20
  hasMore: boolean;
21
21
  offset: number;
@@ -31,8 +31,12 @@ interface AsyncAutocompleteProps<Option, Multiple extends boolean | undefined, D
31
31
  hasMore: boolean;
32
32
  offset: number;
33
33
  }>;
34
+ /** Object of parameters used for the cacheKey. Options are re-reftched when a value in the object changes */
35
+ watchParams?: Record<string, unknown>;
36
+ /** Time to wait before searching with the input value typed into the component */
37
+ debounceTimeout?: number;
34
38
  }
35
- declare const AsyncAutocomplete: <Option, Multiple extends boolean | undefined = false, DisableClearable extends boolean | undefined = false, FreeSolo extends boolean | undefined = false, ChipComponent extends react.ElementType<any, keyof react.JSX.IntrinsicElements> = "div">({ loadOptions, limit, queryKey, ListboxProps, queryOptions, ...rest }: AsyncAutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>) => react_jsx_runtime.JSX.Element;
39
+ declare const AsyncAutocomplete: <Option, Multiple extends boolean | undefined = false, DisableClearable extends boolean | undefined = false, FreeSolo extends boolean | undefined = false, ChipComponent extends react.ElementType<any, keyof react.JSX.IntrinsicElements> = "div">({ loadOptions, limit, queryKey, ListboxProps, queryOptions, watchParams, debounceTimeout, FieldProps, ...rest }: AsyncAutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>) => react_jsx_runtime.JSX.Element;
36
40
 
37
41
  type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
38
42
 
@@ -52,10 +56,51 @@ type Provider = {
52
56
  id: string;
53
57
  businessName: string;
54
58
  uiDisplayName: string;
55
- aytypical: boolean;
59
+ lastName?: string;
60
+ firstName?: string;
61
+ payerAssignedIdentifiers?: {
62
+ payerId: string;
63
+ identifier: string;
64
+ }[];
65
+ atypical: boolean;
66
+ npi: string;
67
+ customerIds: string[];
68
+ roles: {
69
+ code: string;
70
+ value: string;
71
+ }[];
72
+ primaryPhone: {
73
+ internationalCellularCode: string;
74
+ areaCode: string;
75
+ phoneNumber: string;
76
+ };
77
+ primaryFax: {
78
+ internationalCellularCode: string;
79
+ areaCode: string;
80
+ phoneNumber: string;
81
+ };
82
+ taxId?: string;
83
+ ssn?: string;
84
+ primaryAddress: {
85
+ line1: string;
86
+ line2: string;
87
+ city: string;
88
+ state: string;
89
+ stateCode: string;
90
+ zip: {
91
+ code: string;
92
+ addon: string;
93
+ };
94
+ };
95
+ primarySpecialty?: {
96
+ code: string;
97
+ value: string;
98
+ };
56
99
  };
57
100
  interface ProviderAutocompleteProps<Option = Provider, Multiple extends boolean | undefined = false, DisableClearable extends boolean | undefined = false, FreeSolo extends boolean | undefined = false, ChipComponent extends React.ElementType = ChipTypeMap['defaultComponent']> extends Omit<Optional<AsyncAutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>, 'queryKey'>, 'loadOptions'> {
101
+ /** Customer ID of the Organization you are requesting the providers for */
58
102
  customerId: string;
103
+ /** Config passed to the AvProvidersApi.getProviders function */
59
104
  apiConfig?: ApiConfig;
60
105
  }
61
106
  declare const ProviderAutocomplete: ({ apiConfig, customerId, queryKey, ...rest }: ProviderAutocompleteProps) => react_jsx_runtime.JSX.Element;
package/dist/index.js CHANGED
@@ -122,7 +122,9 @@ var Autocomplete = (_a) => {
122
122
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
123
123
  import_Autocomplete.default,
124
124
  __spreadValues(__spreadValues({
125
- renderInput: (params) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_mui_textfield.TextField, __spreadValues(__spreadValues(__spreadValues({}, params), resolvedProps(params)), FieldProps)),
125
+ renderInput: (params) => {
126
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_mui_textfield.TextField, __spreadValues(__spreadValues(__spreadValues({}, params), FieldProps), resolvedProps(params)));
127
+ },
126
128
  popupIcon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_mui_form_utils.SelectExpandIcon, { className: "MuiSelect-avExpandIcon" }),
127
129
  slotProps: { popupIndicator: { component: PopupIndicatorWrapper } }
128
130
  }, props), defaultProps)
@@ -130,7 +132,25 @@ var Autocomplete = (_a) => {
130
132
  };
131
133
 
132
134
  // src/lib/AsyncAutocomplete.tsx
135
+ var import_react3 = require("react");
133
136
  var import_react_query = require("@tanstack/react-query");
137
+
138
+ // src/lib/util.tsx
139
+ var import_react2 = require("react");
140
+ var useDebounce = (value, delay) => {
141
+ const [debouncedValue, setDebouncedValue] = (0, import_react2.useState)("");
142
+ (0, import_react2.useEffect)(() => {
143
+ const timer = setTimeout(() => {
144
+ setDebouncedValue(value);
145
+ }, delay);
146
+ return () => {
147
+ clearTimeout(timer);
148
+ };
149
+ }, [value]);
150
+ return debouncedValue;
151
+ };
152
+
153
+ // src/lib/AsyncAutocomplete.tsx
134
154
  var import_jsx_runtime2 = require("react/jsx-runtime");
135
155
  var AsyncAutocomplete = (_a) => {
136
156
  var _b = _a, {
@@ -138,18 +158,32 @@ var AsyncAutocomplete = (_a) => {
138
158
  limit = 50,
139
159
  queryKey,
140
160
  ListboxProps,
141
- queryOptions
161
+ queryOptions,
162
+ watchParams,
163
+ debounceTimeout = 350,
164
+ FieldProps
142
165
  } = _b, rest = __objRest(_b, [
143
166
  "loadOptions",
144
167
  "limit",
145
168
  "queryKey",
146
169
  "ListboxProps",
147
- "queryOptions"
170
+ "queryOptions",
171
+ "watchParams",
172
+ "debounceTimeout",
173
+ "FieldProps"
148
174
  ]);
175
+ const [inputValue, setInputValue] = (0, import_react3.useState)("");
176
+ const handleInputChange = (event) => {
177
+ var _a2;
178
+ setInputValue(event.target.value);
179
+ if ((_a2 = FieldProps == null ? void 0 : FieldProps.InputProps) == null ? void 0 : _a2.onChange)
180
+ FieldProps.InputProps.onChange(event);
181
+ };
182
+ const debouncedInput = useDebounce(inputValue, debounceTimeout);
149
183
  const { isLoading, isFetching, data, hasNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)(__spreadValues({
150
- queryKey: [queryKey, limit],
184
+ queryKey: [queryKey, limit, debouncedInput, watchParams],
151
185
  queryFn: (_0) => __async(void 0, [_0], function* ({ pageParam = 0 }) {
152
- return loadOptions(pageParam, limit);
186
+ return loadOptions(pageParam, limit, debouncedInput);
153
187
  }),
154
188
  staleTime: 1e4,
155
189
  getNextPageParam: (lastPage) => lastPage.hasMore ? lastPage.offset + limit : false
@@ -158,6 +192,11 @@ var AsyncAutocomplete = (_a) => {
158
192
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
159
193
  Autocomplete,
160
194
  __spreadProps(__spreadValues({}, rest), {
195
+ FieldProps: __spreadProps(__spreadValues({}, FieldProps), {
196
+ InputProps: __spreadProps(__spreadValues({}, FieldProps == null ? void 0 : FieldProps.InputProps), {
197
+ onChange: handleInputChange
198
+ })
199
+ }),
161
200
  loading: isFetching,
162
201
  options,
163
202
  ListboxProps: __spreadProps(__spreadValues({}, ListboxProps), {
@@ -229,8 +268,10 @@ var ProviderAutocomplete = (_a) => {
229
268
  "customerId",
230
269
  "queryKey"
231
270
  ]);
232
- const handleLoadOptions = (offset, limit) => __async(void 0, null, function* () {
233
- const resp = yield fetchProviders(customerId, __spreadProps(__spreadValues({}, apiConfig), { params: __spreadProps(__spreadValues({}, apiConfig.params), { offset, limit }) }));
271
+ const handleLoadOptions = (offset, limit, inputValue) => __async(void 0, null, function* () {
272
+ const resp = yield fetchProviders(customerId, __spreadProps(__spreadValues({}, apiConfig), {
273
+ params: __spreadProps(__spreadValues({}, apiConfig.params), { offset, limit, q: inputValue })
274
+ }));
234
275
  return resp;
235
276
  });
236
277
  const handleGetOptionLabel = (option) => option.uiDisplayName;
@@ -239,7 +280,8 @@ var ProviderAutocomplete = (_a) => {
239
280
  __spreadProps(__spreadValues({
240
281
  getOptionLabel: handleGetOptionLabel,
241
282
  queryOptions: { enabled: !!customerId },
242
- queryKey
283
+ queryKey,
284
+ watchParams: { customerId }
243
285
  }, rest), {
244
286
  loadOptions: handleLoadOptions
245
287
  })
package/dist/index.mjs CHANGED
@@ -88,7 +88,9 @@ var Autocomplete = (_a) => {
88
88
  return /* @__PURE__ */ jsx(
89
89
  MuiAutocomplete,
90
90
  __spreadValues(__spreadValues({
91
- renderInput: (params) => /* @__PURE__ */ jsx(TextField, __spreadValues(__spreadValues(__spreadValues({}, params), resolvedProps(params)), FieldProps)),
91
+ renderInput: (params) => {
92
+ return /* @__PURE__ */ jsx(TextField, __spreadValues(__spreadValues(__spreadValues({}, params), FieldProps), resolvedProps(params)));
93
+ },
92
94
  popupIcon: /* @__PURE__ */ jsx(SelectExpandIcon, { className: "MuiSelect-avExpandIcon" }),
93
95
  slotProps: { popupIndicator: { component: PopupIndicatorWrapper } }
94
96
  }, props), defaultProps)
@@ -96,7 +98,25 @@ var Autocomplete = (_a) => {
96
98
  };
97
99
 
98
100
  // src/lib/AsyncAutocomplete.tsx
101
+ import { useState as useState2 } from "react";
99
102
  import { useInfiniteQuery } from "@tanstack/react-query";
103
+
104
+ // src/lib/util.tsx
105
+ import { useEffect, useState } from "react";
106
+ var useDebounce = (value, delay) => {
107
+ const [debouncedValue, setDebouncedValue] = useState("");
108
+ useEffect(() => {
109
+ const timer = setTimeout(() => {
110
+ setDebouncedValue(value);
111
+ }, delay);
112
+ return () => {
113
+ clearTimeout(timer);
114
+ };
115
+ }, [value]);
116
+ return debouncedValue;
117
+ };
118
+
119
+ // src/lib/AsyncAutocomplete.tsx
100
120
  import { jsx as jsx2 } from "react/jsx-runtime";
101
121
  var AsyncAutocomplete = (_a) => {
102
122
  var _b = _a, {
@@ -104,18 +124,32 @@ var AsyncAutocomplete = (_a) => {
104
124
  limit = 50,
105
125
  queryKey,
106
126
  ListboxProps,
107
- queryOptions
127
+ queryOptions,
128
+ watchParams,
129
+ debounceTimeout = 350,
130
+ FieldProps
108
131
  } = _b, rest = __objRest(_b, [
109
132
  "loadOptions",
110
133
  "limit",
111
134
  "queryKey",
112
135
  "ListboxProps",
113
- "queryOptions"
136
+ "queryOptions",
137
+ "watchParams",
138
+ "debounceTimeout",
139
+ "FieldProps"
114
140
  ]);
141
+ const [inputValue, setInputValue] = useState2("");
142
+ const handleInputChange = (event) => {
143
+ var _a2;
144
+ setInputValue(event.target.value);
145
+ if ((_a2 = FieldProps == null ? void 0 : FieldProps.InputProps) == null ? void 0 : _a2.onChange)
146
+ FieldProps.InputProps.onChange(event);
147
+ };
148
+ const debouncedInput = useDebounce(inputValue, debounceTimeout);
115
149
  const { isLoading, isFetching, data, hasNextPage, fetchNextPage } = useInfiniteQuery(__spreadValues({
116
- queryKey: [queryKey, limit],
150
+ queryKey: [queryKey, limit, debouncedInput, watchParams],
117
151
  queryFn: (_0) => __async(void 0, [_0], function* ({ pageParam = 0 }) {
118
- return loadOptions(pageParam, limit);
152
+ return loadOptions(pageParam, limit, debouncedInput);
119
153
  }),
120
154
  staleTime: 1e4,
121
155
  getNextPageParam: (lastPage) => lastPage.hasMore ? lastPage.offset + limit : false
@@ -124,6 +158,11 @@ var AsyncAutocomplete = (_a) => {
124
158
  return /* @__PURE__ */ jsx2(
125
159
  Autocomplete,
126
160
  __spreadProps(__spreadValues({}, rest), {
161
+ FieldProps: __spreadProps(__spreadValues({}, FieldProps), {
162
+ InputProps: __spreadProps(__spreadValues({}, FieldProps == null ? void 0 : FieldProps.InputProps), {
163
+ onChange: handleInputChange
164
+ })
165
+ }),
127
166
  loading: isFetching,
128
167
  options,
129
168
  ListboxProps: __spreadProps(__spreadValues({}, ListboxProps), {
@@ -195,8 +234,10 @@ var ProviderAutocomplete = (_a) => {
195
234
  "customerId",
196
235
  "queryKey"
197
236
  ]);
198
- const handleLoadOptions = (offset, limit) => __async(void 0, null, function* () {
199
- const resp = yield fetchProviders(customerId, __spreadProps(__spreadValues({}, apiConfig), { params: __spreadProps(__spreadValues({}, apiConfig.params), { offset, limit }) }));
237
+ const handleLoadOptions = (offset, limit, inputValue) => __async(void 0, null, function* () {
238
+ const resp = yield fetchProviders(customerId, __spreadProps(__spreadValues({}, apiConfig), {
239
+ params: __spreadProps(__spreadValues({}, apiConfig.params), { offset, limit, q: inputValue })
240
+ }));
200
241
  return resp;
201
242
  });
202
243
  const handleGetOptionLabel = (option) => option.uiDisplayName;
@@ -205,7 +246,8 @@ var ProviderAutocomplete = (_a) => {
205
246
  __spreadProps(__spreadValues({
206
247
  getOptionLabel: handleGetOptionLabel,
207
248
  queryOptions: { enabled: !!customerId },
208
- queryKey
249
+ queryKey,
250
+ watchParams: { customerId }
209
251
  }, rest), {
210
252
  loadOptions: handleLoadOptions
211
253
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@availity/mui-autocomplete",
3
- "version": "0.6.3",
3
+ "version": "0.7.0",
4
4
  "description": "Availity MUI Autocomplete Component - part of the @availity/element design system",
5
5
  "keywords": [
6
6
  "react",
@@ -36,9 +36,9 @@
36
36
  "@mui/types": "^7.2.14"
37
37
  },
38
38
  "devDependencies": {
39
- "@availity/api-axios": "^8.0.8",
40
- "@availity/mui-form-utils": "^0.12.1",
41
- "@availity/mui-textfield": "^0.6.1",
39
+ "@availity/api-axios": "^9.0.0",
40
+ "@availity/mui-form-utils": "^0.12.2",
41
+ "@availity/mui-textfield": "^0.6.2",
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.1",
52
- "@availity/mui-textfield": "^0.6.1",
51
+ "@availity/mui-form-utils": "^0.12.2",
52
+ "@availity/mui-textfield": "^0.6.2",
53
53
  "@mui/material": "^5.11.9",
54
54
  "@tanstack/react-query": "^4.36.1",
55
55
  "react": ">=16.3.0"
@@ -8,8 +8,6 @@ import { AsyncAutocomplete } from './AsyncAutocomplete';
8
8
 
9
9
  const api = new AvApi({ name: 'example' } as ApiConfig);
10
10
 
11
- const client = new QueryClient();
12
-
13
11
  type Option = {
14
12
  label: string;
15
13
  value: number;
@@ -67,6 +65,8 @@ describe('AsyncAutocomplete', () => {
67
65
  });
68
66
 
69
67
  test('should render successfully', () => {
68
+ const client = new QueryClient();
69
+
70
70
  const { getByLabelText } = render(
71
71
  <QueryClientProvider client={client}>
72
72
  <AsyncAutocomplete queryKey="test" FieldProps={{ label: 'Test' }} loadOptions={loadOptions} />
@@ -77,6 +77,8 @@ describe('AsyncAutocomplete', () => {
77
77
  });
78
78
 
79
79
  test('options should be available', async () => {
80
+ const client = new QueryClient();
81
+
80
82
  render(
81
83
  <QueryClientProvider client={client}>
82
84
  <AsyncAutocomplete queryKey="test1" loadOptions={loadOptions} FieldProps={{ label: 'Test' }} />
@@ -99,6 +101,8 @@ describe('AsyncAutocomplete', () => {
99
101
  });
100
102
 
101
103
  test('should call loadOptions when scroll to the bottom', async () => {
104
+ const client = new QueryClient();
105
+
102
106
  render(
103
107
  <QueryClientProvider client={client}>
104
108
  <AsyncAutocomplete queryKey="test2" loadOptions={loadOptions} limit={10} FieldProps={{ label: 'Test' }} />
@@ -123,4 +127,82 @@ describe('AsyncAutocomplete', () => {
123
127
  expect(() => screen.getByText('Option 20')).toThrowError();
124
128
  });
125
129
  });
130
+
131
+ test('should search with input value', async () => {
132
+ const mockLoadOptions = jest.fn(async () => ({ options: [{ label: 'Option 1' }], hasMore: false, offset: 50 }));
133
+ const client = new QueryClient();
134
+
135
+ render(
136
+ <QueryClientProvider client={client}>
137
+ <AsyncAutocomplete queryKey="test1" loadOptions={mockLoadOptions} FieldProps={{ label: 'Test' }} />
138
+ </QueryClientProvider>
139
+ );
140
+
141
+ const input = screen.getByRole('combobox');
142
+ fireEvent.click(input);
143
+ fireEvent.keyDown(input, { key: 'ArrowDown' });
144
+
145
+ await waitFor(() => {
146
+ expect(screen.getByText('Option 1')).toBeDefined();
147
+ expect(mockLoadOptions).toHaveBeenNthCalledWith(1, 0, 50, '');
148
+ });
149
+
150
+ fireEvent.change(input, { target: { value: 'test' } });
151
+
152
+ await waitFor(() => expect(mockLoadOptions).toHaveBeenNthCalledWith(2, 0, 50, 'test'));
153
+ });
154
+
155
+ test('should make call when watchParams changes', async () => {
156
+ const mockLoadOptions = jest.fn(async () => ({ options: [{ label: 'Option 1' }], hasMore: false, offset: 50 }));
157
+ const client = new QueryClient();
158
+
159
+ const watchParams = { foo: 'bar' };
160
+
161
+ const { rerender } = render(
162
+ <QueryClientProvider client={client}>
163
+ <AsyncAutocomplete
164
+ queryKey="test1"
165
+ loadOptions={mockLoadOptions}
166
+ FieldProps={{ label: 'Test' }}
167
+ watchParams={watchParams}
168
+ />
169
+ </QueryClientProvider>
170
+ );
171
+
172
+ const input = screen.getByRole('combobox');
173
+ fireEvent.click(input);
174
+ fireEvent.keyDown(input, { key: 'ArrowDown' });
175
+
176
+ await waitFor(() => {
177
+ expect(screen.getByText('Option 1')).toBeDefined();
178
+ // Check that api was called
179
+ expect(mockLoadOptions).toHaveBeenNthCalledWith(1, 0, 50, '');
180
+ // Make sure data is in the query client with given watchParams
181
+ expect(client.getQueryData(['test1', 50, '', watchParams])).toBeDefined();
182
+ });
183
+
184
+ watchParams.foo = 'test';
185
+
186
+ rerender(
187
+ <QueryClientProvider client={client}>
188
+ <AsyncAutocomplete
189
+ queryKey="test1"
190
+ loadOptions={mockLoadOptions}
191
+ FieldProps={{ label: 'Test' }}
192
+ watchParams={watchParams}
193
+ />
194
+ </QueryClientProvider>
195
+ );
196
+
197
+ fireEvent.click(input);
198
+ fireEvent.keyDown(input, { key: 'ArrowDown' });
199
+
200
+ await waitFor(() => {
201
+ expect(screen.getByText('Option 1')).toBeDefined();
202
+ // Check that options were fetched
203
+ expect(mockLoadOptions).toHaveBeenNthCalledWith(2, 0, 50, '');
204
+ // Make sure call was made with new watchParams
205
+ expect(client.getQueryData(['test1', 50, '', watchParams])).toBeDefined();
206
+ });
207
+ });
126
208
  });
@@ -1,7 +1,9 @@
1
+ import { useState } from 'react';
1
2
  import type { ChipTypeMap } from '@mui/material/Chip';
2
3
  import { useInfiniteQuery, UseInfiniteQueryOptions } from '@tanstack/react-query';
3
4
 
4
5
  import { Autocomplete, AutocompleteProps } from './Autocomplete';
6
+ import { useDebounce } from './util';
5
7
 
6
8
  export interface AsyncAutocompleteProps<
7
9
  Option,
@@ -14,7 +16,11 @@ export interface AsyncAutocompleteProps<
14
16
  'options' | 'disableListWrap' | 'loading'
15
17
  > {
16
18
  /** Function that is called to fetch the options for the list. Returns a promise with options, hasMore, and offset */
17
- loadOptions: (offset: number, limit: number) => Promise<{ options: Option[]; hasMore: boolean; offset: number }>;
19
+ loadOptions: (
20
+ offset: number,
21
+ limit: number,
22
+ inputValue?: string
23
+ ) => Promise<{ options: Option[]; hasMore: boolean; offset: number }>;
18
24
  /** The key used by @tanstack/react-query to cache the response */
19
25
  queryKey: string;
20
26
  /** The number of options to request from the api
@@ -22,6 +28,10 @@ export interface AsyncAutocompleteProps<
22
28
  limit?: number;
23
29
  /** Config options for the useInfiniteQuery hook */
24
30
  queryOptions?: UseInfiniteQueryOptions<{ options: Option[]; hasMore: boolean; offset: number }>;
31
+ /** Object of parameters used for the cacheKey. Options are re-reftched when a value in the object changes */
32
+ watchParams?: Record<string, unknown>;
33
+ /** Time to wait before searching with the input value typed into the component */
34
+ debounceTimeout?: number;
25
35
  }
26
36
 
27
37
  export const AsyncAutocomplete = <
@@ -36,11 +46,24 @@ export const AsyncAutocomplete = <
36
46
  queryKey,
37
47
  ListboxProps,
38
48
  queryOptions,
49
+ watchParams,
50
+ debounceTimeout = 350,
51
+ FieldProps,
39
52
  ...rest
40
53
  }: AsyncAutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>) => {
54
+ const [inputValue, setInputValue] = useState('');
55
+
56
+ const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
57
+ setInputValue(event.target.value);
58
+ // Call passed in onChange if present
59
+ if (FieldProps?.InputProps?.onChange) FieldProps.InputProps.onChange(event);
60
+ };
61
+
62
+ const debouncedInput = useDebounce(inputValue, debounceTimeout);
63
+
41
64
  const { isLoading, isFetching, data, hasNextPage, fetchNextPage } = useInfiniteQuery({
42
- queryKey: [queryKey, limit],
43
- queryFn: async ({ pageParam = 0 }) => loadOptions(pageParam, limit),
65
+ queryKey: [queryKey, limit, debouncedInput, watchParams],
66
+ queryFn: async ({ pageParam = 0 }) => loadOptions(pageParam, limit, debouncedInput),
44
67
  staleTime: 10000,
45
68
  getNextPageParam: (lastPage) => (lastPage.hasMore ? lastPage.offset + limit : false),
46
69
  ...queryOptions,
@@ -51,6 +74,13 @@ export const AsyncAutocomplete = <
51
74
  return (
52
75
  <Autocomplete
53
76
  {...rest}
77
+ FieldProps={{
78
+ ...FieldProps,
79
+ InputProps: {
80
+ ...FieldProps?.InputProps,
81
+ onChange: handleInputChange,
82
+ },
83
+ }}
54
84
  loading={isFetching}
55
85
  options={options}
56
86
  ListboxProps={{
@@ -7,7 +7,7 @@ const meta: Meta<typeof Autocomplete> = {
7
7
  component: Autocomplete,
8
8
  tags: ['autodocs'],
9
9
  args: {
10
- options: ['1', '2', '3', '4', '5'],
10
+ options: ['Option 1', 'Option 2', 'Option 3', 'Option 4', 'Option 5'],
11
11
  id: 'example',
12
12
  },
13
13
  argTypes: {
@@ -93,9 +93,9 @@ export const Autocomplete = <
93
93
 
94
94
  return (
95
95
  <MuiAutocomplete
96
- renderInput={(params: AutocompleteRenderInputParams) => (
97
- <TextField {...params} {...resolvedProps(params)} {...FieldProps} />
98
- )}
96
+ renderInput={(params: AutocompleteRenderInputParams) => {
97
+ return <TextField {...params} {...FieldProps} {...resolvedProps(params)} />;
98
+ }}
99
99
  popupIcon={<SelectExpandIcon className="MuiSelect-avExpandIcon" />}
100
100
  slotProps={{ popupIndicator: { component: PopupIndicatorWrapper } }}
101
101
  {...props}
@@ -8,7 +8,29 @@ export type Provider = {
8
8
  id: string;
9
9
  businessName: string;
10
10
  uiDisplayName: string;
11
- aytypical: boolean;
11
+ lastName?: string;
12
+ firstName?: string;
13
+ payerAssignedIdentifiers?: { payerId: string; identifier: string }[];
14
+ atypical: boolean;
15
+ npi: string;
16
+ customerIds: string[];
17
+ roles: { code: string; value: string }[];
18
+ primaryPhone: { internationalCellularCode: string; areaCode: string; phoneNumber: string };
19
+ primaryFax: { internationalCellularCode: string; areaCode: string; phoneNumber: string };
20
+ taxId?: string;
21
+ ssn?: string;
22
+ primaryAddress: {
23
+ line1: string;
24
+ line2: string;
25
+ city: string;
26
+ state: string;
27
+ stateCode: string;
28
+ zip: { code: string; addon: string };
29
+ };
30
+ primarySpecialty?: {
31
+ code: string;
32
+ value: string;
33
+ };
12
34
  };
13
35
 
14
36
  const fetchProviders = async (
@@ -34,7 +56,9 @@ export interface ProviderAutocompleteProps<
34
56
  Optional<AsyncAutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>, 'queryKey'>,
35
57
  'loadOptions'
36
58
  > {
59
+ /** Customer ID of the Organization you are requesting the providers for */
37
60
  customerId: string;
61
+ /** Config passed to the AvProvidersApi.getProviders function */
38
62
  apiConfig?: ApiConfig;
39
63
  }
40
64
 
@@ -44,8 +68,11 @@ export const ProviderAutocomplete = ({
44
68
  queryKey = 'prov-autocomplete',
45
69
  ...rest
46
70
  }: ProviderAutocompleteProps) => {
47
- const handleLoadOptions = async (offset: number, limit: number) => {
48
- const resp = await fetchProviders(customerId, { ...apiConfig, params: { ...apiConfig.params, offset, limit } });
71
+ const handleLoadOptions = async (offset: number, limit: number, inputValue: string) => {
72
+ const resp = await fetchProviders(customerId, {
73
+ ...apiConfig,
74
+ params: { ...apiConfig.params, offset, limit, q: inputValue },
75
+ });
49
76
 
50
77
  return resp;
51
78
  };
@@ -57,6 +84,7 @@ export const ProviderAutocomplete = ({
57
84
  getOptionLabel={handleGetOptionLabel}
58
85
  queryOptions={{ enabled: !!customerId }}
59
86
  queryKey={queryKey}
87
+ watchParams={{ customerId }}
60
88
  {...rest}
61
89
  loadOptions={handleLoadOptions}
62
90
  />
@@ -0,0 +1,20 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ // Mark a field as optional in an already defined type
4
+ export type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
5
+
6
+ export const useDebounce = (value: string, delay: number) => {
7
+ const [debouncedValue, setDebouncedValue] = useState('');
8
+
9
+ useEffect(() => {
10
+ const timer = setTimeout(() => {
11
+ setDebouncedValue(value);
12
+ }, delay);
13
+
14
+ return () => {
15
+ clearTimeout(timer);
16
+ };
17
+ }, [value]);
18
+
19
+ return debouncedValue;
20
+ };
package/src/lib/util.ts DELETED
@@ -1,2 +0,0 @@
1
- // Mark a field as optional in an already defined type
2
- export type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;