@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@availity/mui-autocomplete",
3
- "version": "2.0.2",
3
+ "version": "2.1.0",
4
4
  "description": "Availity MUI Autocomplete Component - part of the @availity/element design system",
5
5
  "keywords": [
6
6
  "react",
@@ -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={options}
118
+ options={finalOptions}
103
119
  ListboxProps={{
104
120
  ...ListboxProps,
105
121
  onScroll: async (event: React.SyntheticEvent) => {