@availity/mui-autocomplete 2.0.2 → 2.1.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/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +13 -3
- package/dist/index.mjs +13 -3
- package/package.json +1 -1
- package/src/lib/AsyncAutocomplete.test.tsx +79 -0
- package/src/lib/AsyncAutocomplete.tsx +17 -1
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
|
+
## [2.1.0](https://github.com/Availity/element/compare/@availity/mui-autocomplete@2.0.2...@availity/mui-autocomplete@2.1.0) (2025-12-23)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* prependOptions functionality for AsyncAutocompletes ([532b8b6](https://github.com/Availity/element/commit/532b8b6dc7a165ed37feb885fcea9cdf8007ae41))
|
|
11
|
+
|
|
5
12
|
## [2.0.2](https://github.com/Availity/element/compare/@availity/mui-autocomplete@2.0.1...@availity/mui-autocomplete@2.0.2) (2025-12-09)
|
|
6
13
|
|
|
7
14
|
### Dependency Updates
|
package/dist/index.d.mts
CHANGED
|
@@ -35,8 +35,10 @@ interface AsyncAutocompleteProps<Option, Multiple extends boolean | undefined, D
|
|
|
35
35
|
watchParams?: Record<string, unknown>;
|
|
36
36
|
/** Time to wait before searching with the input value typed into the component */
|
|
37
37
|
debounceTimeout?: number;
|
|
38
|
+
/** Options to prepend to the list (e.g., frequently used items). These will be filtered from loadOptions results to avoid duplicates. */
|
|
39
|
+
prependOptions?: Option[];
|
|
38
40
|
}
|
|
39
|
-
declare const AsyncAutocomplete: <Option, Multiple extends boolean | undefined = false, DisableClearable extends boolean | undefined = false, FreeSolo extends boolean | undefined = false, ChipComponent extends React.ElementType = ChipTypeMap["defaultComponent"]>({ loadOptions, limit, queryKey, ListboxProps, queryOptions, watchParams, debounceTimeout, FieldProps, onInputChange, ...rest }: AsyncAutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>) => react_jsx_runtime.JSX.Element;
|
|
41
|
+
declare const AsyncAutocomplete: <Option, Multiple extends boolean | undefined = false, DisableClearable extends boolean | undefined = false, FreeSolo extends boolean | undefined = false, ChipComponent extends React.ElementType = ChipTypeMap["defaultComponent"]>({ loadOptions, limit, queryKey, ListboxProps, queryOptions, watchParams, debounceTimeout, FieldProps, onInputChange, prependOptions, ...rest }: AsyncAutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>) => react_jsx_runtime.JSX.Element;
|
|
40
42
|
|
|
41
43
|
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
|
|
42
44
|
|
package/dist/index.d.ts
CHANGED
|
@@ -35,8 +35,10 @@ interface AsyncAutocompleteProps<Option, Multiple extends boolean | undefined, D
|
|
|
35
35
|
watchParams?: Record<string, unknown>;
|
|
36
36
|
/** Time to wait before searching with the input value typed into the component */
|
|
37
37
|
debounceTimeout?: number;
|
|
38
|
+
/** Options to prepend to the list (e.g., frequently used items). These will be filtered from loadOptions results to avoid duplicates. */
|
|
39
|
+
prependOptions?: Option[];
|
|
38
40
|
}
|
|
39
|
-
declare const AsyncAutocomplete: <Option, Multiple extends boolean | undefined = false, DisableClearable extends boolean | undefined = false, FreeSolo extends boolean | undefined = false, ChipComponent extends React.ElementType = ChipTypeMap["defaultComponent"]>({ loadOptions, limit, queryKey, ListboxProps, queryOptions, watchParams, debounceTimeout, FieldProps, onInputChange, ...rest }: AsyncAutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>) => react_jsx_runtime.JSX.Element;
|
|
41
|
+
declare const AsyncAutocomplete: <Option, Multiple extends boolean | undefined = false, DisableClearable extends boolean | undefined = false, FreeSolo extends boolean | undefined = false, ChipComponent extends React.ElementType = ChipTypeMap["defaultComponent"]>({ loadOptions, limit, queryKey, ListboxProps, queryOptions, watchParams, debounceTimeout, FieldProps, onInputChange, prependOptions, ...rest }: AsyncAutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>) => react_jsx_runtime.JSX.Element;
|
|
40
42
|
|
|
41
43
|
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
|
|
42
44
|
|
package/dist/index.js
CHANGED
|
@@ -174,7 +174,8 @@ var AsyncAutocomplete = (_a) => {
|
|
|
174
174
|
watchParams,
|
|
175
175
|
debounceTimeout = 350,
|
|
176
176
|
FieldProps,
|
|
177
|
-
onInputChange
|
|
177
|
+
onInputChange,
|
|
178
|
+
prependOptions = []
|
|
178
179
|
} = _b, rest = __objRest(_b, [
|
|
179
180
|
"loadOptions",
|
|
180
181
|
"limit",
|
|
@@ -184,7 +185,8 @@ var AsyncAutocomplete = (_a) => {
|
|
|
184
185
|
"watchParams",
|
|
185
186
|
"debounceTimeout",
|
|
186
187
|
"FieldProps",
|
|
187
|
-
"onInputChange"
|
|
188
|
+
"onInputChange",
|
|
189
|
+
"prependOptions"
|
|
188
190
|
]);
|
|
189
191
|
const [inputValue, setInputValue] = (0, import_react3.useState)("");
|
|
190
192
|
const handleInputPropsOnChange = (event) => {
|
|
@@ -202,6 +204,14 @@ var AsyncAutocomplete = (_a) => {
|
|
|
202
204
|
getNextPageParam: (lastPage) => lastPage.hasMore ? lastPage.offset + limit : void 0
|
|
203
205
|
}, queryOptions));
|
|
204
206
|
const options = (data == null ? void 0 : data.pages) ? data.pages.map((page) => page.options).flat() : [];
|
|
207
|
+
const finalOptions = prependOptions.length > 0 ? [
|
|
208
|
+
...prependOptions,
|
|
209
|
+
...options.filter(
|
|
210
|
+
(option) => !prependOptions.some(
|
|
211
|
+
(prepended) => rest.isOptionEqualToValue ? rest.isOptionEqualToValue(option, prepended) : option === prepended
|
|
212
|
+
)
|
|
213
|
+
)
|
|
214
|
+
] : options;
|
|
205
215
|
const handleOnInputChange = (event, value, reason) => {
|
|
206
216
|
if (reason === "clear") {
|
|
207
217
|
setInputValue(event.target.value);
|
|
@@ -220,7 +230,7 @@ var AsyncAutocomplete = (_a) => {
|
|
|
220
230
|
})
|
|
221
231
|
}),
|
|
222
232
|
loading: isFetching,
|
|
223
|
-
options,
|
|
233
|
+
options: finalOptions,
|
|
224
234
|
ListboxProps: __spreadProps(__spreadValues({}, ListboxProps), {
|
|
225
235
|
onScroll: (event) => __async(null, null, function* () {
|
|
226
236
|
const listboxNode = event.currentTarget;
|
package/dist/index.mjs
CHANGED
|
@@ -132,7 +132,8 @@ var AsyncAutocomplete = (_a) => {
|
|
|
132
132
|
watchParams,
|
|
133
133
|
debounceTimeout = 350,
|
|
134
134
|
FieldProps,
|
|
135
|
-
onInputChange
|
|
135
|
+
onInputChange,
|
|
136
|
+
prependOptions = []
|
|
136
137
|
} = _b, rest = __objRest(_b, [
|
|
137
138
|
"loadOptions",
|
|
138
139
|
"limit",
|
|
@@ -142,7 +143,8 @@ var AsyncAutocomplete = (_a) => {
|
|
|
142
143
|
"watchParams",
|
|
143
144
|
"debounceTimeout",
|
|
144
145
|
"FieldProps",
|
|
145
|
-
"onInputChange"
|
|
146
|
+
"onInputChange",
|
|
147
|
+
"prependOptions"
|
|
146
148
|
]);
|
|
147
149
|
const [inputValue, setInputValue] = useState2("");
|
|
148
150
|
const handleInputPropsOnChange = (event) => {
|
|
@@ -160,6 +162,14 @@ var AsyncAutocomplete = (_a) => {
|
|
|
160
162
|
getNextPageParam: (lastPage) => lastPage.hasMore ? lastPage.offset + limit : void 0
|
|
161
163
|
}, queryOptions));
|
|
162
164
|
const options = (data == null ? void 0 : data.pages) ? data.pages.map((page) => page.options).flat() : [];
|
|
165
|
+
const finalOptions = prependOptions.length > 0 ? [
|
|
166
|
+
...prependOptions,
|
|
167
|
+
...options.filter(
|
|
168
|
+
(option) => !prependOptions.some(
|
|
169
|
+
(prepended) => rest.isOptionEqualToValue ? rest.isOptionEqualToValue(option, prepended) : option === prepended
|
|
170
|
+
)
|
|
171
|
+
)
|
|
172
|
+
] : options;
|
|
163
173
|
const handleOnInputChange = (event, value, reason) => {
|
|
164
174
|
if (reason === "clear") {
|
|
165
175
|
setInputValue(event.target.value);
|
|
@@ -178,7 +188,7 @@ var AsyncAutocomplete = (_a) => {
|
|
|
178
188
|
})
|
|
179
189
|
}),
|
|
180
190
|
loading: isFetching,
|
|
181
|
-
options,
|
|
191
|
+
options: finalOptions,
|
|
182
192
|
ListboxProps: __spreadProps(__spreadValues({}, ListboxProps), {
|
|
183
193
|
onScroll: (event) => __async(null, null, function* () {
|
|
184
194
|
const listboxNode = event.currentTarget;
|
package/package.json
CHANGED
|
@@ -103,6 +103,85 @@ describe('AsyncAutocomplete', () => {
|
|
|
103
103
|
});
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
+
test('prependedOptions should be available', async () => {
|
|
107
|
+
const client = new QueryClient();
|
|
108
|
+
|
|
109
|
+
render(
|
|
110
|
+
<QueryClientProvider client={client}>
|
|
111
|
+
<AsyncAutocomplete
|
|
112
|
+
queryKey="prepended-options"
|
|
113
|
+
prependOptions={[{ label: 'Option 0', value: 0 }]}
|
|
114
|
+
loadOptions={loadOptions}
|
|
115
|
+
FieldProps={{ label: 'Test' }}
|
|
116
|
+
/>
|
|
117
|
+
</QueryClientProvider>
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const input = screen.getByRole('combobox');
|
|
121
|
+
fireEvent.click(input);
|
|
122
|
+
fireEvent.keyDown(input, { key: 'ArrowDown' });
|
|
123
|
+
|
|
124
|
+
waitFor(() => {
|
|
125
|
+
expect(screen.getByText('Option 0')).toBeDefined();
|
|
126
|
+
expect(screen.getByText('Option 1')).toBeDefined();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
fireEvent.click(await screen.findByText('Option 0'));
|
|
130
|
+
|
|
131
|
+
waitFor(() => {
|
|
132
|
+
expect(screen.getByText('Option 0')).toBeDefined();
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('prependedOptions should not be duplicative', async () => {
|
|
137
|
+
const client = new QueryClient();
|
|
138
|
+
|
|
139
|
+
render(
|
|
140
|
+
<QueryClientProvider client={client}>
|
|
141
|
+
<AsyncAutocomplete
|
|
142
|
+
queryKey="prepended-options-unique"
|
|
143
|
+
prependOptions={[{ label: 'Option 1', value: 1 }]}
|
|
144
|
+
loadOptions={loadOptions}
|
|
145
|
+
FieldProps={{ label: 'Test' }}
|
|
146
|
+
/>
|
|
147
|
+
</QueryClientProvider>
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const input = screen.getByRole('combobox');
|
|
151
|
+
fireEvent.click(input);
|
|
152
|
+
fireEvent.keyDown(input, { key: 'ArrowDown' });
|
|
153
|
+
|
|
154
|
+
waitFor(() => {
|
|
155
|
+
expect(screen.getAllByText('Option 1').length).toBe(1);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('should filter duplicates using custom isOptionEqualToValue', async () => {
|
|
160
|
+
const client = new QueryClient();
|
|
161
|
+
|
|
162
|
+
render(
|
|
163
|
+
<QueryClientProvider client={client}>
|
|
164
|
+
<AsyncAutocomplete
|
|
165
|
+
queryKey="test-duplicates"
|
|
166
|
+
prependOptions={[{ label: 'Option 1', value: 1 }]}
|
|
167
|
+
isOptionEqualToValue={(option, value) => option.value === value.value}
|
|
168
|
+
loadOptions={loadOptions}
|
|
169
|
+
FieldProps={{ label: 'Test' }}
|
|
170
|
+
/>
|
|
171
|
+
</QueryClientProvider>
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
const input = screen.getByRole('combobox');
|
|
175
|
+
fireEvent.click(input);
|
|
176
|
+
fireEvent.keyDown(input, { key: 'ArrowDown' });
|
|
177
|
+
|
|
178
|
+
await waitFor(() => {
|
|
179
|
+
expect(screen.getByText('Option 1')).toBeDefined();
|
|
180
|
+
expect(screen.getByText('Option 2')).toBeDefined();
|
|
181
|
+
expect(screen.getAllByText('Option 1')).toHaveLength(1);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
106
185
|
test('should call loadOptions when scroll to the bottom', async () => {
|
|
107
186
|
const client = new QueryClient();
|
|
108
187
|
|
|
@@ -33,6 +33,8 @@ export interface AsyncAutocompleteProps<
|
|
|
33
33
|
watchParams?: Record<string, unknown>;
|
|
34
34
|
/** Time to wait before searching with the input value typed into the component */
|
|
35
35
|
debounceTimeout?: number;
|
|
36
|
+
/** Options to prepend to the list (e.g., frequently used items). These will be filtered from loadOptions results to avoid duplicates. */
|
|
37
|
+
prependOptions?: Option[];
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
export const AsyncAutocomplete = <
|
|
@@ -51,6 +53,7 @@ export const AsyncAutocomplete = <
|
|
|
51
53
|
debounceTimeout = 350,
|
|
52
54
|
FieldProps,
|
|
53
55
|
onInputChange,
|
|
56
|
+
prependOptions = [],
|
|
54
57
|
...rest
|
|
55
58
|
}: AsyncAutocompleteProps<Option, Multiple, DisableClearable, FreeSolo, ChipComponent>) => {
|
|
56
59
|
const [inputValue, setInputValue] = useState('');
|
|
@@ -73,6 +76,19 @@ export const AsyncAutocomplete = <
|
|
|
73
76
|
|
|
74
77
|
const options = data?.pages ? data.pages.map((page) => page.options).flat() : [];
|
|
75
78
|
|
|
79
|
+
const finalOptions =
|
|
80
|
+
prependOptions.length > 0
|
|
81
|
+
? [
|
|
82
|
+
...prependOptions,
|
|
83
|
+
...options.filter(
|
|
84
|
+
(option) =>
|
|
85
|
+
!prependOptions.some((prepended) =>
|
|
86
|
+
rest.isOptionEqualToValue ? rest.isOptionEqualToValue(option, prepended) : option === prepended
|
|
87
|
+
)
|
|
88
|
+
),
|
|
89
|
+
]
|
|
90
|
+
: options;
|
|
91
|
+
|
|
76
92
|
const handleOnInputChange = (
|
|
77
93
|
event: React.ChangeEvent<HTMLInputElement>,
|
|
78
94
|
value: string,
|
|
@@ -99,7 +115,7 @@ export const AsyncAutocomplete = <
|
|
|
99
115
|
},
|
|
100
116
|
}}
|
|
101
117
|
loading={isFetching}
|
|
102
|
-
options={
|
|
118
|
+
options={finalOptions}
|
|
103
119
|
ListboxProps={{
|
|
104
120
|
...ListboxProps,
|
|
105
121
|
onScroll: async (event: React.SyntheticEvent) => {
|