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

Make sure the first result in most recents is highlighted when user uses CMD+K #52298

Merged
merged 19 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
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
81 changes: 50 additions & 31 deletions src/components/Search/SearchRouter/SearchRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type PersonalDetails from '@src/types/onyx/PersonalDetails';
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue';
import {getQueryWithSubstitutions} from './getQueryWithSubstitutions';
import type {SubstitutionMap} from './getQueryWithSubstitutions';
import {getUpdatedSubstitutionsMap} from './getUpdatedSubstitutionsMap';
Expand All @@ -55,11 +56,13 @@ function SearchRouter({onRouterClose, shouldHideInputCaret}: SearchRouterProps)
const styles = useThemeStyles();
const {translate} = useLocalize();
const [betas] = useOnyx(ONYXKEYS.BETAS);
const [recentSearches] = useOnyx(ONYXKEYS.RECENT_SEARCHES);
const [recentSearches, recentSearchesMetadata] = useOnyx(ONYXKEYS.RECENT_SEARCHES);
const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false});
const [autocompleteSuggestions, setAutocompleteSuggestions] = useState<AutocompleteItemData[] | undefined>([]);
const [autocompleteSubstitutions, setAutocompleteSubstitutions] = useState<SubstitutionMap>({});

const {isLargeScreenWidth} = useResponsiveLayout();
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
const {isLargeScreenWidth} = useResponsiveLayout();


const {shouldUseNarrowLayout} = useResponsiveLayout();
const listRef = useRef<SelectionListHandle>(null);

Expand Down Expand Up @@ -329,6 +332,11 @@ function SearchRouter({onRouterClose, shouldHideInputCaret}: SearchRouterProps)
Report.searchInServer(debouncedInputValue.trim());
}, [debouncedInputValue]);

const setInitialFocus = useCallback(() => {
const initialFocusIndex = (sortedRecentSearches?.slice(0, 5).length ?? 0) + (contextualReportData ? 1 : 0);
listRef.current?.setFocusedIndex(initialFocusIndex);
}, [sortedRecentSearches, contextualReportData]);

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
const setInitialFocus = useCallback(() => {
const initialFocusIndex = (sortedRecentSearches?.slice(0, 5).length ?? 0) + (contextualReportData ? 1 : 0);
listRef.current?.setFocusedIndex(initialFocusIndex);
}, [sortedRecentSearches, contextualReportData]);

const onSearchChange = useCallback(
(userQuery: string) => {
let newUserQuery = userQuery;
Expand All @@ -344,14 +352,16 @@ function SearchRouter({onRouterClose, shouldHideInputCaret}: SearchRouterProps)

if (newUserQuery || !isEmpty(prevUserQueryRef.current)) {
listRef.current?.updateAndScrollToFocusedIndex(0);
} else {
} else if (!isLargeScreenWidth) {
listRef.current?.updateAndScrollToFocusedIndex(-1);
} else {
setInitialFocus();
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
} else if (!isLargeScreenWidth) {
listRef.current?.updateAndScrollToFocusedIndex(-1);
} else {
setInitialFocus();
} else {
listRef.current?.updateAndScrollToFocusedIndex(-1);
}

}

// Store the previous newUserQuery
prevUserQueryRef.current = newUserQuery;
},
[autocompleteSubstitutions, autocompleteSuggestions, setTextInputValue, updateAutocomplete],
[autocompleteSubstitutions, autocompleteSuggestions, setTextInputValue, updateAutocomplete, isLargeScreenWidth, setInitialFocus],
);

const onSearchSubmit = useCallback(
Expand Down Expand Up @@ -385,6 +395,10 @@ function SearchRouter({onRouterClose, shouldHideInputCaret}: SearchRouterProps)

const modalWidth = shouldUseNarrowLayout ? styles.w100 : {width: variables.searchRouterPopoverWidth};

const isDataLoaded = useMemo(() => {
return (!contextualReportID || contextualReportData !== undefined) && !isLoadingOnyxValue(recentSearchesMetadata) && recentReports.length > 0;
}, [contextualReportID, contextualReportData, recentSearchesMetadata, recentReports]);

return (
<View
style={[styles.flex1, modalWidth, styles.h100, !shouldUseNarrowLayout && styles.mh85vh]}
Expand All @@ -396,34 +410,39 @@ function SearchRouter({onRouterClose, shouldHideInputCaret}: SearchRouterProps)
onBackButtonPress={() => onRouterClose()}
/>
)}
<SearchRouterInput
value={textInputValue}
isFullWidth={shouldUseNarrowLayout}
updateSearch={onSearchChange}
onSubmit={() => {
onSearchSubmit(textInputValue);
}}
caretHidden={shouldHideInputCaret}
routerListRef={listRef}
shouldShowOfflineMessage
wrapperStyle={[styles.border, styles.alignItemsCenter]}
outerWrapperStyle={[shouldUseNarrowLayout ? styles.mv3 : styles.mv2, shouldUseNarrowLayout ? styles.mh5 : styles.mh2]}
wrapperFocusedStyle={[styles.borderColorFocus]}
isSearchingForReports={isSearchingForReports}
/>
<SearchRouterList
textInputValue={textInputValue}
updateSearchValue={onSearchChange}
setTextInputValue={setTextInputValue}
reportForContextualSearch={contextualReportData}
recentSearches={sortedRecentSearches?.slice(0, 5)}
recentReports={recentReports}
autocompleteSuggestions={autocompleteSuggestions}
onSearchSubmit={onSearchSubmit}
closeRouter={onRouterClose}
onAutocompleteSuggestionClick={onAutocompleteSuggestionClick}
ref={listRef}
/>

{(isDataLoaded || !!debouncedInputValue) && (
nyomanjyotisa marked this conversation as resolved.
Show resolved Hide resolved
<>
<SearchRouterInput
value={textInputValue}
isFullWidth={shouldUseNarrowLayout}
updateSearch={onSearchChange}
onSubmit={() => {
onSearchSubmit(textInputValue);
}}
caretHidden={shouldHideInputCaret}
routerListRef={listRef}
shouldShowOfflineMessage
wrapperStyle={[styles.border, styles.alignItemsCenter]}
outerWrapperStyle={[shouldUseNarrowLayout ? styles.mv3 : styles.mv2, shouldUseNarrowLayout ? styles.mh5 : styles.mh2]}
wrapperFocusedStyle={[styles.borderColorFocus]}
isSearchingForReports={isSearchingForReports}
/>
<SearchRouterList
textInputValue={textInputValue}
updateSearchValue={onSearchChange}
setTextInputValue={setTextInputValue}
reportForContextualSearch={contextualReportData}
recentSearches={sortedRecentSearches?.slice(0, 5)}
recentReports={recentReports}
autocompleteSuggestions={autocompleteSuggestions}
onSearchSubmit={onSearchSubmit}
closeRouter={onRouterClose}
onAutocompleteSuggestionClick={onAutocompleteSuggestionClick}
ref={listRef}
/>
</>
)}
</View>
);
}
Expand Down
20 changes: 17 additions & 3 deletions src/components/Search/SearchRouter/SearchRouterList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {forwardRef, useCallback} from 'react';
import React, {forwardRef, useCallback, useEffect, useState} from 'react';
import type {ForwardedRef} from 'react';
import {useOnyx} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
Expand Down Expand Up @@ -148,14 +148,16 @@ function SearchRouterList(
) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {shouldUseNarrowLayout} = useResponsiveLayout();
const {shouldUseNarrowLayout, isLargeScreenWidth} = useResponsiveLayout();

const personalDetails = usePersonalDetails();
const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT);
const taxRates = getAllTaxRates();
const [cardList = {}] = useOnyx(ONYXKEYS.CARD_LIST);
const sections: Array<SectionListDataType<OptionData | SearchQueryItem>> = [];

const [isInitialRender, setIsInitialRender] = useState(true);

if (textInputValue) {
sections.push({
data: [
Expand Down Expand Up @@ -298,6 +300,13 @@ function SearchRouterList(
[setTextInputValue, textInputValue, onAutocompleteSuggestionClick],
);

useEffect(() => {
if (textInputValue) {
return;
}
setIsInitialRender(true);
}, [textInputValue]);

return (
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
useEffect(() => {
if (textInputValue) {
return;
}
setIsInitialRender(true);
}, [textInputValue]);

<SelectionList<OptionData | SearchQueryItem>
sections={sections}
Expand All @@ -307,12 +316,17 @@ function SearchRouterList(
sectionListStyle={[shouldUseNarrowLayout ? styles.ph5 : styles.ph2, styles.pb2]}
listItemWrapperStyle={[styles.pr0, styles.pl0]}
getItemHeight={getItemHeight}
onLayout={setPerformanceTimersEnd}
onLayout={() => {
setPerformanceTimersEnd();
setIsInitialRender(false);
}}
ref={ref}
showScrollIndicator={!shouldUseNarrowLayout}
sectionTitleStyles={styles.mhn2}
shouldSingleExecuteRowSelect
onArrowFocus={onArrowFocus}
initiallyFocusedOptionKey={isLargeScreenWidth ? styledRecentReports.at(0)?.keyForList : undefined}
shouldScrollToFocusedIndex={!isInitialRender}
/>
);
}
Expand Down
14 changes: 10 additions & 4 deletions src/components/SelectionList/BaseSelectionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ function BaseSelectionList<TItem extends ListItem>(
shouldKeepFocusedItemAtTopOfViewableArea = false,
shouldDebounceScrolling = false,
shouldPreventActiveCellVirtualization = false,
shouldScrollToFocusedIndex = true,
}: BaseSelectionListProps<TItem>,
ref: ForwardedRef<SelectionListHandle>,
) {
Expand Down Expand Up @@ -321,7 +322,9 @@ function BaseSelectionList<TItem extends ListItem>(
if (focusedItem) {
onArrowFocus(focusedItem);
}
(shouldDebounceScrolling ? debouncedScrollToIndex : scrollToIndex)(index, true);
if (shouldScrollToFocusedIndex) {
(shouldDebounceScrolling ? debouncedScrollToIndex : scrollToIndex)(index, true);
}
},
isFocused,
});
Expand Down Expand Up @@ -586,10 +589,12 @@ function BaseSelectionList<TItem extends ListItem>(
if (!isInitialSectionListRender) {
return;
}
scrollToIndex(focusedIndex, false);
if (shouldScrollToFocusedIndex) {
scrollToIndex(focusedIndex, false);
}
setIsInitialSectionListRender(false);
},
[focusedIndex, isInitialSectionListRender, scrollToIndex, shouldUseDynamicMaxToRenderPerBatch],
[focusedIndex, isInitialSectionListRender, scrollToIndex, shouldUseDynamicMaxToRenderPerBatch, shouldScrollToFocusedIndex],
);

const onSectionListLayout = useCallback(
Expand Down Expand Up @@ -716,12 +721,13 @@ function BaseSelectionList<TItem extends ListItem>(
isTextInputFocusedRef.current = isTextInputFocused;
}, []);

useImperativeHandle(ref, () => ({scrollAndHighlightItem, clearInputAfterSelect, updateAndScrollToFocusedIndex, updateExternalTextInputFocus, scrollToIndex}), [
useImperativeHandle(ref, () => ({scrollAndHighlightItem, clearInputAfterSelect, updateAndScrollToFocusedIndex, updateExternalTextInputFocus, scrollToIndex, setFocusedIndex}), [
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
useImperativeHandle(ref, () => ({scrollAndHighlightItem, clearInputAfterSelect, updateAndScrollToFocusedIndex, updateExternalTextInputFocus, scrollToIndex, setFocusedIndex}), [
useImperativeHandle(ref, () => ({scrollAndHighlightItem, clearInputAfterSelect, updateAndScrollToFocusedIndex, updateExternalTextInputFocus, scrollToIndex}), [

scrollAndHighlightItem,
clearInputAfterSelect,
updateAndScrollToFocusedIndex,
updateExternalTextInputFocus,
scrollToIndex,
setFocusedIndex,
]);

/** Selects row when pressing Enter */
Expand Down
4 changes: 4 additions & 0 deletions src/components/SelectionList/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -612,12 +612,16 @@ type BaseSelectionListProps<TItem extends ListItem> = Partial<ChildrenProps> & {

/** Whether to prevent the active cell from being virtualized and losing focus in browsers */
shouldPreventActiveCellVirtualization?: boolean;

/** Whether to scroll to the focused index */
shouldScrollToFocusedIndex?: boolean;
} & TRightHandSideComponent<TItem>;

type SelectionListHandle = {
scrollAndHighlightItem?: (items: string[], timeout: number) => void;
clearInputAfterSelect?: () => void;
scrollToIndex: (index: number, animated?: boolean) => void;
setFocusedIndex: (index: number) => void;
updateAndScrollToFocusedIndex: (newFocusedIndex: number) => void;
updateExternalTextInputFocus: (isTextInputFocused: boolean) => void;
};
Expand Down
Loading