Skip to content

Commit

Permalink
Merge pull request #39327 from ZhenjaHorbach/user-can-access-the-back…
Browse files Browse the repository at this point in the history
…ground-list-using-the-arrow-keys-while-the-RHP-is-open

User can access the background list using the arrow keys while the RHP is open
  • Loading branch information
pecanoro authored Apr 5, 2024
2 parents a01b338 + 2f1cede commit 200de35
Show file tree
Hide file tree
Showing 11 changed files with 49 additions and 28 deletions.
29 changes: 23 additions & 6 deletions src/components/ArrowKeyFocusManager.js
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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,

Expand All @@ -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;
Expand Down Expand Up @@ -77,7 +81,7 @@ class ArrowKeyFocusManager extends Component {
}

onArrowUpKey() {
if (this.props.maxIndex < 0) {
if (this.props.maxIndex < 0 || !this.props.isFocused) {
return;
}

Expand All @@ -96,7 +100,7 @@ class ArrowKeyFocusManager extends Component {
}

onArrowDownKey() {
if (this.props.maxIndex < 0) {
if (this.props.maxIndex < 0 || !this.props.isFocused) {
return;
}

Expand All @@ -119,7 +123,20 @@ class ArrowKeyFocusManager extends Component {
}
}

ArrowKeyFocusManager.propTypes = propTypes;
ArrowKeyFocusManager.defaultProps = defaultProps;
function ArrowKeyFocusManager(props) {
const isFocused = useIsFocused();

return (
<BaseArrowKeyFocusManager
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
isFocused={isFocused}
/>
);
}

BaseArrowKeyFocusManager.propTypes = propTypes;
BaseArrowKeyFocusManager.defaultProps = defaultProps;
ArrowKeyFocusManager.displayName = 'ArrowKeyFocusManager';

export default ArrowKeyFocusManager;
2 changes: 2 additions & 0 deletions src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -294,6 +295,7 @@ function WorkspaceCategoriesPage({policy, policyCategories, route}: WorkspaceCat
sections={[{data: categoryList, isDisabled: false}]}
onCheckboxPress={toggleCategory}
onSelectRow={navigateToCategorySettings}
shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()}
onSelectAll={toggleAllCategories}
showScrollIndicator
ListItem={TableListItem}
Expand Down
2 changes: 2 additions & 0 deletions src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -298,6 +299,7 @@ function PolicyDistanceRatesPage({policy, route}: PolicyDistanceRatesPageProps)
onDismissError={dismissError}
showScrollIndicator
ListItem={TableListItem}
shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()}
customListHeader={getCustomListHeader()}
listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
/>
Expand Down
2 changes: 2 additions & 0 deletions src/pages/workspace/tags/WorkspaceTagsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -305,6 +306,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)}
/>
Expand Down
2 changes: 2 additions & 0 deletions src/pages/workspace/taxes/WorkspaceTaxesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -268,6 +269,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)}
/>
Expand Down
13 changes: 13 additions & 0 deletions tests/perf-test/OptionsSelector.perf-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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) =>
Expand Down
4 changes: 1 addition & 3 deletions tests/perf-test/ReportActionCompose.perf-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ jest.mock('@react-navigation/native', () => {
navigate: jest.fn(),
addListener: () => jest.fn(),
}),
useIsFocused: () => ({
navigate: jest.fn(),
}),
useIsFocused: () => true,
} as typeof Navigation;
});

Expand Down
5 changes: 1 addition & 4 deletions tests/perf-test/ReportScreen.perf-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
5 changes: 1 addition & 4 deletions tests/perf-test/SearchPage.perf-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
5 changes: 1 addition & 4 deletions tests/perf-test/SignInPage.perf-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
8 changes: 1 addition & 7 deletions tests/utils/LHNTestUtils.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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<NavigationProp<ParamListBase>['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(),
Expand Down

0 comments on commit 200de35

Please sign in to comment.