Skip to content

Commit 5b134c4

Browse files
authored
Merge pull request Expensify#76228 from software-mansion-labs/@zfurtak/fix-input-scrolling
[CP Staging] Manage scrolling with text input in `SelectionList`
2 parents 02b8ad2 + e9e7f1d commit 5b134c4

2 files changed

Lines changed: 52 additions & 7 deletions

File tree

src/components/SelectionList/BaseSelectionList.tsx

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager';
1212
import useDebounce from '@hooks/useDebounce';
1313
import useKeyboardShortcut from '@hooks/useKeyboardShortcut';
1414
import useKeyboardState from '@hooks/useKeyboardState';
15+
import usePrevious from '@hooks/usePrevious';
1516
import useSafeAreaPaddings from '@hooks/useSafeAreaPaddings';
1617
import useScrollEnabled from '@hooks/useScrollEnabled';
1718
import useSingleExecution from '@hooks/useSingleExecution';
@@ -288,7 +289,6 @@ function BaseSelectionList<TItem extends ListItem>({
288289
onFocusChange={(v: boolean) => (isTextInputFocusedRef.current = v)}
289290
showLoadingPlaceholder={showLoadingPlaceholder}
290291
isLoadingNewOptions={isLoadingNewOptions}
291-
setFocusedIndex={setFocusedIndex}
292292
/>
293293
);
294294
};
@@ -392,6 +392,56 @@ function BaseSelectionList<TItem extends ListItem>({
392392
[data.length, scrollToIndex, setFocusedIndex],
393393
);
394394

395+
const prevSearchValue = usePrevious(textInputOptions?.value);
396+
const prevSelectedOptionsLength = usePrevious(dataDetails.selectedOptions.length);
397+
const prevAllOptionsLength = usePrevious(data.length);
398+
399+
useEffect(() => {
400+
const currentSearchValue = textInputOptions?.value;
401+
const searchChanged = prevSearchValue !== currentSearchValue;
402+
const selectedOptionsChanged = dataDetails.selectedOptions.length !== prevSelectedOptionsLength;
403+
// Do not change focus if:
404+
// 1. Input value is the same or
405+
// 2. Data length is 0 or
406+
// 3. shouldUpdateFocusedIndex is true => other function handles the focus
407+
if ((!searchChanged && !selectedOptionsChanged) || data.length === 0 || shouldUpdateFocusedIndex) {
408+
return;
409+
}
410+
411+
const hasSearchBeenCleared = prevSearchValue && !currentSearchValue;
412+
if (hasSearchBeenCleared) {
413+
const foundSelectedItemIndex = data.findIndex(isItemSelected);
414+
415+
if (foundSelectedItemIndex !== -1 && !canSelectMultiple) {
416+
scrollToIndex(foundSelectedItemIndex);
417+
setFocusedIndex(foundSelectedItemIndex);
418+
return;
419+
}
420+
}
421+
422+
// Remove focus (set focused index to -1) if:
423+
// 1. If the search is idle or
424+
// 2. If the user is just toggling options without changing the list content
425+
// Otherwise (e.g. when filtering/typing), focus on the first item (0)
426+
const isSearchIdle = !prevSearchValue && !currentSearchValue;
427+
const newSelectedIndex = isSearchIdle || (selectedOptionsChanged && prevAllOptionsLength === data.length) ? -1 : 0;
428+
429+
scrollToIndex(newSelectedIndex);
430+
setFocusedIndex(newSelectedIndex);
431+
}, [
432+
canSelectMultiple,
433+
data,
434+
dataDetails.selectedOptions.length,
435+
isItemSelected,
436+
prevAllOptionsLength,
437+
prevSelectedOptionsLength,
438+
prevSearchValue,
439+
scrollToIndex,
440+
setFocusedIndex,
441+
shouldUpdateFocusedIndex,
442+
textInputOptions?.value,
443+
]);
444+
395445
useEffect(() => {
396446
if (!itemFocusTimeoutRef.current) {
397447
return;

src/components/SelectionList/components/TextInput.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,6 @@ type TextInputProps = {
4545
/** Whether to show the loading indicator for new options */
4646
isLoadingNewOptions?: boolean;
4747

48-
/** Function to update the focused index in the list */
49-
setFocusedIndex: (index: number) => void;
50-
5148
/** Function to focus text input component */
5249
focusTextInput: () => void;
5350
};
@@ -64,7 +61,6 @@ function TextInput({
6461
showLoadingPlaceholder,
6562
isLoadingNewOptions,
6663
shouldShowTextInput,
67-
setFocusedIndex,
6864
focusTextInput,
6965
}: TextInputProps) {
7066
const styles = useThemeStyles();
@@ -80,9 +76,8 @@ function TextInput({
8076
const handleTextInputChange = useCallback(
8177
(text: string) => {
8278
onChangeText?.(text);
83-
setFocusedIndex(0);
8479
},
85-
[onChangeText, setFocusedIndex],
80+
[onChangeText],
8681
);
8782

8883
useFocusEffect(

0 commit comments

Comments
 (0)