diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index 08a1c41469fb..51bd8705edf3 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -181,7 +181,7 @@ function BaseSelectionList({ setShouldDisableHoverStyle(true); }, [setShouldDisableHoverStyle]); - const [focusedIndex, setFocusedIndex, currentHoverIndexRef] = useArrowKeyFocusManager({ + const [focusedIndex, setFocusedIndex] = useArrowKeyFocusManager({ initialFocusedIndex, maxIndex: data.length - 1, disabledIndexes: dataDetails.disabledArrowKeyIndexes, @@ -319,16 +319,6 @@ function BaseSelectionList({ ); }; - const setCurrentHoverIndex = useCallback( - (hoverIndex: number | null) => { - if (shouldDisableHoverStyle) { - return; - } - currentHoverIndexRef.current = hoverIndex; - }, - [currentHoverIndexRef, shouldDisableHoverStyle], - ); - const renderItem: ListRenderItem = ({item, index}: ListRenderItemInfo) => { const isItemDisabled = isDisabled || item.isDisabled; const selected = isItemSelected(item); @@ -336,51 +326,41 @@ function BaseSelectionList({ const isItemHighlighted = !!itemsToHighlight?.has(item.keyForList); return ( - setCurrentHoverIndex(index)} - onMouseEnter={() => setCurrentHoverIndex(index)} - onMouseLeave={(e) => { - e.stopPropagation(); - setCurrentHoverIndex(null); + - 1} - alternateTextNumberOfLines={alternateNumberOfSupportedLines} - shouldIgnoreFocus={shouldIgnoreFocus} - titleStyles={style?.listItemTitleStyles} - wrapperStyle={style?.listItemWrapperStyle} - titleContainerStyles={style?.listItemTitleContainerStyles} - errorRowStyles={style?.listItemErrorRowStyles} - singleExecution={singleExecution} - shouldHighlightSelectedItem={shouldHighlightSelectedItem} - shouldSyncFocus={!isTextInputFocusedRef.current && hasKeyBeenPressed.current} - shouldDisableHoverStyle={shouldDisableHoverStyle} - shouldStopMouseLeavePropagation={false} - shouldShowRightCaret={shouldShowRightCaret} - /> - + setFocusedIndex={setFocusedIndex} + index={index} + isFocused={isItemFocused} + isDisabled={isItemDisabled} + canSelectMultiple={canSelectMultiple} + onDismissError={onDismissError} + onLongPressRow={onLongPressRow} + onCheckboxPress={onCheckboxPress} + shouldSingleExecuteRowSelect={shouldSingleExecuteRowSelect} + shouldUseDefaultRightHandSideCheckmark={shouldUseDefaultRightHandSideCheckmark} + shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow} + rightHandSideComponent={rightHandSideComponent} + isMultilineSupported={isRowMultilineSupported} + isAlternateTextMultilineSupported={(alternateNumberOfSupportedLines ?? 0) > 1} + alternateTextNumberOfLines={alternateNumberOfSupportedLines} + shouldIgnoreFocus={shouldIgnoreFocus} + titleStyles={style?.listItemTitleStyles} + wrapperStyle={style?.listItemWrapperStyle} + titleContainerStyles={style?.listItemTitleContainerStyles} + errorRowStyles={style?.listItemErrorRowStyles} + singleExecution={singleExecution} + shouldHighlightSelectedItem={shouldHighlightSelectedItem} + shouldSyncFocus={!isTextInputFocusedRef.current && hasKeyBeenPressed.current} + shouldDisableHoverStyle={shouldDisableHoverStyle} + shouldShowRightCaret={shouldShowRightCaret} + /> ); }; diff --git a/src/components/SelectionList/ListItem/BaseListItem.tsx b/src/components/SelectionList/ListItem/BaseListItem.tsx index 525a5fb65cc8..59ef77d63452 100644 --- a/src/components/SelectionList/ListItem/BaseListItem.tsx +++ b/src/components/SelectionList/ListItem/BaseListItem.tsx @@ -44,7 +44,6 @@ function BaseListItem({ shouldUseDefaultRightHandSideCheckmark = true, shouldHighlightSelectedItem = true, shouldDisableHoverStyle, - shouldStopMouseLeavePropagation = true, shouldShowRightCaret = false, accessible, accessibilityRole = getButtonRole(true), @@ -63,9 +62,7 @@ function BaseListItem({ useSyncFocus(pressableRef, !!isFocused, shouldSyncFocus); const handleMouseLeave = (e: React.MouseEvent) => { bind.onMouseLeave(); - if (shouldStopMouseLeavePropagation) { - e.stopPropagation(); - } + e.stopPropagation(); setMouseUp(); }; diff --git a/src/components/SelectionList/ListItem/ListItemRenderer.tsx b/src/components/SelectionList/ListItem/ListItemRenderer.tsx index 98d387710b98..7d4d38e0243a 100644 --- a/src/components/SelectionList/ListItem/ListItemRenderer.tsx +++ b/src/components/SelectionList/ListItem/ListItemRenderer.tsx @@ -49,7 +49,6 @@ function ListItemRenderer({ shouldUseDefaultRightHandSideCheckmark, shouldHighlightSelectedItem, shouldDisableHoverStyle, - shouldStopMouseLeavePropagation, shouldShowRightCaret, errorRowStyles, }: ListItemRendererProps) { @@ -106,7 +105,6 @@ function ListItemRenderer({ shouldUseDefaultRightHandSideCheckmark={shouldUseDefaultRightHandSideCheckmark} shouldHighlightSelectedItem={shouldHighlightSelectedItem} shouldDisableHoverStyle={shouldDisableHoverStyle} - shouldStopMouseLeavePropagation={shouldStopMouseLeavePropagation} shouldShowRightCaret={shouldShowRightCaret} /> {item.footerContent && item.footerContent} diff --git a/src/components/SelectionList/ListItem/RadioListItem.tsx b/src/components/SelectionList/ListItem/RadioListItem.tsx index 021c45e4e5de..fca9c5add93d 100644 --- a/src/components/SelectionList/ListItem/RadioListItem.tsx +++ b/src/components/SelectionList/ListItem/RadioListItem.tsx @@ -25,7 +25,6 @@ function RadioListItem({ titleStyles, shouldHighlightSelectedItem = true, shouldDisableHoverStyle, - shouldStopMouseLeavePropagation, accessibilityRole, }: RadioListItemProps) { const styles = useThemeStyles(); @@ -51,7 +50,6 @@ function RadioListItem({ pendingAction={item.pendingAction} shouldHighlightSelectedItem={shouldHighlightSelectedItem} shouldDisableHoverStyle={shouldDisableHoverStyle} - shouldStopMouseLeavePropagation={shouldStopMouseLeavePropagation} accessibilityRole={accessibilityRole} > <> diff --git a/src/components/SelectionList/ListItem/UserListItem.tsx b/src/components/SelectionList/ListItem/UserListItem.tsx index e3a7cb4986f4..c83b28706baa 100644 --- a/src/components/SelectionList/ListItem/UserListItem.tsx +++ b/src/components/SelectionList/ListItem/UserListItem.tsx @@ -39,6 +39,7 @@ function UserListItem({ pressableStyle, shouldUseDefaultRightHandSideCheckmark, forwardedFSClass, + shouldDisableHoverStyle, }: UserListItemProps) { const icons = useMemoizedLazyExpensifyIcons(['ArrowRight', 'Checkmark'] as const); const styles = useThemeStyles(); @@ -94,102 +95,107 @@ function UserListItem({ keyForList={item.keyForList} onFocus={onFocus} shouldSyncFocus={shouldSyncFocus} + shouldDisableHoverStyle={shouldDisableHoverStyle} > - {(hovered?: boolean) => ( - <> - {!shouldUseDefaultRightHandSideCheckmark && !!canSelectMultiple && ( - - - {!!item.isSelected && ( - - )} - - - )} - {(!!reportExists || !!itemAccountID || !!policyID) && ( - - )} - - - {!!item.alternateText && ( - { + const isHovered = !!hovered && !shouldDisableHoverStyle; + + return ( + <> + {!shouldUseDefaultRightHandSideCheckmark && !!canSelectMultiple && ( + + + {!!item.isSelected && ( + + )} + + + )} + {(!!reportExists || !!itemAccountID || !!policyID) && ( + )} - - {!!item.rightElement && item.rightElement} - {!!item.shouldShowRightCaret && ( - - + + {!!item.alternateText && ( + + )} - )} - {!!shouldUseDefaultRightHandSideCheckmark && !!canSelectMultiple && ( - - - {!!item.isSelected && ( - - )} + {!!item.rightElement && item.rightElement} + {!!item.shouldShowRightCaret && ( + + - - )} - - )} + )} + {!!shouldUseDefaultRightHandSideCheckmark && !!canSelectMultiple && ( + + + {!!item.isSelected && ( + + )} + + + )} + + ); + }} ); } diff --git a/src/components/SelectionList/ListItem/types.ts b/src/components/SelectionList/ListItem/types.ts index e8851cf7945f..3f8c97c0c101 100644 --- a/src/components/SelectionList/ListItem/types.ts +++ b/src/components/SelectionList/ListItem/types.ts @@ -280,9 +280,6 @@ type ListItemProps = CommonListItemProps & { /** Whether to disable the hover style of the item */ shouldDisableHoverStyle?: boolean; - - /** Whether to call stopPropagation on the mouseleave event in BaseListItem */ - shouldStopMouseLeavePropagation?: boolean; }; type ValidListItem = @@ -327,9 +324,6 @@ type BaseListItemProps = CommonListItemProps & { /** Whether to disable the hover style of the item */ shouldDisableHoverStyle?: boolean; - /** Whether to call stopPropagation on the mouseleave event in BaseListItem */ - shouldStopMouseLeavePropagation?: boolean; - /** * Whether the pressable should be accessible as a single element. * When false, allows child elements (like TextInput) to be independently focusable by screen readers. diff --git a/src/components/SelectionList/SelectionListWithSections/BaseSelectionListWithSections.tsx b/src/components/SelectionList/SelectionListWithSections/BaseSelectionListWithSections.tsx index 593a179396ba..ac45224f09da 100644 --- a/src/components/SelectionList/SelectionListWithSections/BaseSelectionListWithSections.tsx +++ b/src/components/SelectionList/SelectionListWithSections/BaseSelectionListWithSections.tsx @@ -71,6 +71,8 @@ function BaseSelectionListWithSections({ shouldScrollToFocusedIndex = true, shouldSingleExecuteRowSelect = false, shouldPreventDefaultFocusOnSelectRow = false, + shouldDisableHoverStyle = false, + setShouldDisableHoverStyle = () => {}, canShowProductTrainingTooltip, }: SelectionListWithSectionsProps) { const styles = useThemeStyles(); @@ -120,6 +122,10 @@ function BaseSelectionListWithSections({ const debouncedScrollToIndex = useDebounce(scrollToIndex, CONST.TIMING.LIST_SCROLLING_DEBOUNCE_TIME, {leading: true, trailing: true}); + const onArrowUpDownCallback = useCallback(() => { + setShouldDisableHoverStyle(true); + }, [setShouldDisableHoverStyle]); + const [focusedIndex, setFocusedIndex] = useArrowKeyFocusManager({ initialFocusedIndex, maxIndex: flattenedData.length - 1, @@ -134,6 +140,7 @@ function BaseSelectionListWithSections({ }, setHasKeyBeenPressed, isFocused: isScreenFocused, + onArrowUpDownCallback, }); const getFocusedItem = (): TItem | undefined => { @@ -339,6 +346,7 @@ function BaseSelectionListWithSections({ shouldIgnoreFocus={shouldIgnoreFocus} wrapperStyle={style?.listItemWrapperStyle} titleStyles={style?.listItemTitleStyles} + shouldDisableHoverStyle={shouldDisableHoverStyle} /> ); } diff --git a/src/components/SelectionList/SelectionListWithSections/index.tsx b/src/components/SelectionList/SelectionListWithSections/index.tsx index a33d98b466d8..adc8288029f4 100644 --- a/src/components/SelectionList/SelectionListWithSections/index.tsx +++ b/src/components/SelectionList/SelectionListWithSections/index.tsx @@ -4,7 +4,10 @@ import BaseSelectionList from './BaseSelectionListWithSections'; import type {ListItem, SelectionListWithSectionsProps} from './types'; function SelectionList({shouldHideKeyboardOnScroll = true, ref, ...props}: SelectionListWithSectionsProps) { - const {shouldIgnoreFocus, shouldDebounceScrolling, onScroll} = useWebSelectionListBehavior({shouldHideKeyboardOnScroll}); + const {shouldIgnoreFocus, shouldDebounceScrolling, shouldDisableHoverStyle, setShouldDisableHoverStyle, onScroll} = useWebSelectionListBehavior({ + shouldTrackHoverStyle: true, + shouldHideKeyboardOnScroll, + }); return ( ({shouldHideKeyboardOnScroll = tru // For example, a long press will trigger a focus event on mobile chrome. shouldIgnoreFocus={shouldIgnoreFocus} shouldDebounceScrolling={shouldDebounceScrolling} + shouldDisableHoverStyle={shouldDisableHoverStyle} + setShouldDisableHoverStyle={setShouldDisableHoverStyle} /> ); } diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 4eb07895c456..c6eced0fda02 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -108,6 +108,12 @@ type BaseSelectionListProps = { /** Configuration for the confirm button */ confirmButtonOptions?: ConfirmButtonOptions; + + /** Whether hover style should be disabled */ + shouldDisableHoverStyle?: boolean; + + /** Whether to set the hover style */ + setShouldDisableHoverStyle?: React.Dispatch>; }; /** @@ -173,12 +179,6 @@ type SelectionListProps = Partial & /** Whether to show the default right hand side checkmark */ shouldUseDefaultRightHandSideCheckmark?: boolean; - - /** Whether hover style should be disabled */ - shouldDisableHoverStyle?: boolean; - - /** Whether to set the hover style */ - setShouldDisableHoverStyle?: React.Dispatch>; }; type SelectionListStyle = { diff --git a/src/components/SelectionListWithSections/BaseListItem.tsx b/src/components/SelectionListWithSections/BaseListItem.tsx index cdebcc04ca16..db7dbba99818 100644 --- a/src/components/SelectionListWithSections/BaseListItem.tsx +++ b/src/components/SelectionListWithSections/BaseListItem.tsx @@ -45,7 +45,6 @@ function BaseListItem({ shouldShowRightCaret = false, shouldHighlightSelectedItem = true, shouldDisableHoverStyle, - shouldStopMouseLeavePropagation = true, accessibilityRole = getButtonRole(true), }: BaseListItemProps) { const icons = useMemoizedLazyExpensifyIcons(['ArrowRight', 'Checkmark', 'DotIndicator'] as const); @@ -62,9 +61,7 @@ function BaseListItem({ useSyncFocus(pressableRef, !!isFocused, shouldSyncFocus); const handleMouseLeave = (e: React.MouseEvent) => { bind.onMouseLeave(); - if (shouldStopMouseLeavePropagation) { - e.stopPropagation(); - } + e.stopPropagation(); setMouseUp(); }; diff --git a/src/components/SelectionListWithSections/BaseSelectionListItemRenderer.tsx b/src/components/SelectionListWithSections/BaseSelectionListItemRenderer.tsx index 8e374b0a08c8..d1d12a84852a 100644 --- a/src/components/SelectionListWithSections/BaseSelectionListItemRenderer.tsx +++ b/src/components/SelectionListWithSections/BaseSelectionListItemRenderer.tsx @@ -60,7 +60,6 @@ function BaseSelectionListItemRenderer({ shouldShowRightCaret, shouldHighlightSelectedItem = true, shouldDisableHoverStyle, - shouldStopMouseLeavePropagation, }: BaseSelectionListItemRendererProps) { const handleOnCheckboxPress = () => { if (isTransactionGroupListItemType(item)) { @@ -121,7 +120,6 @@ function BaseSelectionListItemRenderer({ shouldHighlightSelectedItem={shouldHighlightSelectedItem} sectionIndex={sectionIndex} shouldDisableHoverStyle={shouldDisableHoverStyle} - shouldStopMouseLeavePropagation={shouldStopMouseLeavePropagation} /> {item.footerContent && item.footerContent} diff --git a/src/components/SelectionListWithSections/BaseSelectionListWithSections.tsx b/src/components/SelectionListWithSections/BaseSelectionListWithSections.tsx index 4e5bc9aeebe6..1ac942be57d8 100644 --- a/src/components/SelectionListWithSections/BaseSelectionListWithSections.tsx +++ b/src/components/SelectionListWithSections/BaseSelectionListWithSections.tsx @@ -448,7 +448,7 @@ function BaseSelectionListWithSections({ }, [setShouldDisableHoverStyle]); // If `initiallyFocusedOptionKey` is not passed, we fall back to `-1`, to avoid showing the highlight on the first member - const [focusedIndex, setFocusedIndex, currentHoverIndexRef] = useArrowKeyFocusManager({ + const [focusedIndex, setFocusedIndex] = useArrowKeyFocusManager({ initialFocusedIndex: flattenedSections.allOptions.findIndex((option) => option.keyForList === initiallyFocusedOptionKey), maxIndex: Math.min(flattenedSections.allOptions.length - 1, CONST.MAX_SELECTION_LIST_PAGE_LENGTH * currentPage - 1), disabledIndexes: disabledArrowKeyIndexes, @@ -665,16 +665,6 @@ function BaseSelectionListWithSections({ ); - const setCurrentHoverIndex = useCallback( - (hoverIndex: number | null) => { - if (shouldDisableHoverStyle) { - return; - } - currentHoverIndexRef.current = hoverIndex; - }, - [currentHoverIndexRef, shouldDisableHoverStyle], - ); - const renderItem = ({item, index, section}: SectionListRenderItemInfo>) => { const normalizedIndex = index + (section?.indexOffset ?? 0); const isDisabled = !!section.isDisabled || item.isDisabled; @@ -683,15 +673,7 @@ function BaseSelectionListWithSections({ const isItemHighlighted = !!itemsToHighlight?.has(item.keyForList ?? ''); return ( - onItemLayout(event, item?.keyForList)} - onMouseMove={() => setCurrentHoverIndex(normalizedIndex)} - onMouseEnter={() => setCurrentHoverIndex(normalizedIndex)} - onMouseLeave={(e) => { - e.stopPropagation(); - setCurrentHoverIndex(null); - }} - > + onItemLayout(event, item?.keyForList)}> ({ canShowProductTrainingTooltip={canShowProductTrainingTooltipMemo} shouldShowRightCaret={shouldShowRightCaret} shouldDisableHoverStyle={shouldDisableHoverStyle} - shouldStopMouseLeavePropagation={false} /> ); diff --git a/src/components/SelectionListWithSections/RadioListItem.tsx b/src/components/SelectionListWithSections/RadioListItem.tsx index 021c45e4e5de..fca9c5add93d 100644 --- a/src/components/SelectionListWithSections/RadioListItem.tsx +++ b/src/components/SelectionListWithSections/RadioListItem.tsx @@ -25,7 +25,6 @@ function RadioListItem({ titleStyles, shouldHighlightSelectedItem = true, shouldDisableHoverStyle, - shouldStopMouseLeavePropagation, accessibilityRole, }: RadioListItemProps) { const styles = useThemeStyles(); @@ -51,7 +50,6 @@ function RadioListItem({ pendingAction={item.pendingAction} shouldHighlightSelectedItem={shouldHighlightSelectedItem} shouldDisableHoverStyle={shouldDisableHoverStyle} - shouldStopMouseLeavePropagation={shouldStopMouseLeavePropagation} accessibilityRole={accessibilityRole} > <> diff --git a/src/components/SelectionListWithSections/Search/SearchQueryListItem.tsx b/src/components/SelectionListWithSections/Search/SearchQueryListItem.tsx index 17e213aa871f..bc79f5c581af 100644 --- a/src/components/SelectionListWithSections/Search/SearchQueryListItem.tsx +++ b/src/components/SelectionListWithSections/Search/SearchQueryListItem.tsx @@ -27,13 +27,14 @@ type SearchQueryListItemProps = { onSelectRow: (item: SearchQueryItem) => void; onFocus?: ListItemFocusEventHandler; shouldSyncFocus?: boolean; + shouldDisableHoverStyle?: boolean; }; function isSearchQueryItem(item: OptionData | SearchQueryItem): item is SearchQueryItem { return 'searchItemType' in item; } -function SearchQueryListItem({item, isFocused, showTooltip, onSelectRow, onFocus, shouldSyncFocus}: SearchQueryListItemProps) { +function SearchQueryListItem({item, isFocused, showTooltip, onSelectRow, onFocus, shouldSyncFocus, shouldDisableHoverStyle}: SearchQueryListItemProps) { const styles = useThemeStyles(); const theme = useTheme(); @@ -49,6 +50,7 @@ function SearchQueryListItem({item, isFocused, showTooltip, onSelectRow, onFocus hoverStyle={item.isSelected && styles.activeComponentBG} shouldSyncFocus={shouldSyncFocus} showTooltip={showTooltip} + shouldDisableHoverStyle={shouldDisableHoverStyle} > <> {!!item.singleIcon && ( diff --git a/src/components/SelectionListWithSections/UserListItem.tsx b/src/components/SelectionListWithSections/UserListItem.tsx index f0a0b1034d43..4562b9625bc8 100644 --- a/src/components/SelectionListWithSections/UserListItem.tsx +++ b/src/components/SelectionListWithSections/UserListItem.tsx @@ -37,6 +37,7 @@ function UserListItem({ pressableStyle, shouldUseDefaultRightHandSideCheckmark, forwardedFSClass, + shouldDisableHoverStyle, }: UserListItemProps) { const icons = useMemoizedLazyExpensifyIcons(['ArrowRight', 'Checkmark'] as const); const styles = useThemeStyles(); @@ -92,102 +93,107 @@ function UserListItem({ keyForList={item.keyForList} onFocus={onFocus} shouldSyncFocus={shouldSyncFocus} + shouldDisableHoverStyle={shouldDisableHoverStyle} > - {(hovered?: boolean) => ( - <> - {!shouldUseDefaultRightHandSideCheckmark && !!canSelectMultiple && ( - - - {!!item.isSelected && ( - - )} - - - )} - {(!!reportExists || !!itemAccountID || !!policyID) && ( - - )} - - - {!!item.alternateText && ( - { + const isHovered = !!hovered && !shouldDisableHoverStyle; + + return ( + <> + {!shouldUseDefaultRightHandSideCheckmark && !!canSelectMultiple && ( + + + {!!item.isSelected && ( + + )} + + + )} + {(!!reportExists || !!itemAccountID || !!policyID) && ( + )} - - {!!item.rightElement && item.rightElement} - {!!item.shouldShowRightIcon && ( - - + + {!!item.alternateText && ( + + )} - )} - {!!shouldUseDefaultRightHandSideCheckmark && !!canSelectMultiple && ( - - - {!!item.isSelected && ( - - )} + {!!item.rightElement && item.rightElement} + {!!item.shouldShowRightIcon && ( + + - - )} - - )} + )} + {!!shouldUseDefaultRightHandSideCheckmark && !!canSelectMultiple && ( + + + {!!item.isSelected && ( + + )} + + + )} + + ); + }} ); } diff --git a/src/components/SelectionListWithSections/types.ts b/src/components/SelectionListWithSections/types.ts index 5dd2f9735037..4a425361cef6 100644 --- a/src/components/SelectionListWithSections/types.ts +++ b/src/components/SelectionListWithSections/types.ts @@ -122,9 +122,6 @@ type CommonListItemProps = { /** Whether to disable the hover style of the item */ shouldDisableHoverStyle?: boolean; - /** Whether to call stopPropagation on the mouseleave event in BaseListItem */ - shouldStopMouseLeavePropagation?: boolean; - /** Accessibility role for the list item (e.g. 'checkbox' for multi-select options so screen readers announce checked state) */ accessibilityRole?: Role; } & TRightHandSideComponent; diff --git a/src/hooks/useArrowKeyFocusManager.ts b/src/hooks/useArrowKeyFocusManager.ts index e34de627b3e7..c9a7a1755003 100644 --- a/src/hooks/useArrowKeyFocusManager.ts +++ b/src/hooks/useArrowKeyFocusManager.ts @@ -1,5 +1,4 @@ -import {useCallback, useEffect, useMemo, useRef, useState} from 'react'; -import type {RefObject} from 'react'; +import {useCallback, useEffect, useMemo, useState} from 'react'; import CONST from '@src/CONST'; import useKeyboardShortcut from './useKeyboardShortcut'; import usePrevious from './usePrevious'; @@ -21,7 +20,7 @@ type Config = { onArrowUpDownCallback?: () => void; }; -type UseArrowKeyFocusManager = [number, (index: number) => void, RefObject]; +type UseArrowKeyFocusManager = [number, (index: number) => void]; /** * A hook that makes it easy to use the arrow keys to manage focus of items in a list @@ -60,7 +59,6 @@ export default function useArrowKeyFocusManager({ }: Config): UseArrowKeyFocusManager { const [focusedIndex, setFocusedIndex] = useState(initialFocusedIndex); const prevIsFocusedIndex = usePrevious(focusedIndex); - const currentHoverIndexRef = useRef(null); const arrowConfig = useMemo( () => ({ excludedNodes: shouldExcludeTextAreaNodes ? ['TEXTAREA'] : [], @@ -94,7 +92,7 @@ export default function useArrowKeyFocusManager({ const nextIndex = disableCyclicTraversal ? -1 : maxIndex; setHasKeyBeenPressed?.(); setFocusedIndex((actualIndexParam) => { - const actualIndex = currentHoverIndexRef.current ?? actualIndexParam; + const actualIndex = actualIndexParam; const currentFocusedIndex = actualIndex > 0 ? actualIndex - (itemsPerRow ?? 1) : nextIndex; let newFocusedIndex = currentFocusedIndex; @@ -116,8 +114,6 @@ export default function useArrowKeyFocusManager({ } return newFocusedIndex; }); - - currentHoverIndexRef.current = null; onArrowUpDownCallback(); }, [maxIndex, isFocused, disableCyclicTraversal, itemsPerRow, disabledIndexes, allowNegativeIndexes, setHasKeyBeenPressed, onArrowUpDownCallback]); @@ -133,7 +129,7 @@ export default function useArrowKeyFocusManager({ setFocusedIndex((actualIndexParam) => { let currentFocusedIndex = -1; - const actualIndex = currentHoverIndexRef.current ?? actualIndexParam; + const actualIndex = actualIndexParam; if (actualIndex === -1) { currentFocusedIndex = 0; @@ -166,8 +162,6 @@ export default function useArrowKeyFocusManager({ } return newFocusedIndex; }); - - currentHoverIndexRef.current = null; onArrowUpDownCallback(); }, [disableCyclicTraversal, disabledIndexes, isFocused, itemsPerRow, maxIndex, setHasKeyBeenPressed, onArrowUpDownCallback]); @@ -226,5 +220,5 @@ export default function useArrowKeyFocusManager({ useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.ARROW_RIGHT, arrowRightCallback, horizontalArrowConfig); // Note: you don't need to manually manage focusedIndex in the parent. setFocusedIndex is only exposed in case you want to reset focusedIndex or focus a specific item - return [focusedIndex, setFocusedIndex, currentHoverIndexRef]; + return [focusedIndex, setFocusedIndex]; } diff --git a/tests/unit/BaseSelectionListSectionsTest.tsx b/tests/unit/BaseSelectionListSectionsTest.tsx index 58b11fd7e53a..88e1bf901b35 100644 --- a/tests/unit/BaseSelectionListSectionsTest.tsx +++ b/tests/unit/BaseSelectionListSectionsTest.tsx @@ -283,7 +283,7 @@ describe('BaseSelectionList', () => { expect(screen.getByTestId(`${CONST.BASE_LIST_ITEM_TEST_ID}3`)).toBeTruthy(); }); - it('should focus next/previous item relative to hovered item when arrow keys are pressed', async () => { + it('should focus next/previous item relative to focused item when arrow keys are pressed', async () => { render( { expect(screen.getByTestId(`${CONST.BASE_LIST_ITEM_TEST_ID}1`)).toHaveStyle({backgroundColor: colors.productDark400}); - // Trigger a mouse move event to hover the item - fireEvent(screen.getByTestId(`${CONST.BASE_LIST_ITEM_TEST_ID}10`), 'mouseMove', {stopPropagation: () => {}}); - // eslint-disable-next-line testing-library/no-unnecessary-act act(() => { arrowDownCallback(); }); - // The item that gets focused will be the one following the hovered item + // The item that gets focused will be the one following the currently focused item await waitFor(() => { - expect(screen.getByTestId(`${CONST.BASE_LIST_ITEM_TEST_ID}11`)).toHaveStyle({backgroundColor: colors.productDark300}); + expect(screen.getByTestId(`${CONST.BASE_LIST_ITEM_TEST_ID}2`)).toHaveStyle({backgroundColor: colors.productDark300}); }); act(() => { @@ -313,7 +310,7 @@ describe('BaseSelectionList', () => { }); await waitFor(() => { - expect(screen.getByTestId(`${CONST.BASE_LIST_ITEM_TEST_ID}9`)).toHaveStyle({backgroundColor: colors.productDark300}); + expect(screen.getByTestId(`${CONST.BASE_LIST_ITEM_TEST_ID}0`)).toHaveStyle({backgroundColor: colors.productDark300}); }); act(() => { @@ -321,7 +318,7 @@ describe('BaseSelectionList', () => { }); await waitFor(() => { - expect(screen.getByTestId(`${CONST.BASE_LIST_ITEM_TEST_ID}10`)).toHaveStyle({backgroundColor: colors.productDark300}); + expect(screen.getByTestId(`${CONST.BASE_LIST_ITEM_TEST_ID}1`)).toHaveStyle({backgroundColor: colors.productDark400}); }); }); }); diff --git a/tests/unit/BaseSelectionListTest.tsx b/tests/unit/BaseSelectionListTest.tsx index ee0306d5e18f..aa445b5092d8 100644 --- a/tests/unit/BaseSelectionListTest.tsx +++ b/tests/unit/BaseSelectionListTest.tsx @@ -46,22 +46,6 @@ jest.mock('@hooks/useKeyboardShortcut', () => (key: {shortcutKey: string}, callb } }); -let mockShouldStopMouseLeavePropagation = false; -jest.mock('@components/SelectionList/ListItem/BaseListItem', () => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access - const ActualBaseListItem = jest.requireActual('@components/SelectionList/ListItem/BaseListItem').default; - - return ((props) => ( - - {props.children} - - )) as typeof BaseListItem; -}); - describe('BaseSelectionList', () => { const onSelectRowMock = jest.fn(); @@ -82,7 +66,7 @@ describe('BaseSelectionList', () => { ); } - it('should focus next/previous item relative to hovered item when arrow keys are pressed', async () => { + it('should focus next/previous item relative to focused item when arrow keys are pressed', async () => { render( { expect(screen.getByTestId(`${CONST.BASE_LIST_ITEM_TEST_ID}0`)).toHaveStyle({backgroundColor: colors.productDark400}); - // Trigger a mouse move event to hover the item - fireEvent(screen.getByTestId(`${CONST.BASE_LIST_ITEM_TEST_ID}8`), 'mouseMove', {stopPropagation: () => {}}); - // eslint-disable-next-line testing-library/no-unnecessary-act act(() => { arrowDownCallback(); }); - // The item that gets focused will be the one following the hovered item + // The item that gets focused will be the one following the currently focused item await waitFor(() => { - expect(screen.getByTestId(`${CONST.BASE_LIST_ITEM_TEST_ID}9`)).toHaveStyle({backgroundColor: colors.productDark300}); + expect(screen.getByTestId(`${CONST.BASE_LIST_ITEM_TEST_ID}1`)).toHaveStyle({backgroundColor: colors.productDark300}); }); act(() => { @@ -111,7 +92,7 @@ describe('BaseSelectionList', () => { }); await waitFor(() => { - expect(screen.getByTestId(`${CONST.BASE_LIST_ITEM_TEST_ID}7`)).toHaveStyle({backgroundColor: colors.productDark300}); + expect(screen.getByTestId(`${CONST.BASE_LIST_ITEM_TEST_ID}9`)).toHaveStyle({backgroundColor: colors.productDark300}); }); act(() => { @@ -119,37 +100,10 @@ describe('BaseSelectionList', () => { }); await waitFor(() => { - expect(screen.getByTestId(`${CONST.BASE_LIST_ITEM_TEST_ID}8`)).toHaveStyle({backgroundColor: colors.productDark300}); + expect(screen.getByTestId(`${CONST.BASE_LIST_ITEM_TEST_ID}0`)).toHaveStyle({backgroundColor: colors.productDark400}); }); }); - it("the stopPropagation from the BaseListItem's mouseLeave event does not trigger if shouldStopMouseLeavePropagation === false", () => { - mockShouldStopMouseLeavePropagation = false; - render( - , - ); - - const mockStopPropagation = jest.fn(); - fireEvent(screen.getByTestId(`${CONST.BASE_LIST_ITEM_TEST_ID}3`), 'mouseLeave', {stopPropagation: mockStopPropagation}); - - expect(mockStopPropagation).toHaveBeenCalledTimes(0); - - mockShouldStopMouseLeavePropagation = true; - render( - , - ); - - fireEvent(screen.getByTestId(`${CONST.BASE_LIST_ITEM_TEST_ID}3`), 'mouseLeave', {stopPropagation: mockStopPropagation}); - - expect(mockStopPropagation).toHaveBeenCalledTimes(1); - }); - it('should not call preventDefault on mouseDown when the target is an INPUT element', () => { render( diff --git a/tests/unit/SuggestionMentionTest.tsx b/tests/unit/SuggestionMentionTest.tsx index d19981e6e92e..c00f294ace54 100644 --- a/tests/unit/SuggestionMentionTest.tsx +++ b/tests/unit/SuggestionMentionTest.tsx @@ -113,7 +113,7 @@ describe('SuggestionMention', () => { mockPersonalDetails = {}; mockUsePersonalDetails.mockImplementation(() => mockPersonalDetails); - mockUseArrowKeyFocusManager.mockReturnValue([0, mockSetHighlightedMentionIndex, {current: null}]); + mockUseArrowKeyFocusManager.mockReturnValue([0, mockSetHighlightedMentionIndex]); mockUseCurrentReportIDState.mockReturnValue({currentReportID: ''}); mockUseCurrentUserPersonalDetails.mockReturnValue({accountID: 1, login: 'current@gmail.com'}); mockUseDebounce.mockImplementation((callback) => {