Skip to content

Commit

Permalink
fix Enter shortcut logic
Browse files Browse the repository at this point in the history
  • Loading branch information
SzymczakJ committed Sep 26, 2024
1 parent 445a031 commit 6e4d2b2
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 11 deletions.
9 changes: 6 additions & 3 deletions src/components/Search/SearchRouter/SearchRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import {useNavigationState} from '@react-navigation/native';
import debounce from 'lodash/debounce';
import React, {useCallback, useMemo, useState} from 'react';
import React, {useCallback, useMemo, useRef, useState} from 'react';
import {View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import FocusTrapForModal from '@components/FocusTrap/FocusTrapForModal';
import Modal from '@components/Modal';
import {useOptionsList} from '@components/OptionListContextProvider';
import type {SearchQueryJSON} from '@components/Search/types';
import type {SelectionListHandle} from '@components/SelectionList/types';
import useKeyboardShortcut from '@hooks/useKeyboardShortcut';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
Expand All @@ -30,6 +31,7 @@ function SearchRouter() {

const {isSmallScreenWidth} = useResponsiveLayout();
const {isSearchRouterDisplayed, closeSearchRouter} = useSearchRouterContext();
const listRef = useRef<SelectionListHandle>(null);

const [textInputValue, setTextInputValue] = useState('');
const [userSearchQuery, setUserSearchQuery] = useState<SearchQueryJSON | undefined>(undefined);
Expand Down Expand Up @@ -66,7 +68,7 @@ function SearchRouter() {
clearUserQuery();
return;
}

listRef.current?.updateAndScrollToFocusedIndex(0);
const queryJSON = SearchUtils.buildSearchQueryJSON(userQuery);

if (queryJSON) {
Expand Down Expand Up @@ -127,8 +129,8 @@ function SearchRouter() {
onSubmit={() => {
onSearchSubmit(userSearchQuery);
}}
routerListRef={listRef}
/>

<SearchRouterList
currentQuery={userSearchQuery}
reportForContextualSearch={contextualReportData}
Expand All @@ -137,6 +139,7 @@ function SearchRouter() {
onSearchSubmit={onSearchSubmit}
updateUserSearchQuery={updateUserSearchQuery}
closeAndClearRouter={closeAndClearRouter}
ref={listRef}
/>
</View>
</FocusTrapForModal>
Expand Down
11 changes: 10 additions & 1 deletion src/components/Search/SearchRouter/SearchRouterInput.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import React from 'react';
import type {RefObject} from 'react';
import type {SelectionListHandle} from '@components/SelectionList/types';
import BaseTextInput from '@components/TextInput/BaseTextInput';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
Expand All @@ -8,9 +10,10 @@ type SearchRouterInputProps = {
setValue: (searchTerm: string) => void;
updateSearch: (searchTerm: string) => void;
onSubmit: () => void;
routerListRef: RefObject<SelectionListHandle>;
};

function SearchRouterInput({value, setValue, updateSearch, onSubmit}: SearchRouterInputProps) {
function SearchRouterInput({value, setValue, updateSearch, onSubmit, routerListRef}: SearchRouterInputProps) {
const styles = useThemeStyles();

const onChangeText = (text: string) => {
Expand All @@ -28,6 +31,12 @@ function SearchRouterInput({value, setValue, updateSearch, onSubmit}: SearchRout
inputStyle={[styles.searchInputStyle, styles.searchRouterInputStyle, styles.ph2]}
role={CONST.ROLE.PRESENTATION}
autoCapitalize="none"
onFocus={() => {
routerListRef?.current?.updateExternalTextInputFocus(true);
}}
onBlur={() => {
routerListRef?.current?.updateExternalTextInputFocus(false);
}}
/>
);
}
Expand Down
15 changes: 10 additions & 5 deletions src/components/Search/SearchRouter/SearchRouterList.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React, {useCallback} from 'react';
import React, {forwardRef, useCallback} from 'react';
import type {ForwardedRef} from 'react';
import * as Expensicons from '@components/Icon/Expensicons';
import type {SearchQueryJSON, SearchRouterListItem} from '@components/Search/types';
import SelectionList from '@components/SelectionList';
import SingleIconListItem from '@components/SelectionList/Search/SingleIconListItem';
import type {ListItemWithSingleIcon, SingleIconListItemProps} from '@components/SelectionList/Search/SingleIconListItem';
import type {SectionListDataType, UserListItemProps} from '@components/SelectionList/types';
import type {SectionListDataType, SelectionListHandle, UserListItemProps} from '@components/SelectionList/types';
import UserListItem from '@components/SelectionList/UserListItem';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
Expand Down Expand Up @@ -44,7 +45,10 @@ function SearchRouterItem(props: UserListItemProps<SearchRouterListItem> | Singl
return <SingleIconListItem {...(props as SingleIconListItemProps<ListItemWithSingleIcon & ItemWithQuery>)} />;
}

function SearchRouterList({currentQuery, reportForContextualSearch, recentSearches, recentReports, onSearchSubmit, updateUserSearchQuery, closeAndClearRouter}: SearchRouterListProps) {
function SearchRouterList(
{currentQuery, reportForContextualSearch, recentSearches, recentReports, onSearchSubmit, updateUserSearchQuery, closeAndClearRouter}: SearchRouterListProps,
ref: ForwardedRef<SelectionListHandle>,
) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const sections: Array<SectionListDataType<SearchRouterListItem>> = [];
Expand Down Expand Up @@ -96,7 +100,7 @@ function SearchRouterList({currentQuery, reportForContextualSearch, recentSearch

const onSelectRow = useCallback(
(item: SearchRouterListItem) => {
// eslint-disable-next-line default-case
// eslint-disable-next-line default-case, @typescript-eslint/switch-exhaustiveness-check
switch (item.itemType) {
case CONST.SEARCH.ROUTER_LIST_ITEM_TYPE.SEARCH:
// Handle selection of "Recent search"
Expand Down Expand Up @@ -131,10 +135,11 @@ function SearchRouterList({currentQuery, reportForContextualSearch, recentSearch
onSelectRow={onSelectRow}
ListItem={SearchRouterItem}
containerStyle={styles.mh100}
ref={ref}
/>
);
}

export default SearchRouterList;
export default forwardRef(SearchRouterList);
export {SearchRouterItem};
export type {ItemWithQuery};
16 changes: 15 additions & 1 deletion src/components/SelectionList/BaseSelectionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,21 @@ function BaseSelectionList<TItem extends ListItem>(
[flattenedSections.allOptions, setFocusedIndex, updateAndScrollToFocusedIndex],
);

useImperativeHandle(ref, () => ({scrollAndHighlightItem, clearInputAfterSelect}), [scrollAndHighlightItem, clearInputAfterSelect]);
/**
* Changes isTextInputFocusedRef value when using external TextInput, to handle shouldSync focus properly
*
* @param isTextInputFocused - Is external TextInput focused.
*/
const updateExternalTextInputFocus = useCallback((isTextInputFocused: boolean) => {
isTextInputFocusedRef.current = isTextInputFocused;
}, []);

useImperativeHandle(ref, () => ({scrollAndHighlightItem, clearInputAfterSelect, updateAndScrollToFocusedIndex, updateExternalTextInputFocus}), [
scrollAndHighlightItem,
clearInputAfterSelect,
updateAndScrollToFocusedIndex,
updateExternalTextInputFocus,
]);

/** Selects row when pressing Enter */
useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.ENTER, selectFocusedOption, {
Expand Down
4 changes: 3 additions & 1 deletion src/components/SelectionList/Search/SingleIconListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ type SingleIconListItemProps<TItem extends ListItemWithSingleIcon> = {
showTooltip?: boolean;
onSelectRow: (item: TItem) => void;
onFocus?: () => void;
shouldSyncFocus?: boolean;
};

function SingleIconListItem<TItem extends ListItemWithSingleIcon>({item, isFocused, showTooltip, onSelectRow, onFocus}: SingleIconListItemProps<TItem>) {
function SingleIconListItem<TItem extends ListItemWithSingleIcon>({item, isFocused, showTooltip, onSelectRow, onFocus, shouldSyncFocus}: SingleIconListItemProps<TItem>) {
const styles = useThemeStyles();
const theme = useTheme();

Expand All @@ -32,6 +33,7 @@ function SingleIconListItem<TItem extends ListItemWithSingleIcon>({item, isFocus
keyForList={item.keyForList}
onFocus={onFocus}
hoverStyle={item.isSelected && styles.activeComponentBG}
shouldSyncFocus={shouldSyncFocus}
>
<>
{item.singleIcon && (
Expand Down
2 changes: 2 additions & 0 deletions src/components/SelectionList/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,8 @@ type BaseSelectionListProps<TItem extends ListItem> = Partial<ChildrenProps> & {
type SelectionListHandle = {
scrollAndHighlightItem?: (items: string[], timeout: number) => void;
clearInputAfterSelect?: () => void;
updateAndScrollToFocusedIndex: (newFocusedIndex: number) => void;
updateExternalTextInputFocus: (isTextInputFocused: boolean) => void;
};

type ItemLayout = {
Expand Down

0 comments on commit 6e4d2b2

Please sign in to comment.