@availity/mui-autocomplete 0.6.4 → 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 +12 -0
- package/dist/index.d.mts +48 -3
- package/dist/index.d.ts +48 -3
- package/dist/index.js +50 -8
- package/dist/index.mjs +50 -8
- package/package.json +1 -1
- package/src/lib/AsyncAutocomplete.test.tsx +84 -2
- package/src/lib/AsyncAutocomplete.tsx +33 -3
- package/src/lib/Autocomplete.stories.tsx +1 -1
- package/src/lib/Autocomplete.tsx +3 -3
- package/src/lib/ProviderAutocomplete.tsx +31 -3
- package/src/lib/util.tsx +20 -0
- package/src/lib/util.ts +0 -2
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.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
|
+
|
|
5
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)
|
|
6
18
|
|
|
7
19
|
### 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
|
-
|
|
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
|
-
|
|
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) =>
|
|
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), {
|
|
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) =>
|
|
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), {
|
|
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
|
@@ -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: (
|
|
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={{
|
package/src/lib/Autocomplete.tsx
CHANGED
|
@@ -93,9 +93,9 @@ export const Autocomplete = <
|
|
|
93
93
|
|
|
94
94
|
return (
|
|
95
95
|
<MuiAutocomplete
|
|
96
|
-
renderInput={(params: AutocompleteRenderInputParams) =>
|
|
97
|
-
<TextField {...params} {...resolvedProps(params)}
|
|
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
|
-
|
|
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, {
|
|
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
|
/>
|
package/src/lib/util.tsx
ADDED
|
@@ -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