diff --git a/android/app/build.gradle b/android/app/build.gradle
index aaa6aaeb7a78..63aa4215cd90 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -96,8 +96,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1001042200
- versionName "1.4.22-0"
+ versionCode 1001042203
+ versionName "1.4.22-3"
}
flavorDimensions "default"
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index ecf3b8c9cad9..f58687c66c63 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -40,7 +40,7 @@
CFBundleVersion
- 1.4.22.0
+ 1.4.22.3
ITSAppUsesNonExemptEncryption
LSApplicationQueriesSchemes
diff --git a/ios/NewExpensifyTests/Info.plist b/ios/NewExpensifyTests/Info.plist
index 1ec1aeb6ce14..b7b8c9d3416b 100644
--- a/ios/NewExpensifyTests/Info.plist
+++ b/ios/NewExpensifyTests/Info.plist
@@ -19,6 +19,6 @@
CFBundleSignature
????
CFBundleVersion
- 1.4.22.0
+ 1.4.22.3
diff --git a/package-lock.json b/package-lock.json
index 2c1da1670e19..55bfafbec2f2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "new.expensify",
- "version": "1.4.22-0",
+ "version": "1.4.22-3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "1.4.22-0",
+ "version": "1.4.22-3",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
diff --git a/package.json b/package.json
index 118f63e45879..7264cb5fa25e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "1.4.22-0",
+ "version": "1.4.22-3",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
diff --git a/src/CONST.ts b/src/CONST.ts
index 2fd592f539c2..6e9118486a7a 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -1365,6 +1365,7 @@ const CONST = {
DIGITS_AND_PLUS: /^\+?[0-9]*$/,
ALPHABETIC_AND_LATIN_CHARS: /^[\p{Script=Latin} ]*$/u,
NON_ALPHABETIC_AND_NON_LATIN_CHARS: /[^\p{Script=Latin}]/gu,
+ ACCENT_LATIN_CHARS: /[\u00C0-\u017F]/g,
POSITIVE_INTEGER: /^\d+$/,
PO_BOX: /\b[P|p]?(OST|ost)?\.?\s*[O|o|0]?(ffice|FFICE)?\.?\s*[B|b][O|o|0]?[X|x]?\.?\s+[#]?(\d+)\b/,
ANY_VALUE: /^.+$/,
@@ -1422,6 +1423,7 @@ const CONST = {
ROUTES: {
VALIDATE_LOGIN: /\/v($|(\/\/*))/,
UNLINK_LOGIN: /\/u($|(\/\/*))/,
+ REDUNDANT_SLASHES: /(\/{2,})|(\/$)/g,
},
TIME_STARTS_01: /^01:\d{2} [AP]M$/,
@@ -2954,6 +2956,7 @@ const CONST = {
MAPBOX: {
PADDING: 50,
DEFAULT_ZOOM: 10,
+ SINGLE_MARKER_ZOOM: 15,
DEFAULT_COORDINATE: [-122.4021, 37.7911],
STYLE_URL: 'mapbox://styles/expensify/cllcoiqds00cs01r80kp34tmq',
},
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index b6e62814466b..e8a860582bb1 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -314,13 +314,13 @@ const ROUTES = {
getRoute: (iouType: ValueOf, transactionID: string, reportID: string) => `create/${iouType}/start/${transactionID}/${reportID}` as const,
},
MONEY_REQUEST_STEP_CONFIRMATION: {
- route: 'create/:iouType/confirmation/:transactionID/:reportID/',
- getRoute: (iouType: ValueOf, transactionID: string, reportID: string) => `create/${iouType}/confirmation/${transactionID}/${reportID}/` as const,
+ route: 'create/:iouType/confirmation/:transactionID/:reportID',
+ getRoute: (iouType: ValueOf, transactionID: string, reportID: string) => `create/${iouType}/confirmation/${transactionID}/${reportID}` as const,
},
MONEY_REQUEST_STEP_AMOUNT: {
- route: 'create/:iouType/amount/:transactionID/:reportID/',
+ route: 'create/:iouType/amount/:transactionID/:reportID',
getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') =>
- getUrlWithBackToParam(`create/${iouType}/amount/${transactionID}/${reportID}/`, backTo),
+ getUrlWithBackToParam(`create/${iouType}/amount/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_TAX_RATE: {
route: 'create/:iouType/taxRate/:transactionID/:reportID?',
@@ -333,52 +333,52 @@ const ROUTES = {
getUrlWithBackToParam(`create/${iouType}/taxAmount/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_CATEGORY: {
- route: 'create/:iouType/category/:transactionID/:reportID/',
+ route: 'create/:iouType/category/:transactionID/:reportID',
getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') =>
- getUrlWithBackToParam(`create/${iouType}/category/${transactionID}/${reportID}/`, backTo),
+ getUrlWithBackToParam(`create/${iouType}/category/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_CURRENCY: {
- route: 'create/:iouType/currency/:transactionID/:reportID/:pageIndex?/',
+ route: 'create/:iouType/currency/:transactionID/:reportID/:pageIndex?',
getRoute: (iouType: ValueOf, transactionID: string, reportID: string, pageIndex = '', backTo = '') =>
getUrlWithBackToParam(`create/${iouType}/currency/${transactionID}/${reportID}/${pageIndex}`, backTo),
},
MONEY_REQUEST_STEP_DATE: {
- route: 'create/:iouType/date/:transactionID/:reportID/',
+ route: 'create/:iouType/date/:transactionID/:reportID',
getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') =>
- getUrlWithBackToParam(`create/${iouType}/date/${transactionID}/${reportID}/`, backTo),
+ getUrlWithBackToParam(`create/${iouType}/date/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_DESCRIPTION: {
- route: 'create/:iouType/description/:transactionID/:reportID/',
+ route: 'create/:iouType/description/:transactionID/:reportID',
getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') =>
- getUrlWithBackToParam(`create/${iouType}/description/${transactionID}/${reportID}/`, backTo),
+ getUrlWithBackToParam(`create/${iouType}/description/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_DISTANCE: {
- route: 'create/:iouType/distance/:transactionID/:reportID/',
+ route: 'create/:iouType/distance/:transactionID/:reportID',
getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') =>
- getUrlWithBackToParam(`create/${iouType}/distance/${transactionID}/${reportID}/`, backTo),
+ getUrlWithBackToParam(`create/${iouType}/distance/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_MERCHANT: {
- route: 'create/:iouType/merchant/:transactionID/:reportID/',
+ route: 'create/:iouType/merchant/:transactionID/:reportID',
getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') =>
- getUrlWithBackToParam(`create/${iouType}/merchant/${transactionID}/${reportID}/`, backTo),
+ getUrlWithBackToParam(`create/${iouType}/merchant/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_PARTICIPANTS: {
- route: 'create/:iouType/participants/:transactionID/:reportID/',
+ route: 'create/:iouType/participants/:transactionID/:reportID',
getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') =>
- getUrlWithBackToParam(`create/${iouType}/participants/${transactionID}/${reportID}/`, backTo),
+ getUrlWithBackToParam(`create/${iouType}/participants/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_SCAN: {
- route: 'create/:iouType/scan/:transactionID/:reportID/',
+ route: 'create/:iouType/scan/:transactionID/:reportID',
getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') =>
- getUrlWithBackToParam(`create/${iouType}/scan/${transactionID}/${reportID}/`, backTo),
+ getUrlWithBackToParam(`create/${iouType}/scan/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_TAG: {
- route: 'create/:iouType/tag/:transactionID/:reportID/',
+ route: 'create/:iouType/tag/:transactionID/:reportID',
getRoute: (iouType: ValueOf, transactionID: string, reportID: string, backTo = '') =>
- getUrlWithBackToParam(`create/${iouType}/tag/${transactionID}/${reportID}/`, backTo),
+ getUrlWithBackToParam(`create/${iouType}/tag/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_WAYPOINT: {
- route: 'create/:iouType/waypoint/:transactionID/:reportID/:pageIndex/',
+ route: 'create/:iouType/waypoint/:transactionID/:reportID/:pageIndex',
getRoute: (iouType: ValueOf, transactionID: string, reportID: string, pageIndex = '', backTo = '') =>
getUrlWithBackToParam(`create/${iouType}/waypoint/${transactionID}/${reportID}/${pageIndex}`, backTo),
},
diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js
index e5b605dd3ff2..1b4d350f7d4f 100755
--- a/src/components/AttachmentModal.js
+++ b/src/components/AttachmentModal.js
@@ -129,7 +129,7 @@ function AttachmentModal(props) {
const [isAuthTokenRequired, setIsAuthTokenRequired] = useState(props.isAuthTokenRequired);
const [attachmentInvalidReasonTitle, setAttachmentInvalidReasonTitle] = useState('');
const [attachmentInvalidReason, setAttachmentInvalidReason] = useState(null);
- const [source, setSource] = useState(props.source);
+ const [source, setSource] = useState(() => props.source);
const [modalType, setModalType] = useState(CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE);
const [isConfirmButtonDisabled, setIsConfirmButtonDisabled] = useState(false);
const [confirmButtonFadeAnimation] = useState(() => new Animated.Value(1));
@@ -362,7 +362,7 @@ function AttachmentModal(props) {
}, []);
useEffect(() => {
- setSource(props.source);
+ setSource(() => props.source);
}, [props.source]);
useEffect(() => {
diff --git a/src/components/BaseMiniContextMenuItem.tsx b/src/components/BaseMiniContextMenuItem.tsx
index 1f9a14cdfdee..7bed44cd8f13 100644
--- a/src/components/BaseMiniContextMenuItem.tsx
+++ b/src/components/BaseMiniContextMenuItem.tsx
@@ -8,6 +8,7 @@ import DomUtils from '@libs/DomUtils';
import getButtonState from '@libs/getButtonState';
import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager';
import variables from '@styles/variables';
+import CONST from '@src/CONST';
import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback';
import Tooltip from './Tooltip/PopoverAnchorTooltip';
@@ -66,6 +67,7 @@ function BaseMiniContextMenuItem({tooltipText, onPress, children, isDelayButtonS
event.preventDefault();
}}
accessibilityLabel={tooltipText}
+ role={CONST.ROLE.BUTTON}
style={({hovered, pressed}) => [
styles.reportActionContextMenuMiniButton,
StyleUtils.getButtonBackgroundColorStyle(getButtonState(hovered, pressed, isDelayButtonStateComplete)),
diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx
index dd0499d4d243..fb72f0cc845f 100644
--- a/src/components/Button/index.tsx
+++ b/src/components/Button/index.tsx
@@ -171,16 +171,16 @@ function Button(
const keyboardShortcutCallback = useCallback(
(event?: GestureResponderEvent | KeyboardEvent) => {
- if (!validateSubmitShortcut(isFocused, isDisabled, isLoading, event)) {
+ if (!validateSubmitShortcut(isDisabled, isLoading, event)) {
return;
}
onPress();
},
- [isDisabled, isFocused, isLoading, onPress],
+ [isDisabled, isLoading, onPress],
);
useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.ENTER, keyboardShortcutCallback, {
- isActive: pressOnEnter && !shouldDisableEnterShortcut,
+ isActive: pressOnEnter && !shouldDisableEnterShortcut && isFocused,
shouldBubble: allowBubble,
priority: enterKeyEventListenerPriority,
shouldPreventDefault: false,
diff --git a/src/components/Button/validateSubmitShortcut/index.native.ts b/src/components/Button/validateSubmitShortcut/index.native.ts
index 3f277ed208a1..4602b40c832f 100644
--- a/src/components/Button/validateSubmitShortcut/index.native.ts
+++ b/src/components/Button/validateSubmitShortcut/index.native.ts
@@ -3,14 +3,13 @@ import type ValidateSubmitShortcut from './types';
/**
* Validate if the submit shortcut should be triggered depending on the button state
*
- * @param isFocused Whether Button is on active screen
* @param isDisabled Indicates whether the button should be disabled
* @param isLoading Indicates whether the button should be disabled and in the loading state
* @return Returns `true` if the shortcut should be triggered
*/
-const validateSubmitShortcut: ValidateSubmitShortcut = (isFocused, isDisabled, isLoading) => {
- if (!isFocused || isDisabled || isLoading) {
+const validateSubmitShortcut: ValidateSubmitShortcut = (isDisabled, isLoading) => {
+ if (isDisabled || isLoading) {
return false;
}
diff --git a/src/components/Button/validateSubmitShortcut/index.ts b/src/components/Button/validateSubmitShortcut/index.ts
index 695c56af7bb7..f8cea44f73d6 100644
--- a/src/components/Button/validateSubmitShortcut/index.ts
+++ b/src/components/Button/validateSubmitShortcut/index.ts
@@ -3,16 +3,15 @@ import type ValidateSubmitShortcut from './types';
/**
* Validate if the submit shortcut should be triggered depending on the button state
*
- * @param isFocused Whether Button is on active screen
* @param isDisabled Indicates whether the button should be disabled
* @param isLoading Indicates whether the button should be disabled and in the loading state
* @param event Focused input event
* @returns Returns `true` if the shortcut should be triggered
*/
-const validateSubmitShortcut: ValidateSubmitShortcut = (isFocused, isDisabled, isLoading, event) => {
+const validateSubmitShortcut: ValidateSubmitShortcut = (isDisabled, isLoading, event) => {
const eventTarget = event?.target as HTMLElement;
- if (!isFocused || isDisabled || isLoading || eventTarget.nodeName === 'TEXTAREA') {
+ if (isDisabled || isLoading || eventTarget.nodeName === 'TEXTAREA') {
return false;
}
diff --git a/src/components/Button/validateSubmitShortcut/types.ts b/src/components/Button/validateSubmitShortcut/types.ts
index 088718d0334e..d1ff24fb0510 100644
--- a/src/components/Button/validateSubmitShortcut/types.ts
+++ b/src/components/Button/validateSubmitShortcut/types.ts
@@ -1,5 +1,5 @@
import type {GestureResponderEvent} from 'react-native';
-type ValidateSubmitShortcut = (isFocused: boolean, isDisabled: boolean, isLoading: boolean, event?: GestureResponderEvent | KeyboardEvent) => boolean;
+type ValidateSubmitShortcut = (isDisabled: boolean, isLoading: boolean, event?: GestureResponderEvent | KeyboardEvent) => boolean;
export default ValidateSubmitShortcut;
diff --git a/src/components/CheckboxWithLabel.tsx b/src/components/CheckboxWithLabel.tsx
index a25ccf184f52..602fb154deba 100644
--- a/src/components/CheckboxWithLabel.tsx
+++ b/src/components/CheckboxWithLabel.tsx
@@ -42,7 +42,7 @@ type CheckboxWithLabelProps = RequiredLabelProps & {
/** Error text to display */
errorText?: string;
- /** Value for checkbox. This prop is intended to be set by Form.js only */
+ /** Value for checkbox. This prop is intended to be set by FormProvider only */
value?: boolean;
/** The default value for the checkbox */
diff --git a/src/components/DatePicker/CalendarPicker/index.js b/src/components/DatePicker/CalendarPicker/index.js
index 571ddc820d43..f10af5e4a5a7 100644
--- a/src/components/DatePicker/CalendarPicker/index.js
+++ b/src/components/DatePicker/CalendarPicker/index.js
@@ -112,14 +112,44 @@ class CalendarPicker extends React.PureComponent {
* Handles the user pressing the previous month arrow of the calendar picker.
*/
moveToPrevMonth() {
- this.setState((prev) => ({currentDateView: subMonths(new Date(prev.currentDateView), 1)}));
+ this.setState((prev) => {
+ const prevMonth = subMonths(new Date(prev.currentDateView), 1);
+ // if year is subtracted, we need to update the years list
+ let newYears = prev.years;
+ if (prevMonth.getFullYear() < prev.currentDateView.getFullYear()) {
+ newYears = _.map(prev.years, (item) => ({
+ ...item,
+ isSelected: item.value === prevMonth.getFullYear(),
+ }));
+ }
+
+ return {
+ currentDateView: prevMonth,
+ years: newYears,
+ };
+ });
}
/**
* Handles the user pressing the next month arrow of the calendar picker.
*/
moveToNextMonth() {
- this.setState((prev) => ({currentDateView: addMonths(new Date(prev.currentDateView), 1)}));
+ this.setState((prev) => {
+ const nextMonth = addMonths(new Date(prev.currentDateView), 1);
+ // if year is added, we need to update the years list
+ let newYears = prev.years;
+ if (nextMonth.getFullYear() > prev.currentDateView.getFullYear()) {
+ newYears = _.map(prev.years, (item) => ({
+ ...item,
+ isSelected: item.value === nextMonth.getFullYear(),
+ }));
+ }
+
+ return {
+ currentDateView: nextMonth,
+ years: newYears,
+ };
+ });
}
render() {
diff --git a/src/components/MapView/MapView.tsx b/src/components/MapView/MapView.tsx
index 6321b461f21e..a3178f642852 100644
--- a/src/components/MapView/MapView.tsx
+++ b/src/components/MapView/MapView.tsx
@@ -105,7 +105,7 @@ const MapView = forwardRef(
if (waypoints.length === 1) {
cameraRef.current?.setCamera({
- zoomLevel: 15,
+ zoomLevel: CONST.MAPBOX.SINGLE_MARKER_ZOOM,
animationDuration: 1500,
centerCoordinate: waypoints[0].coordinate,
});
diff --git a/src/components/MapView/MapView.website.tsx b/src/components/MapView/MapView.website.tsx
index 05d86e8ec999..289f7d0d62a8 100644
--- a/src/components/MapView/MapView.website.tsx
+++ b/src/components/MapView/MapView.website.tsx
@@ -117,7 +117,7 @@ const MapView = forwardRef(
if (waypoints.length === 1) {
mapRef.flyTo({
center: waypoints[0].coordinate,
- zoom: CONST.MAPBOX.DEFAULT_ZOOM,
+ zoom: CONST.MAPBOX.SINGLE_MARKER_ZOOM,
});
return;
}
diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx
index 34d60418d3ab..86e77ae4bfc3 100644
--- a/src/components/MenuItem.tsx
+++ b/src/components/MenuItem.tsx
@@ -35,20 +35,6 @@ import RenderHTML from './RenderHTML';
import SelectCircle from './SelectCircle';
import Text from './Text';
-type ResponsiveProps = {
- /** Function to fire when component is pressed */
- onPress: (event: GestureResponderEvent | KeyboardEvent) => void;
-
- interactive?: true;
-};
-
-type UnresponsiveProps = {
- onPress?: undefined;
-
- /** Whether the menu item should be interactive at all */
- interactive: false;
-};
-
type IconProps = {
/** Flag to choose between avatar image or an icon */
iconType?: typeof CONST.ICON_TYPE_ICON;
@@ -69,170 +55,175 @@ type NoIcon = {
icon?: undefined;
};
-type MenuItemProps = (ResponsiveProps | UnresponsiveProps) &
- (IconProps | AvatarProps | NoIcon) & {
- /** Text to be shown as badge near the right end. */
- badgeText?: string;
+type MenuItemProps = (IconProps | AvatarProps | NoIcon) & {
+ /** Function to fire when component is pressed */
+ onPress?: (event: GestureResponderEvent | KeyboardEvent) => void;
- /** Used to apply offline styles to child text components */
- style?: ViewStyle;
+ /** Whether the menu item should be interactive at all */
+ interactive?: boolean;
- /** Any additional styles to apply */
- wrapperStyle?: StyleProp;
+ /** Text to be shown as badge near the right end. */
+ badgeText?: string;
- /** Any additional styles to apply on the outer element */
- containerStyle?: StyleProp;
+ /** Used to apply offline styles to child text components */
+ style?: ViewStyle;
- /** Used to apply styles specifically to the title */
- titleStyle?: ViewStyle;
+ /** Any additional styles to apply */
+ wrapperStyle?: StyleProp;
- /** Any adjustments to style when menu item is hovered or pressed */
- hoverAndPressStyle?: StyleProp>;
+ /** Any additional styles to apply on the outer element */
+ containerStyle?: StyleProp;
- /** Additional styles to style the description text below the title */
- descriptionTextStyle?: StyleProp;
+ /** Used to apply styles specifically to the title */
+ titleStyle?: ViewStyle;
- /** The fill color to pass into the icon. */
- iconFill?: string;
+ /** Any adjustments to style when menu item is hovered or pressed */
+ hoverAndPressStyle?: StyleProp>;
- /** Secondary icon to display on the left side of component, right of the icon */
- secondaryIcon?: IconAsset;
+ /** Additional styles to style the description text below the title */
+ descriptionTextStyle?: StyleProp;
- /** The fill color to pass into the secondary icon. */
- secondaryIconFill?: string;
+ /** The fill color to pass into the icon. */
+ iconFill?: string;
- /** Icon Width */
- iconWidth?: number;
+ /** Secondary icon to display on the left side of component, right of the icon */
+ secondaryIcon?: IconAsset;
- /** Icon Height */
- iconHeight?: number;
+ /** The fill color to pass into the secondary icon. */
+ secondaryIconFill?: string;
- /** Any additional styles to pass to the icon container. */
- iconStyles?: StyleProp;
+ /** Icon Width */
+ iconWidth?: number;
- /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */
- fallbackIcon?: IconAsset;
+ /** Icon Height */
+ iconHeight?: number;
- /** An icon to display under the main item */
- furtherDetailsIcon?: IconAsset;
+ /** Any additional styles to pass to the icon container. */
+ iconStyles?: StyleProp;
- /** Boolean whether to display the title right icon */
- shouldShowTitleIcon?: boolean;
+ /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */
+ fallbackIcon?: IconAsset;
- /** Icon to display at right side of title */
- titleIcon?: IconAsset;
+ /** An icon to display under the main item */
+ furtherDetailsIcon?: IconAsset;
- /** Boolean whether to display the right icon */
- shouldShowRightIcon?: boolean;
+ /** Boolean whether to display the title right icon */
+ shouldShowTitleIcon?: boolean;
- /** Overrides the icon for shouldShowRightIcon */
- iconRight?: IconAsset;
+ /** Icon to display at right side of title */
+ titleIcon?: IconAsset;
- /** Should render component on the right */
- shouldShowRightComponent?: boolean;
+ /** Boolean whether to display the right icon */
+ shouldShowRightIcon?: boolean;
- /** Component to be displayed on the right */
- rightComponent?: ReactNode;
+ /** Overrides the icon for shouldShowRightIcon */
+ iconRight?: IconAsset;
- /** A description text to show under the title */
- description?: string;
+ /** Should render component on the right */
+ shouldShowRightComponent?: boolean;
- /** Should the description be shown above the title (instead of the other way around) */
- shouldShowDescriptionOnTop?: boolean;
+ /** Component to be displayed on the right */
+ rightComponent?: ReactNode;
- /** Error to display below the title */
- error?: string;
+ /** A description text to show under the title */
+ description?: string;
- /** Error to display at the bottom of the component */
- errorText?: string;
+ /** Should the description be shown above the title (instead of the other way around) */
+ shouldShowDescriptionOnTop?: boolean;
- /** A boolean flag that gives the icon a green fill if true */
- success?: boolean;
+ /** Error to display below the title */
+ error?: string;
- /** Whether item is focused or active */
- focused?: boolean;
+ /** Error to display at the bottom of the component */
+ errorText?: string;
- /** Should we disable this menu item? */
- disabled?: boolean;
+ /** A boolean flag that gives the icon a green fill if true */
+ success?: boolean;
- /** Text that appears above the title */
- label?: string;
+ /** Whether item is focused or active */
+ focused?: boolean;
- /** Label to be displayed on the right */
- rightLabel?: string;
+ /** Should we disable this menu item? */
+ disabled?: boolean;
- /** Text to display for the item */
- title?: string;
+ /** Text that appears above the title */
+ label?: string;
- /** A right-aligned subtitle for this menu option */
- subtitle?: string | number;
+ /** Label to be displayed on the right */
+ rightLabel?: string;
- /** Should the title show with normal font weight (not bold) */
- shouldShowBasicTitle?: boolean;
+ /** Text to display for the item */
+ title?: string;
- /** Should we make this selectable with a checkbox */
- shouldShowSelectedState?: boolean;
+ /** A right-aligned subtitle for this menu option */
+ subtitle?: string | number;
- /** Whether this item is selected */
- isSelected?: boolean;
+ /** Should the title show with normal font weight (not bold) */
+ shouldShowBasicTitle?: boolean;
- /** Prop to identify if we should load avatars vertically instead of diagonally */
- shouldStackHorizontally?: boolean;
+ /** Should we make this selectable with a checkbox */
+ shouldShowSelectedState?: boolean;
- /** Prop to represent the size of the avatar images to be shown */
- avatarSize?: (typeof CONST.AVATAR_SIZE)[keyof typeof CONST.AVATAR_SIZE];
+ /** Whether this item is selected */
+ isSelected?: boolean;
- /** Avatars to show on the right of the menu item */
- floatRightAvatars?: IconType[];
+ /** Prop to identify if we should load avatars vertically instead of diagonally */
+ shouldStackHorizontally?: boolean;
- /** Prop to represent the size of the float right avatar images to be shown */
- floatRightAvatarSize?: ValueOf;
+ /** Prop to represent the size of the avatar images to be shown */
+ avatarSize?: (typeof CONST.AVATAR_SIZE)[keyof typeof CONST.AVATAR_SIZE];
- /** Affects avatar size */
- viewMode?: ValueOf;
+ /** Avatars to show on the right of the menu item */
+ floatRightAvatars?: IconType[];
- /** Used to truncate the text with an ellipsis after computing the text layout */
- numberOfLinesTitle?: number;
+ /** Prop to represent the size of the float right avatar images to be shown */
+ floatRightAvatarSize?: ValueOf;
- /** Whether we should use small avatar subscript sizing the for menu item */
- isSmallAvatarSubscriptMenu?: boolean;
+ /** Affects avatar size */
+ viewMode?: ValueOf;
- /** The type of brick road indicator to show. */
- brickRoadIndicator?: ValueOf;
+ /** Used to truncate the text with an ellipsis after computing the text layout */
+ numberOfLinesTitle?: number;
- /** Should render the content in HTML format */
- shouldRenderAsHTML?: boolean;
+ /** Whether we should use small avatar subscript sizing the for menu item */
+ isSmallAvatarSubscriptMenu?: boolean;
- /** Should we grey out the menu item when it is disabled? */
- shouldGreyOutWhenDisabled?: boolean;
+ /** The type of brick road indicator to show. */
+ brickRoadIndicator?: ValueOf;
- /** The action accept for anonymous user or not */
- isAnonymousAction?: boolean;
+ /** Should render the content in HTML format */
+ shouldRenderAsHTML?: boolean;
- /** Flag to indicate whether or not text selection should be disabled from long-pressing the menu item. */
- shouldBlockSelection?: boolean;
+ /** Should we grey out the menu item when it is disabled? */
+ shouldGreyOutWhenDisabled?: boolean;
- /** Whether should render title as HTML or as Text */
- shouldParseTitle?: false;
+ /** The action accept for anonymous user or not */
+ isAnonymousAction?: boolean;
- /** Should check anonymous user in onPress function */
- shouldCheckActionAllowedOnPress?: boolean;
+ /** Flag to indicate whether or not text selection should be disabled from long-pressing the menu item. */
+ shouldBlockSelection?: boolean;
- /** Text to display under the main item */
- furtherDetails?: string;
+ /** Whether should render title as HTML or as Text */
+ shouldParseTitle?: false;
- /** The function that should be called when this component is LongPressed or right-clicked. */
- onSecondaryInteraction?: () => void;
+ /** Should check anonymous user in onPress function */
+ shouldCheckActionAllowedOnPress?: boolean;
- /** Array of objects that map display names to their corresponding tooltip */
- titleWithTooltips?: DisplayNameWithTooltip[];
+ /** Text to display under the main item */
+ furtherDetails?: string;
- /** Icon should be displayed in its own color */
- displayInDefaultIconColor?: boolean;
+ /** The function that should be called when this component is LongPressed or right-clicked. */
+ onSecondaryInteraction?: (event: GestureResponderEvent | MouseEvent) => void;
- /** Determines how the icon should be resized to fit its container */
- contentFit?: ImageContentFit;
- };
+ /** Array of objects that map display names to their corresponding tooltip */
+ titleWithTooltips?: DisplayNameWithTooltip[];
+
+ /** Icon should be displayed in its own color */
+ displayInDefaultIconColor?: boolean;
+
+ /** Determines how the icon should be resized to fit its container */
+ contentFit?: ImageContentFit;
+};
function MenuItem(
{
diff --git a/src/components/MenuItemList.js b/src/components/MenuItemList.js
deleted file mode 100644
index c9eee8e888e1..000000000000
--- a/src/components/MenuItemList.js
+++ /dev/null
@@ -1,62 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import _ from 'underscore';
-import useSingleExecution from '@hooks/useSingleExecution';
-import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu';
-import CONST from '@src/CONST';
-import MenuItem from './MenuItem';
-import menuItemPropTypes from './menuItemPropTypes';
-
-const propTypes = {
- /** An array of props that are pass to individual MenuItem components */
- menuItems: PropTypes.arrayOf(PropTypes.shape(menuItemPropTypes)),
-
- /** Whether or not to use the single execution hook */
- shouldUseSingleExecution: PropTypes.bool,
-};
-const defaultProps = {
- menuItems: [],
- shouldUseSingleExecution: false,
-};
-
-function MenuItemList(props) {
- let popoverAnchor;
- const {isExecuting, singleExecution} = useSingleExecution();
-
- /**
- * Handle the secondary interaction for a menu item.
- *
- * @param {*} link the menu item link or function to get the link
- * @param {Event} e the interaction event
- */
- const secondaryInteraction = (link, e) => {
- if (typeof link === 'function') {
- link().then((url) => ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, e, url, popoverAnchor));
- } else if (!_.isEmpty(link)) {
- ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, e, link, popoverAnchor);
- }
- };
-
- return (
- <>
- {_.map(props.menuItems, (menuItemProps) => (
-