diff --git a/src/components/ArrowKeyFocusManager.js b/src/components/ArrowKeyFocusManager.js
index 19dc3a7ac614..2532e52156df 100644
--- a/src/components/ArrowKeyFocusManager.js
+++ b/src/components/ArrowKeyFocusManager.js
@@ -1,5 +1,6 @@
+import {useIsFocused} from '@react-navigation/native';
import PropTypes from 'prop-types';
-import {Component} from 'react';
+import React, {Component} from 'react';
import KeyboardShortcut from '@libs/KeyboardShortcut';
import CONST from '@src/CONST';
@@ -16,6 +17,9 @@ const propTypes = {
/** The maximum index – provided so that the focus can be sent back to the beginning of the list when the end is reached. */
maxIndex: PropTypes.number.isRequired,
+ /** Whether navigation is focused */
+ isFocused: PropTypes.bool.isRequired,
+
/** A callback executed when the focused input changes. */
onFocusedIndexChanged: PropTypes.func.isRequired,
@@ -32,7 +36,7 @@ const defaultProps = {
shouldResetIndexOnEndReached: true,
};
-class ArrowKeyFocusManager extends Component {
+class BaseArrowKeyFocusManager extends Component {
componentDidMount() {
const arrowUpConfig = CONST.KEYBOARD_SHORTCUTS.ARROW_UP;
const arrowDownConfig = CONST.KEYBOARD_SHORTCUTS.ARROW_DOWN;
@@ -77,7 +81,7 @@ class ArrowKeyFocusManager extends Component {
}
onArrowUpKey() {
- if (this.props.maxIndex < 0) {
+ if (this.props.maxIndex < 0 || !this.props.isFocused) {
return;
}
@@ -96,7 +100,7 @@ class ArrowKeyFocusManager extends Component {
}
onArrowDownKey() {
- if (this.props.maxIndex < 0) {
+ if (this.props.maxIndex < 0 || !this.props.isFocused) {
return;
}
@@ -119,7 +123,20 @@ class ArrowKeyFocusManager extends Component {
}
}
-ArrowKeyFocusManager.propTypes = propTypes;
-ArrowKeyFocusManager.defaultProps = defaultProps;
+function ArrowKeyFocusManager(props) {
+ const isFocused = useIsFocused();
+
+ return (
+
+ );
+}
+
+BaseArrowKeyFocusManager.propTypes = propTypes;
+BaseArrowKeyFocusManager.defaultProps = defaultProps;
+ArrowKeyFocusManager.displayName = 'ArrowKeyFocusManager';
export default ArrowKeyFocusManager;
diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
index 5b246caa6e07..553a712f45c4 100644
--- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
+++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
@@ -24,6 +24,7 @@ import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import {deleteWorkspaceCategories, setWorkspaceCategoryEnabled} from '@libs/actions/Policy';
+import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import localeCompare from '@libs/LocaleCompare';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
@@ -318,6 +319,7 @@ function WorkspaceCategoriesPage({policy, policyCategories, route}: WorkspaceCat
sections={[{data: categoryList, isDisabled: false}]}
onCheckboxPress={toggleCategory}
onSelectRow={navigateToCategorySettings}
+ shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()}
onSelectAll={toggleAllCategories}
showScrollIndicator
ListItem={TableListItem}
diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx
index faa766945452..04b11b31d3da 100644
--- a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx
+++ b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx
@@ -22,6 +22,7 @@ import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import * as CurrencyUtils from '@libs/CurrencyUtils';
+import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import Navigation from '@libs/Navigation/Navigation';
import type {WorkspacesCentralPaneNavigatorParamList} from '@navigation/types';
import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper';
@@ -310,6 +311,7 @@ function PolicyDistanceRatesPage({policy, route}: PolicyDistanceRatesPageProps)
onDismissError={dismissError}
showScrollIndicator
ListItem={TableListItem}
+ shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()}
customListHeader={getCustomListHeader()}
listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
/>
diff --git a/src/pages/workspace/tags/WorkspaceTagsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsPage.tsx
index 56cf00582782..2e2d3a483873 100644
--- a/src/pages/workspace/tags/WorkspaceTagsPage.tsx
+++ b/src/pages/workspace/tags/WorkspaceTagsPage.tsx
@@ -24,6 +24,7 @@ import useNetwork from '@hooks/useNetwork';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
+import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import localeCompare from '@libs/LocaleCompare';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
@@ -317,6 +318,7 @@ function WorkspaceTagsPage({policyTags, route}: WorkspaceTagsPageProps) {
showScrollIndicator
ListItem={TableListItem}
customListHeader={getCustomListHeader()}
+ shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()}
listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
onDismissError={(item) => Policy.clearPolicyTagErrors(route.params.policyID, item.value)}
/>
diff --git a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx
index 8ce33ec783c0..5540e09693eb 100644
--- a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx
+++ b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx
@@ -22,6 +22,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import {openPolicyTaxesPage} from '@libs/actions/Policy';
import {clearTaxRateError, deletePolicyTaxes, setPolicyTaxesEnabled} from '@libs/actions/TaxRate';
+import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as PolicyUtils from '@libs/PolicyUtils';
@@ -278,6 +279,7 @@ function WorkspaceTaxesPage({
showScrollIndicator
ListItem={TableListItem}
customListHeader={getCustomListHeader()}
+ shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()}
listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
onDismissError={(item) => (item.keyForList ? clearTaxRateError(policyID, item.keyForList, item.pendingAction) : undefined)}
/>
diff --git a/tests/perf-test/OptionsSelector.perf-test.tsx b/tests/perf-test/OptionsSelector.perf-test.tsx
index 44dc4ac6c317..fe234dda1e19 100644
--- a/tests/perf-test/OptionsSelector.perf-test.tsx
+++ b/tests/perf-test/OptionsSelector.perf-test.tsx
@@ -5,6 +5,7 @@ import type {ComponentType} from 'react';
import {measurePerformance} from 'reassure';
import type {WithLocalizeProps} from '@components/withLocalize';
import type {WithNavigationFocusProps} from '@components/withNavigationFocus';
+import type Navigation from '@libs/Navigation/Navigation';
import OptionsSelector from '@src/components/OptionsSelector';
import variables from '@src/styles/variables';
@@ -38,6 +39,18 @@ jest.mock('@src/components/withNavigationFocus', () => (Component: ComponentType
return WithNavigationFocus;
});
+jest.mock('@react-navigation/native', () => {
+ const actualNav = jest.requireActual('@react-navigation/native');
+ return {
+ ...actualNav,
+ useNavigation: () => ({
+ navigate: jest.fn(),
+ addListener: () => jest.fn(),
+ }),
+ useIsFocused: () => true,
+ } as typeof Navigation;
+});
+
type GenerateSectionsProps = Array<{numberOfItems: number; shouldShow?: boolean}>;
const generateSections = (sections: GenerateSectionsProps) =>
diff --git a/tests/perf-test/ReportActionCompose.perf-test.tsx b/tests/perf-test/ReportActionCompose.perf-test.tsx
index 8d6cb1ac7e57..91e4f51f1c66 100644
--- a/tests/perf-test/ReportActionCompose.perf-test.tsx
+++ b/tests/perf-test/ReportActionCompose.perf-test.tsx
@@ -38,9 +38,7 @@ jest.mock('@react-navigation/native', () => {
navigate: jest.fn(),
addListener: () => jest.fn(),
}),
- useIsFocused: () => ({
- navigate: jest.fn(),
- }),
+ useIsFocused: () => true,
} as typeof Navigation;
});
diff --git a/tests/perf-test/ReportScreen.perf-test.tsx b/tests/perf-test/ReportScreen.perf-test.tsx
index ff3d1473c662..b55f4b9ccb93 100644
--- a/tests/perf-test/ReportScreen.perf-test.tsx
+++ b/tests/perf-test/ReportScreen.perf-test.tsx
@@ -81,15 +81,12 @@ jest.mock('@src/hooks/usePermissions.ts');
jest.mock('@src/libs/Navigation/Navigation');
-const mockedNavigate = jest.fn();
jest.mock('@react-navigation/native', () => {
const actualNav = jest.requireActual('@react-navigation/native');
return {
...actualNav,
useFocusEffect: jest.fn(),
- useIsFocused: () => ({
- navigate: mockedNavigate,
- }),
+ useIsFocused: () => true,
useRoute: () => jest.fn(),
useNavigation: () => ({
navigate: jest.fn(),
diff --git a/tests/perf-test/SearchPage.perf-test.tsx b/tests/perf-test/SearchPage.perf-test.tsx
index 95f5630e3fe9..b7cdee0d7417 100644
--- a/tests/perf-test/SearchPage.perf-test.tsx
+++ b/tests/perf-test/SearchPage.perf-test.tsx
@@ -45,15 +45,12 @@ jest.mock('@src/libs/API', () => ({
jest.mock('@src/libs/Navigation/Navigation');
-const mockedNavigate = jest.fn();
jest.mock('@react-navigation/native', () => {
const actualNav = jest.requireActual('@react-navigation/native');
return {
...actualNav,
useFocusEffect: jest.fn(),
- useIsFocused: () => ({
- navigate: mockedNavigate,
- }),
+ useIsFocused: () => true,
useRoute: () => jest.fn(),
useNavigation: () => ({
navigate: jest.fn(),
diff --git a/tests/perf-test/SignInPage.perf-test.tsx b/tests/perf-test/SignInPage.perf-test.tsx
index e3e2c20ae72a..dc93b0d81059 100644
--- a/tests/perf-test/SignInPage.perf-test.tsx
+++ b/tests/perf-test/SignInPage.perf-test.tsx
@@ -26,15 +26,12 @@ jest.mock('../../src/libs/API', () => ({
read: jest.fn(),
}));
-const mockedNavigate = jest.fn();
jest.mock('@react-navigation/native', () => {
const actualNav = jest.requireActual('@react-navigation/native');
return {
...actualNav,
useFocusEffect: jest.fn(),
- useIsFocused: () => ({
- navigate: mockedNavigate,
- }),
+ useIsFocused: () => true,
useRoute: () => jest.fn(),
useNavigation: () => ({
navigate: jest.fn(),
diff --git a/tests/utils/LHNTestUtils.tsx b/tests/utils/LHNTestUtils.tsx
index f6eee590313b..58c765b5c98e 100644
--- a/tests/utils/LHNTestUtils.tsx
+++ b/tests/utils/LHNTestUtils.tsx
@@ -1,7 +1,5 @@
/* eslint-disable @typescript-eslint/naming-convention */
-import type {NavigationProp} from '@react-navigation/core/src/types';
import type * as Navigation from '@react-navigation/native';
-import type {ParamListBase} from '@react-navigation/routers';
import {render} from '@testing-library/react-native';
import type {ReactElement} from 'react';
import React from 'react';
@@ -33,17 +31,13 @@ type MockedSidebarLinksProps = {
currentReportID?: string;
};
-// we have to mock `useIsFocused` because it's used in the SidebarLinks component
-const mockedNavigate: jest.MockedFn['navigate']> = jest.fn();
jest.mock('@react-navigation/native', (): typeof Navigation => {
const actualNav = jest.requireActual('@react-navigation/native');
return {
...actualNav,
useRoute: jest.fn(),
useFocusEffect: jest.fn(),
- useIsFocused: () => ({
- navigate: mockedNavigate,
- }),
+ useIsFocused: () => true,
useNavigation: () => ({
navigate: jest.fn(),
addListener: jest.fn(),