Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Autocomplete wrapper for Menu and ListBox #7181

Open
wants to merge 48 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
c33ac96
scaffolding, copied Combobox and renamed to Autocomplete
LFDanLu Oct 9, 2024
a185473
get listbox rendering by default without open state
LFDanLu Oct 9, 2024
e6fa7c1
update intl file and more clean up to get rid of combobox stuff
LFDanLu Oct 10, 2024
61aab83
fix lint
LFDanLu Oct 10, 2024
66fecc8
rough working version of Menu instead of Listbox in autocomplete
LFDanLu Oct 11, 2024
779309b
fix submenu
LFDanLu Oct 11, 2024
78de7f8
Update autocomplete to have the wrapped menu filter itself
LFDanLu Oct 19, 2024
e0f098a
fix keyboard interactions, and clean up
LFDanLu Oct 22, 2024
5914d11
fix menu, add more stories, fix strict
LFDanLu Oct 22, 2024
12480fd
add announcements to menu and various clean up
LFDanLu Oct 22, 2024
ad8942a
update yarn.lock
LFDanLu Oct 22, 2024
70c2d0f
Merge branch 'main' of github.com:adobe/react-spectrum into autocomplete
LFDanLu Oct 22, 2024
9812c3c
get rid of dom node in Autocomplete and fix readOnly bugs
LFDanLu Oct 23, 2024
8478937
fix build failure
LFDanLu Oct 23, 2024
3fbd35f
test against popover experience
LFDanLu Oct 23, 2024
e15d999
fix popover story
LFDanLu Oct 24, 2024
368a677
properly clear aria-activedecendant
LFDanLu Oct 24, 2024
82b3120
cleanup
LFDanLu Oct 24, 2024
423b73c
fix build
LFDanLu Oct 24, 2024
6c498f1
Merge branch 'main' into autocomplete
LFDanLu Oct 28, 2024
fe5bfbe
properly focus trap the autocomplete popover
LFDanLu Oct 29, 2024
477ca7f
update interaction pattern as per discussion
LFDanLu Nov 1, 2024
9a58613
update yarn.lock
LFDanLu Nov 1, 2024
574bd67
dont autofocus if user hasnt typed in the field yet
LFDanLu Nov 5, 2024
ae7a00f
add delay for now to make NVDA announcement better
LFDanLu Nov 5, 2024
7b11c5d
fix lint and scrap custom announcements
LFDanLu Nov 5, 2024
04e8777
intial tests
LFDanLu Nov 6, 2024
79c9064
more tests and fixes to BaseCollection and keyboard interactions from…
LFDanLu Nov 6, 2024
b20b626
fix lint and add RAC test
LFDanLu Nov 7, 2024
28afe21
Merge branch 'main' of github.com:adobe/react-spectrum into autocomplete
LFDanLu Nov 7, 2024
2d0a15f
use MenuSection
LFDanLu Nov 7, 2024
c87a507
(WIP) Refactor autocomplete logic to use custom events to update virt…
LFDanLu Nov 20, 2024
682357f
Merge branch 'main' of github.com:adobe/react-spectrum into autocomplete
LFDanLu Nov 20, 2024
3871e7b
fix lint and test with wrapping Listbox
LFDanLu Nov 21, 2024
e3c3c35
refactor so that we defer to the child input as much as possible for …
LFDanLu Nov 23, 2024
04d9a8b
cleanup
LFDanLu Nov 23, 2024
0757832
fix listbox test
LFDanLu Nov 23, 2024
fd709a2
Merge branch 'main' of github.com:adobe/react-spectrum into autocomplete
LFDanLu Nov 23, 2024
9eede0c
fix build and react 19
LFDanLu Nov 23, 2024
6bd5270
get rid of leftover react 19 testing changes
LFDanLu Nov 25, 2024
150bd7e
clean up some todos
LFDanLu Nov 25, 2024
c13544b
fix listbox listeners not registering
LFDanLu Nov 25, 2024
0d68f68
updating tests and making enter trigger listbox link
LFDanLu Nov 26, 2024
d8c5259
initial review comments
LFDanLu Nov 26, 2024
00f67ad
update tests and remove menu id coercing in favor of user defined ids
LFDanLu Nov 27, 2024
1a26d79
fix lock
LFDanLu Nov 27, 2024
435c6dc
Merge branch 'main' of github.com:adobe/react-spectrum into autocomplete
LFDanLu Nov 27, 2024
df03339
update forward ref for react fast refresh
LFDanLu Nov 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/ar-AE.json
LFDanLu marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "مقترحات"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/bg-BG.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Предложения"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/cs-CZ.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Návrhy"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/da-DK.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Forslag"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/de-DE.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Empfehlungen"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/el-GR.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Προτάσεις"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/en-US.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Suggestions"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/es-ES.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Sugerencias"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/et-EE.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Soovitused"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/fi-FI.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Ehdotukset"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/fr-FR.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Suggestions"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/he-IL.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "הצעות"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/hr-HR.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Prijedlozi"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/hu-HU.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Javaslatok"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/it-IT.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Suggerimenti"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/ja-JP.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "候補"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/ko-KR.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "제안"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/lt-LT.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Pasiūlymai"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/lv-LV.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Ieteikumi"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/nb-NO.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Forslag"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/nl-NL.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Suggesties"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/pl-PL.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Sugestie"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/pt-BR.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Sugestões"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/pt-PT.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Sugestões"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/ro-RO.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Sugestii"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/ru-RU.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Предложения"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/sk-SK.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Návrhy"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/sl-SI.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Predlogi"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/sr-SP.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Predlozi"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/sv-SE.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Förslag"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/tr-TR.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Öneriler"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/uk-UA.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "Пропозиції"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/zh-CN.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "建议"
}
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/intl/zh-TW.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"collectionLabel": "建議"
}
2 changes: 2 additions & 0 deletions packages/@react-aria/autocomplete/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
},
"dependencies": {
"@react-aria/combobox": "^3.11.0",
"@react-aria/i18n": "^3.12.3",
"@react-aria/listbox": "^3.13.6",
"@react-aria/searchfield": "^3.7.11",
"@react-aria/utils": "^3.26.0",
"@react-stately/autocomplete": "3.0.0-alpha.1",
"@react-stately/combobox": "^3.10.1",
"@react-types/autocomplete": "3.0.0-alpha.27",
"@react-types/button": "^3.10.1",
Expand Down
3 changes: 3 additions & 0 deletions packages/@react-aria/autocomplete/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@
* governing permissions and limitations under the License.
*/
export {useSearchAutocomplete} from './useSearchAutocomplete';
export {useAutocomplete} from './useAutocomplete';

export type {AriaSearchAutocompleteOptions, SearchAutocompleteAria} from './useSearchAutocomplete';
export type {AriaSearchAutocompleteProps} from '@react-types/autocomplete';
export type {AriaAutocompleteProps, AriaAutocompleteOptions, AutocompleteAria, CollectionOptions} from './useAutocomplete';
242 changes: 242 additions & 0 deletions packages/@react-aria/autocomplete/src/useAutocomplete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
/*
* Copyright 2024 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import {AriaLabelingProps, BaseEvent, DOMProps, RefObject} from '@react-types/shared';
import {AutocompleteProps, AutocompleteState} from '@react-stately/autocomplete';
import {ChangeEvent, InputHTMLAttributes, KeyboardEvent as ReactKeyboardEvent, useCallback, useEffect, useMemo, useRef} from 'react';
import {CLEAR_FOCUS_EVENT, FOCUS_EVENT, mergeProps, mergeRefs, UPDATE_ACTIVEDESCENDANT, useEffectEvent, useId, useLabels, useObjectRef} from '@react-aria/utils';
// @ts-ignore
import intlMessages from '../intl/*.json';
import {useFilter, useLocalizedStringFormatter} from '@react-aria/i18n';

export interface CollectionOptions extends DOMProps, AriaLabelingProps {
/** Whether the collection items should use virtual focus instead of being focused directly. */
shouldUseVirtualFocus: boolean,
/** Whether typeahead is disabled. */
disallowTypeAhead: boolean
}
export interface AriaAutocompleteProps extends AutocompleteProps {

/**
* The filter function used to determine if a option should be included in the autocomplete list.
* @default contains
*/
defaultFilter?: (textValue: string, inputValue: string) => boolean
}

export interface AriaAutocompleteOptions extends Omit<AriaAutocompleteProps, 'children'> {
/** The ref for the wrapped collection element. */
collectionRef: RefObject<HTMLElement | null>
}

export interface AutocompleteAria {
/** Props for the autocomplete input element. */
inputProps: InputHTMLAttributes<HTMLInputElement>,
/** Props for the collection, to be passed to collection's respective aria hook (e.g. useMenu). */
collectionProps: CollectionOptions,
/** Ref to attach to the wrapped collection. */
collectionRef: RefObject<HTMLElement | null>,
/** A filter function that returns if the provided collection node should be filtered out of the collection. */
filterFn: (nodeTextValue: string) => boolean
}

/**
* Provides the behavior and accessibility implementation for a autocomplete component.
* A autocomplete combines a text input with a collection, allowing users to filter the collection's contents match a query.
* @param props - Props for the autocomplete.
* @param state - State for the autocomplete, as returned by `useAutocompleteState`.
*/
export function useAutocomplete(props: AriaAutocompleteOptions, state: AutocompleteState): AutocompleteAria {
let {
collectionRef,
defaultFilter
} = props;

let collectionId = useId();
let timeout = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
let delayNextActiveDescendant = useRef(false);
let lastCollectionNode = useRef<HTMLElement>(null);

let updateActiveDescendant = useCallback((e) => {
let {detail} = e;
clearTimeout(timeout.current);
e.stopPropagation();
if (detail?.id != null) {
if (delayNextActiveDescendant.current) {
timeout.current = setTimeout(() => {
state.setFocusedNodeId(detail.id);
}, 500);
} else {
state.setFocusedNodeId(detail.id);
}
} else {
state.setFocusedNodeId(null);
}

delayNextActiveDescendant.current = false;
}, [state]);
LFDanLu marked this conversation as resolved.
Show resolved Hide resolved

let callbackRef = useEffectEvent((collectionNode: HTMLElement | null) => {
// When typing forward, we want to delay the setting of active descendant to not interrupt the native screen reader announcement
// of the letter you just typed. If we recieve another UPDATE_ACTIVEDESCENDANT call then we clear the queued update
// We track lastCollectionNode to do proper cleanup since callbackRefs just pass null when unmounting. This also handles
// React 19's extra call of the callback ref in strict mode
if (collectionNode != null) {
lastCollectionNode.current?.removeEventListener(UPDATE_ACTIVEDESCENDANT, updateActiveDescendant);
lastCollectionNode.current = collectionNode;
collectionNode.addEventListener(UPDATE_ACTIVEDESCENDANT, updateActiveDescendant);
} else {
lastCollectionNode.current?.removeEventListener(UPDATE_ACTIVEDESCENDANT, updateActiveDescendant);
}
});
// Make sure to memo so that React doesn't keep registering a new event listeners on every rerender of the wrapped collection,
// especially since callback refs's node parameter is null when they cleanup so we can't even clean up properly
let mergedCollectionRef = useObjectRef(useMemo(() => mergeRefs(collectionRef, callbackRef), [collectionRef, callbackRef]));
LFDanLu marked this conversation as resolved.
Show resolved Hide resolved

let focusFirstItem = useEffectEvent(() => {
let focusFirstEvent = new CustomEvent(FOCUS_EVENT, {
cancelable: true,
bubbles: true,
detail: {
focusStrategy: 'first'
}
});

collectionRef.current?.dispatchEvent(focusFirstEvent);
delayNextActiveDescendant.current = true;
LFDanLu marked this conversation as resolved.
Show resolved Hide resolved
});

let clearVirtualFocus = useEffectEvent(() => {
state.setFocusedNodeId(null);
let clearFocusEvent = new CustomEvent(CLEAR_FOCUS_EVENT, {
cancelable: true,
bubbles: true
});
clearTimeout(timeout.current);
delayNextActiveDescendant.current = false;
collectionRef.current?.dispatchEvent(clearFocusEvent);
});

// Tell wrapped collection to focus the first element in the list when typing forward and to clear focused key when deleting text
// for screen reader announcements
let lastInputValue = useRef<string | null>(null);
useEffect(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any chance this can be done in an event (e.g. onChange) rather than an effect? Seems like that would be more explicit about the intent (the user typed something, not a result of some external state update).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when I changed this to onChange in an attempt to have the input value be managed at the input level instead, I ran into the problem where focusFirstItem was firing before the collection filtered itself in response and thus was getting the incorrect activedescendant id. I'll try this again now that the input text is managed at the top level again

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, if I use onChange here the focusFirstItem happens before the collection is filtered in response to the change in input value.

if (state.inputValue != null) {
if (lastInputValue.current != null && lastInputValue.current !== state.inputValue && lastInputValue.current?.length <= state.inputValue.length) {
focusFirstItem();
} else {
clearVirtualFocus();
}

lastInputValue.current = state.inputValue;
}
}, [state.inputValue, focusFirstItem, clearVirtualFocus]);

// For textfield specific keydown operations
let onKeyDown = (e: BaseEvent<ReactKeyboardEvent<any>>) => {
if (e.nativeEvent.isComposing) {
return;
}

switch (e.key) {
case 'Escape':
// Early return for Escape here so it doesn't leak the Escape event from the simulated collection event below and
// close the dialog prematurely. Ideally that should be up to the discretion of the input element hence the check
// for isPropagationStopped
if (e.isPropagationStopped()) {
return;
}
break;
case ' ':
// Space shouldn't trigger onAction so early return.

return;
case 'Home':
case 'End':
case 'PageDown':
case 'PageUp':
case 'ArrowUp':
case 'ArrowDown': {
// Prevent these keys from moving the text cursor in the input
e.preventDefault();
// Move virtual focus into the wrapped collection
let focusCollection = new CustomEvent(FOCUS_EVENT, {
cancelable: true,
bubbles: true
});

collectionRef.current?.dispatchEvent(focusCollection);
break;
}
case 'ArrowLeft':
case 'ArrowRight':
// TODO: will need to special case this so it doesn't clear the focused key if we are currently
// focused on a submenutrigger? May not need to since focus would
// But what about wrapped grids where ArrowLeft and ArrowRight should navigate left/right
clearVirtualFocus();
break;
}

// Emulate the keyboard events that happen in the input field in the wrapped collection. This is for triggering things like onAction via Enter
// or moving focus from one item to another
if (state.focusedNodeId == null) {
collectionRef.current?.dispatchEvent(
new KeyboardEvent(e.nativeEvent.type, e.nativeEvent)
);
} else {
let item = collectionRef.current?.querySelector(`#${CSS.escape(state.focusedNodeId)}`);
LFDanLu marked this conversation as resolved.
Show resolved Hide resolved
item?.dispatchEvent(
new KeyboardEvent(e.nativeEvent.type, e.nativeEvent)
);
}
};

let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/autocomplete');
let collectionProps = useLabels({
id: collectionId,
'aria-label': stringFormatter.format('collectionLabel')
});

let {contains} = useFilter({sensitivity: 'base'});
let filterFn = useCallback((nodeTextValue: string) => {
if (defaultFilter) {
return defaultFilter(nodeTextValue, state.inputValue);
}

return contains(nodeTextValue, state.inputValue);
}, [state.inputValue, defaultFilter, contains]) ;

return {
inputProps: {
value: state.inputValue,
onChange: (e: ChangeEvent<HTMLInputElement>) => state.setInputValue(e.target.value),
onKeyDown,
LFDanLu marked this conversation as resolved.
Show resolved Hide resolved
LFDanLu marked this conversation as resolved.
Show resolved Hide resolved
autoComplete: 'off',
'aria-haspopup': 'listbox',
'aria-controls': collectionId,
// TODO: readd proper logic for completionMode = complete (aria-autocomplete: both)
'aria-autocomplete': 'list',
'aria-activedescendant': state.focusedNodeId ?? undefined,
// This disable's iOS's autocorrect suggestions, since the autocomplete provides its own suggestions.
autoCorrect: 'off',
// This disable's the macOS Safari spell check auto corrections.
spellCheck: 'false'
},
collectionProps: mergeProps(collectionProps, {
// TODO: shouldFocusOnHover?
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wonder if it might make sense to change focus on hover by default for the wrapped collection

shouldUseVirtualFocus: true,
disallowTypeAhead: true
}),
collectionRef: mergedCollectionRef,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a bit strange to return refs from a hook, but I guess in this case we need a callback ref so not sure how else we'd handle it...

filterFn
};
}
Loading