diff --git a/Mobile-Expensify b/Mobile-Expensify
index 6c6f3b2553eb9..dd19b8f51ec16 160000
--- a/Mobile-Expensify
+++ b/Mobile-Expensify
@@ -1 +1 @@
-Subproject commit 6c6f3b2553eb9911633e20ba88afeb55b6f04cbb
+Subproject commit dd19b8f51ec16bb266e7daf794b4b0e0ce10bd8b
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 5d0747c79ec4e..02133ba8d5b28 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -114,8 +114,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
- versionCode 1009022703
- versionName "9.2.27-3"
+ versionCode 1009022704
+ versionName "9.2.27-4"
// Supported language variants must be declared here to avoid from being removed during the compilation.
// This also helps us to not include unnecessary language variants in the APK.
resConfigs "en", "es"
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index 713c1bbf59593..662f96945cf20 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -44,7 +44,7 @@
CFBundleVersion
- 9.2.27.3
+ 9.2.27.4
FullStory
OrgId
diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist
index 02caaed6bd060..dfa3be6a8d1a2 100644
--- a/ios/NotificationServiceExtension/Info.plist
+++ b/ios/NotificationServiceExtension/Info.plist
@@ -13,7 +13,7 @@
CFBundleShortVersionString
9.2.27
CFBundleVersion
- 9.2.27.3
+ 9.2.27.4
NSExtension
NSExtensionPointIdentifier
diff --git a/ios/ShareViewController/Info.plist b/ios/ShareViewController/Info.plist
index f64038acabed0..10b5b84738bb2 100644
--- a/ios/ShareViewController/Info.plist
+++ b/ios/ShareViewController/Info.plist
@@ -13,7 +13,7 @@
CFBundleShortVersionString
9.2.27
CFBundleVersion
- 9.2.27.3
+ 9.2.27.4
NSExtension
NSExtensionAttributes
diff --git a/package-lock.json b/package-lock.json
index 936b9e3112b5b..e19d27bbc26ef 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "new.expensify",
- "version": "9.2.27-3",
+ "version": "9.2.27-4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "new.expensify",
- "version": "9.2.27-3",
+ "version": "9.2.27-4",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
diff --git a/package.json b/package.json
index 0f4e1b0e469a4..c639aad6979c4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
- "version": "9.2.27-3",
+ "version": "9.2.27-4",
"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/components/ActionSheetAwareScrollView/index.ios.tsx b/src/components/ActionSheetAwareScrollView/index.ios.tsx
index 9fa26e898d6ec..6fead4946aea3 100644
--- a/src/components/ActionSheetAwareScrollView/index.ios.tsx
+++ b/src/components/ActionSheetAwareScrollView/index.ios.tsx
@@ -1,10 +1,12 @@
-import React, {useCallback} from 'react';
+import React, {forwardRef, useCallback} from 'react';
+// eslint-disable-next-line no-restricted-imports
+import type {ScrollView} from 'react-native';
import Reanimated, {useAnimatedRef, useAnimatedStyle} from 'react-native-reanimated';
import {Actions, ActionSheetAwareScrollViewContext, ActionSheetAwareScrollViewProvider} from './ActionSheetAwareScrollViewContext';
import type {ActionSheetAwareScrollViewProps, RenderActionSheetAwareScrollViewComponent} from './types';
import useActionSheetKeyboardSpacing from './useActionSheetKeyboardSpacing';
-function ActionSheetAwareScrollView({style, children, ref, ...props}: ActionSheetAwareScrollViewProps) {
+const ActionSheetAwareScrollView = forwardRef(({style, children, ...props}, ref) => {
const scrollViewAnimatedRef = useAnimatedRef();
const onRef = useCallback(
@@ -36,7 +38,7 @@ function ActionSheetAwareScrollView({style, children, ref, ...props}: ActionShee
{children}
);
-}
+});
export default ActionSheetAwareScrollView;
diff --git a/src/components/ActionSheetAwareScrollView/index.tsx b/src/components/ActionSheetAwareScrollView/index.tsx
index aad22519466e2..f2fc64cda8bd0 100644
--- a/src/components/ActionSheetAwareScrollView/index.tsx
+++ b/src/components/ActionSheetAwareScrollView/index.tsx
@@ -1,21 +1,20 @@
// this whole file is just for other platforms
// iOS version has everything implemented
-import React from 'react';
+import React, {forwardRef} from 'react';
// eslint-disable-next-line no-restricted-imports
import {ScrollView} from 'react-native';
import {Actions, ActionSheetAwareScrollViewContext, ActionSheetAwareScrollViewProvider} from './ActionSheetAwareScrollViewContext';
import type {ActionSheetAwareScrollViewProps, RenderActionSheetAwareScrollViewComponent} from './types';
-function ActionSheetAwareScrollView(props: ActionSheetAwareScrollViewProps) {
- return (
-
- {props.children}
-
- );
-}
+const ActionSheetAwareScrollView = forwardRef((props, ref) => (
+
+ {props.children}
+
+));
export default ActionSheetAwareScrollView;
diff --git a/src/components/ActionSheetAwareScrollView/types.ts b/src/components/ActionSheetAwareScrollView/types.ts
index c3b1a2ded8c9a..9d7f7f765fefc 100644
--- a/src/components/ActionSheetAwareScrollView/types.ts
+++ b/src/components/ActionSheetAwareScrollView/types.ts
@@ -1,10 +1,6 @@
-import type {PropsWithChildren, Ref} from 'react';
-// eslint-disable-next-line no-restricted-imports
-import type {ScrollView, ScrollViewProps} from 'react-native';
+import type {PropsWithChildren} from 'react';
+import type {ScrollViewProps} from 'react-native';
-type ActionSheetAwareScrollViewAnimationProps = {
- ref?: Ref;
-};
-type ActionSheetAwareScrollViewProps = PropsWithChildren & ActionSheetAwareScrollViewAnimationProps;
+type ActionSheetAwareScrollViewProps = PropsWithChildren;
type RenderActionSheetAwareScrollViewComponent = ((props: ActionSheetAwareScrollViewProps) => React.ReactElement) | undefined;
export type {ActionSheetAwareScrollViewProps, RenderActionSheetAwareScrollViewComponent};
diff --git a/src/components/AddressSearch/index.tsx b/src/components/AddressSearch/index.tsx
index ac27c32700b3a..4e0fe3d646f7b 100644
--- a/src/components/AddressSearch/index.tsx
+++ b/src/components/AddressSearch/index.tsx
@@ -1,4 +1,5 @@
-import React, {useEffect, useMemo, useRef, useState} from 'react';
+import React, {forwardRef, useEffect, useMemo, useRef, useState} from 'react';
+import type {ForwardedRef} from 'react';
import {Keyboard, LogBox, View} from 'react-native';
import type {LayoutChangeEvent} from 'react-native';
import {GooglePlacesAutocomplete} from 'react-native-google-places-autocomplete';
@@ -48,38 +49,40 @@ function isPlaceMatchForSearch(search: string, place: PredefinedPlace): boolean
// VirtualizedList component with a VirtualizedList-backed instead
LogBox.ignoreLogs(['VirtualizedLists should never be nested']);
-function AddressSearch({
- canUseCurrentLocation = false,
- containerStyles,
- defaultValue,
- errorText = '',
- hint = '',
- inputID,
- limitSearchesToCountry,
- label,
- maxInputLength,
- onFocus,
- onBlur,
- onInputChange,
- onPress,
- onCountryChange,
- predefinedPlaces = [],
- renamedInputKeys = {
- street: 'addressStreet',
- street2: 'addressStreet2',
- city: 'addressCity',
- state: 'addressState',
- zipCode: 'addressZipCode',
- lat: 'addressLat',
- lng: 'addressLng',
- },
- resultTypes = 'address',
- shouldSaveDraft = false,
- value,
- locationBias,
- caretHidden,
- ref,
-}: AddressSearchProps) {
+function AddressSearch(
+ {
+ canUseCurrentLocation = false,
+ containerStyles,
+ defaultValue,
+ errorText = '',
+ hint = '',
+ inputID,
+ limitSearchesToCountry,
+ label,
+ maxInputLength,
+ onFocus,
+ onBlur,
+ onInputChange,
+ onPress,
+ onCountryChange,
+ predefinedPlaces = [],
+ renamedInputKeys = {
+ street: 'addressStreet',
+ street2: 'addressStreet2',
+ city: 'addressCity',
+ state: 'addressState',
+ zipCode: 'addressZipCode',
+ lat: 'addressLat',
+ lng: 'addressLng',
+ },
+ resultTypes = 'address',
+ shouldSaveDraft = false,
+ value,
+ locationBias,
+ caretHidden,
+ }: AddressSearchProps,
+ ref: ForwardedRef,
+) {
const theme = useTheme();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
@@ -487,6 +490,6 @@ function AddressSearch({
AddressSearch.displayName = 'AddressSearchWithRef';
-export default AddressSearch;
+export default forwardRef(AddressSearch);
export type {AddressSearchProps};
diff --git a/src/components/AddressSearch/types.ts b/src/components/AddressSearch/types.ts
index 4c2979e9427ff..dcf8822c4b5e5 100644
--- a/src/components/AddressSearch/types.ts
+++ b/src/components/AddressSearch/types.ts
@@ -1,4 +1,4 @@
-import type {ForwardedRef, RefObject} from 'react';
+import type {RefObject} from 'react';
import type {NativeSyntheticEvent, StyleProp, TextInputFocusEventData, View, ViewStyle} from 'react-native';
import type {Place} from 'react-native-google-places-autocomplete';
import type {Country} from '@src/CONST';
@@ -90,9 +90,6 @@ type AddressSearchProps = {
/** If true, caret is hidden. The default value is false. */
caretHidden?: boolean;
-
- /** Reference to the outer element */
- ref?: ForwardedRef;
};
type IsCurrentTargetInsideContainerType = (event: FocusEvent | NativeSyntheticEvent, containerRef: RefObject) => boolean;
diff --git a/src/components/AmountPicker/index.tsx b/src/components/AmountPicker/index.tsx
index 40ad6ed9b752f..ec9ff2f4117dc 100644
--- a/src/components/AmountPicker/index.tsx
+++ b/src/components/AmountPicker/index.tsx
@@ -1,4 +1,5 @@
-import React, {useState} from 'react';
+import React, {forwardRef, useState} from 'react';
+import type {ForwardedRef} from 'react';
import {View} from 'react-native';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import blurActiveElement from '@libs/Accessibility/blurActiveElement';
@@ -7,7 +8,7 @@ import callOrReturn from '@src/types/utils/callOrReturn';
import AmountSelectorModal from './AmountSelectorModal';
import type {AmountPickerProps} from './types';
-function AmountPicker({value, description, title, errorText = '', onInputChange, furtherDetails, rightLabel, ref, ...rest}: AmountPickerProps) {
+function AmountPicker({value, description, title, errorText = '', onInputChange, furtherDetails, rightLabel, ...rest}: AmountPickerProps, forwardedRef: ForwardedRef) {
const [isPickerVisible, setIsPickerVisible] = useState(false);
const showPickerModal = () => {
@@ -30,7 +31,7 @@ function AmountPicker({value, description, title, errorText = '', onInputChange,
return (
;
} & Pick &
Pick<
NumberWithSymbolFormProps,
diff --git a/src/components/AmountWithoutCurrencyInput.tsx b/src/components/AmountWithoutCurrencyInput.tsx
index 211d4050e40ff..da1f3855dcf17 100644
--- a/src/components/AmountWithoutCurrencyInput.tsx
+++ b/src/components/AmountWithoutCurrencyInput.tsx
@@ -1,9 +1,10 @@
import React, {useCallback, useMemo} from 'react';
+import type {ForwardedRef} from 'react';
import useLocalize from '@hooks/useLocalize';
import getAmountInputKeyboard from '@libs/getAmountInputKeyboard';
import {replaceAllDigits, replaceCommasWithPeriod, stripSpacesFromAmount} from '@libs/MoneyRequestUtils';
import TextInput from './TextInput';
-import type {BaseTextInputProps} from './TextInput/BaseTextInput/types';
+import type {BaseTextInputProps, BaseTextInputRef} from './TextInput/BaseTextInput/types';
type AmountFormProps = {
/** Amount supplied by the FormProvider */
@@ -16,19 +17,10 @@ type AmountFormProps = {
shouldAllowNegative?: boolean;
} & Partial;
-function AmountWithoutCurrencyInput({
- value: amount,
- shouldAllowNegative = false,
- inputID,
- name,
- defaultValue,
- accessibilityLabel,
- role,
- label,
- onInputChange,
- ref,
- ...rest
-}: AmountFormProps) {
+function AmountWithoutCurrencyInput(
+ {value: amount, shouldAllowNegative = false, inputID, name, defaultValue, accessibilityLabel, role, label, onInputChange, ...rest}: AmountFormProps,
+ ref: ForwardedRef,
+) {
const {toLocaleDigit} = useLocalize();
const separator = useMemo(
() =>
@@ -95,4 +87,4 @@ function AmountWithoutCurrencyInput({
AmountWithoutCurrencyInput.displayName = 'AmountWithoutCurrencyInput';
-export default AmountWithoutCurrencyInput;
+export default React.forwardRef(AmountWithoutCurrencyInput);
diff --git a/src/components/Attachments/AttachmentCarousel/Pager/index.tsx b/src/components/Attachments/AttachmentCarousel/Pager/index.tsx
index 4ef7d53d866f7..11875d93473fd 100644
--- a/src/components/Attachments/AttachmentCarousel/Pager/index.tsx
+++ b/src/components/Attachments/AttachmentCarousel/Pager/index.tsx
@@ -48,12 +48,12 @@ type AttachmentCarouselPagerProps = {
/** Callback for attachment errors */
onAttachmentError?: (source: AttachmentSource) => void;
-
- /** Reference to the outer element */
- ref?: ForwardedRef;
};
-function AttachmentCarouselPager({items, activeAttachmentID, initialPage, setShouldShowArrows, onPageSelected, onClose, reportID, onAttachmentError, ref}: AttachmentCarouselPagerProps) {
+function AttachmentCarouselPager(
+ {items, activeAttachmentID, initialPage, setShouldShowArrows, onPageSelected, onClose, reportID, onAttachmentError}: AttachmentCarouselPagerProps,
+ ref: ForwardedRef,
+) {
const {handleTap, handleScaleChange, isScrollEnabled} = useCarouselContextEvents(setShouldShowArrows);
const styles = useThemeStyles();
const pagerRef = useRef(null);
@@ -153,5 +153,5 @@ function AttachmentCarouselPager({items, activeAttachmentID, initialPage, setSho
}
AttachmentCarouselPager.displayName = 'AttachmentCarouselPager';
-export default AttachmentCarouselPager;
+export default React.forwardRef(AttachmentCarouselPager);
export type {AttachmentCarouselPagerHandle};
diff --git a/src/components/CheckboxWithLabel.tsx b/src/components/CheckboxWithLabel.tsx
index b5ef4f2e974d8..0647b495bd339 100644
--- a/src/components/CheckboxWithLabel.tsx
+++ b/src/components/CheckboxWithLabel.tsx
@@ -58,23 +58,12 @@ type CheckboxWithLabelProps = RequiredLabelProps & {
/** An accessibility label for the checkbox */
accessibilityLabel?: string;
-
- /** Reference to the outer element */
- ref?: ForwardedRef;
};
-function CheckboxWithLabel({
- errorText = '',
- isChecked: isCheckedProp = false,
- defaultValue = false,
- onInputChange = () => {},
- LabelComponent,
- label,
- accessibilityLabel,
- style,
- value,
- ref,
-}: CheckboxWithLabelProps) {
+function CheckboxWithLabel(
+ {errorText = '', isChecked: isCheckedProp = false, defaultValue = false, onInputChange = () => {}, LabelComponent, label, accessibilityLabel, style, value}: CheckboxWithLabelProps,
+ ref: ForwardedRef,
+) {
const styles = useThemeStyles();
// We need to pick the first value that is strictly a boolean
// https://github.com/Expensify/App/issues/16885#issuecomment-1520846065
@@ -117,6 +106,6 @@ function CheckboxWithLabel({
CheckboxWithLabel.displayName = 'CheckboxWithLabel';
-export default CheckboxWithLabel;
+export default React.forwardRef(CheckboxWithLabel);
export type {CheckboxWithLabelProps};
diff --git a/src/components/ContextMenuItem.tsx b/src/components/ContextMenuItem.tsx
index 214e7bd616ec3..6aff89666a079 100644
--- a/src/components/ContextMenuItem.tsx
+++ b/src/components/ContextMenuItem.tsx
@@ -1,5 +1,5 @@
import type {ForwardedRef} from 'react';
-import React, {useImperativeHandle} from 'react';
+import React, {forwardRef, useImperativeHandle} from 'react';
import type {GestureResponderEvent, StyleProp, View, ViewStyle} from 'react-native';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
@@ -61,35 +61,34 @@ type ContextMenuItemProps = {
/** Whether the menu item should show loading icon */
shouldShowLoadingSpinnerIcon?: boolean;
-
- /** Reference to the outer element */
- ref?: ForwardedRef;
};
type ContextMenuItemHandle = {
triggerPressAndUpdateSuccess?: () => void;
};
-function ContextMenuItem({
- onPress,
- successIcon,
- successText = '',
- icon,
- text,
- isMini = false,
- description = '',
- isAnonymousAction = false,
- isFocused = false,
- shouldLimitWidth = true,
- wrapperStyle,
- shouldPreventDefaultFocusOnPress = true,
- buttonRef = {current: null},
- onFocus = () => {},
- onBlur = () => {},
- disabled = false,
- shouldShowLoadingSpinnerIcon = false,
- ref,
-}: ContextMenuItemProps) {
+function ContextMenuItem(
+ {
+ onPress,
+ successIcon,
+ successText = '',
+ icon,
+ text,
+ isMini = false,
+ description = '',
+ isAnonymousAction = false,
+ isFocused = false,
+ shouldLimitWidth = true,
+ wrapperStyle,
+ shouldPreventDefaultFocusOnPress = true,
+ buttonRef = {current: null},
+ onFocus = () => {},
+ onBlur = () => {},
+ disabled = false,
+ shouldShowLoadingSpinnerIcon = false,
+ }: ContextMenuItemProps,
+ ref: ForwardedRef,
+) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const {windowWidth} = useWindowDimensions();
@@ -152,5 +151,5 @@ function ContextMenuItem({
ContextMenuItem.displayName = 'ContextMenuItem';
-export default ContextMenuItem;
+export default forwardRef(ContextMenuItem);
export type {ContextMenuItemHandle};
diff --git a/src/components/CountrySelector.tsx b/src/components/CountrySelector.tsx
index 817c49ad03bc1..094f529c66b2e 100644
--- a/src/components/CountrySelector.tsx
+++ b/src/components/CountrySelector.tsx
@@ -1,5 +1,5 @@
import {useIsFocused} from '@react-navigation/native';
-import React, {useEffect, useRef} from 'react';
+import React, {forwardRef, useEffect, useRef} from 'react';
import type {ForwardedRef} from 'react';
import type {View} from 'react-native';
import useGeographicalStateAndCountryFromRoute from '@hooks/useGeographicalStateAndCountryFromRoute';
@@ -27,12 +27,9 @@ type CountrySelectorProps = {
/** Callback to call when the picker modal is dismissed */
onBlur?: () => void;
-
- /** Reference to the outer element */
- ref?: ForwardedRef;
};
-function CountrySelector({errorText = '', value: countryCode, onInputChange = () => {}, onBlur, ref}: CountrySelectorProps) {
+function CountrySelector({errorText = '', value: countryCode, onInputChange = () => {}, onBlur}: CountrySelectorProps, ref: ForwardedRef) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {country: countryFromUrl} = useGeographicalStateAndCountryFromRoute();
@@ -88,4 +85,4 @@ function CountrySelector({errorText = '', value: countryCode, onInputChange = ()
CountrySelector.displayName = 'CountrySelector';
-export default CountrySelector;
+export default forwardRef(CountrySelector);
diff --git a/src/components/CurrencySelector.tsx b/src/components/CurrencySelector.tsx
index 5c242c7bdc7d3..1d45860f5d32b 100644
--- a/src/components/CurrencySelector.tsx
+++ b/src/components/CurrencySelector.tsx
@@ -1,6 +1,6 @@
import {useIsFocused} from '@react-navigation/native';
import type {ForwardedRef} from 'react';
-import React, {useEffect, useRef} from 'react';
+import React, {forwardRef, useEffect, useRef} from 'react';
import type {View} from 'react-native';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
@@ -35,21 +35,20 @@ type CurrencySelectorProps = {
/** Whether to show currency symbol in the title */
shouldShowCurrencySymbol?: boolean;
-
- /** Reference to the outer element */
- ref?: ForwardedRef;
};
-function CurrencySelector({
- errorText = '',
- value: currency,
- onInputChange = () => {},
- onBlur,
- currencySelectorRoute = ROUTES.SETTINGS_CHANGE_CURRENCY,
- label,
- shouldShowCurrencySymbol = false,
- ref,
-}: CurrencySelectorProps) {
+function CurrencySelector(
+ {
+ errorText = '',
+ value: currency,
+ onInputChange = () => {},
+ onBlur,
+ currencySelectorRoute = ROUTES.SETTINGS_CHANGE_CURRENCY,
+ label,
+ shouldShowCurrencySymbol = false,
+ }: CurrencySelectorProps,
+ ref: ForwardedRef,
+) {
const styles = useThemeStyles();
const {translate} = useLocalize();
@@ -95,4 +94,4 @@ function CurrencySelector({
CurrencySelector.displayName = 'CurrencySelector';
-export default CurrencySelector;
+export default forwardRef(CurrencySelector);
diff --git a/src/components/DatePicker/index.tsx b/src/components/DatePicker/index.tsx
index 3704be90fd263..e3855f930347a 100644
--- a/src/components/DatePicker/index.tsx
+++ b/src/components/DatePicker/index.tsx
@@ -1,5 +1,6 @@
import {format, setYear} from 'date-fns';
-import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
+import React, {forwardRef, useCallback, useEffect, useMemo, useRef, useState} from 'react';
+import type {ForwardedRef} from 'react';
import {InteractionManager, View} from 'react-native';
import * as Expensicons from '@components/Icon/Expensicons';
import TextInput from '@components/TextInput';
@@ -15,24 +16,26 @@ import type {DateInputWithPickerProps} from './types';
const PADDING_MODAL_DATE_PICKER = 8;
-function DatePicker({
- defaultValue,
- disabled,
- errorText,
- inputID,
- label,
- minDate = setYear(new Date(), CONST.CALENDAR_PICKER.MIN_YEAR),
- maxDate = setYear(new Date(), CONST.CALENDAR_PICKER.MAX_YEAR),
- onInputChange,
- onTouched = () => {},
- placeholder,
- value,
- shouldSaveDraft = false,
- formID,
- autoFocus = false,
- shouldHideClearButton = false,
- ref,
-}: DateInputWithPickerProps) {
+function DatePicker(
+ {
+ defaultValue,
+ disabled,
+ errorText,
+ inputID,
+ label,
+ minDate = setYear(new Date(), CONST.CALENDAR_PICKER.MIN_YEAR),
+ maxDate = setYear(new Date(), CONST.CALENDAR_PICKER.MAX_YEAR),
+ onInputChange,
+ onTouched = () => {},
+ placeholder,
+ value,
+ shouldSaveDraft = false,
+ formID,
+ autoFocus = false,
+ shouldHideClearButton = false,
+ }: DateInputWithPickerProps,
+ ref: ForwardedRef,
+) {
const styles = useThemeStyles();
const {windowHeight, windowWidth} = useWindowDimensions();
const {translate} = useLocalize();
@@ -162,4 +165,4 @@ function DatePicker({
DatePicker.displayName = 'DatePicker';
-export default DatePicker;
+export default forwardRef(DatePicker);
diff --git a/src/components/DatePicker/types.ts b/src/components/DatePicker/types.ts
index 24ef3e61f7062..1e4bafa5f1d90 100644
--- a/src/components/DatePicker/types.ts
+++ b/src/components/DatePicker/types.ts
@@ -1,6 +1,5 @@
-import type {ForwardedRef} from 'react';
import type PopoverWithMeasuredContentProps from '@components/PopoverWithMeasuredContent/types';
-import type {BaseTextInputProps, BaseTextInputRef} from '@components/TextInput/BaseTextInput/types';
+import type {BaseTextInputProps} from '@components/TextInput/BaseTextInput/types';
import type {OnyxFormValuesMapping} from '@src/ONYXKEYS';
type DatePickerBaseProps = {
@@ -54,11 +53,6 @@ type DateInputWithPickerProps = DatePickerBaseProps &
* @default false
*/
shouldHideClearButton?: boolean;
-
- /**
- * Reference to the outer element
- */
- ref?: ForwardedRef;
};
type DatePickerProps = {
diff --git a/src/components/FormScrollView.tsx b/src/components/FormScrollView.tsx
index 77eed896a9689..91f5a825a38a1 100644
--- a/src/components/FormScrollView.tsx
+++ b/src/components/FormScrollView.tsx
@@ -8,12 +8,9 @@ import ScrollView from './ScrollView';
type FormScrollViewProps = ScrollViewProps & {
/** Form elements */
children: React.ReactNode;
-
- /** Reference to the outer element */
- ref?: ForwardedRef;
};
-function FormScrollView({children, ref, ...rest}: FormScrollViewProps) {
+function FormScrollView({children, ...rest}: FormScrollViewProps, ref: ForwardedRef) {
const styles = useThemeStyles();
return (
;
};
-function HighlightableMenuItem({wrapperStyle, highlighted, ref, ...restOfProps}: Props) {
+function HighlightableMenuItem({wrapperStyle, highlighted, ...restOfProps}: Props, ref: ForwardedRef) {
const styles = useThemeStyles();
const flattenedWrapperStyles = StyleSheet.flatten(wrapperStyle);
@@ -38,4 +35,4 @@ function HighlightableMenuItem({wrapperStyle, highlighted, ref, ...restOfProps}:
HighlightableMenuItem.displayName = 'HighlightableMenuItem';
-export default HighlightableMenuItem;
+export default forwardRef(HighlightableMenuItem);
diff --git a/src/components/InteractiveStepSubHeader.tsx b/src/components/InteractiveStepSubHeader.tsx
index 3be92324e73ae..aad5f502df478 100644
--- a/src/components/InteractiveStepSubHeader.tsx
+++ b/src/components/InteractiveStepSubHeader.tsx
@@ -1,5 +1,5 @@
import type {ForwardedRef} from 'react';
-import React, {useImperativeHandle, useState} from 'react';
+import React, {forwardRef, useImperativeHandle, useState} from 'react';
import type {ViewStyle} from 'react-native';
import {View} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';
@@ -20,9 +20,6 @@ type InteractiveStepSubHeaderProps = {
/** The index of the step to start with */
startStepIndex?: number;
-
- /** Reference to the outer element */
- ref?: ForwardedRef;
};
type InteractiveStepSubHeaderHandle = {
@@ -39,7 +36,7 @@ type InteractiveStepSubHeaderHandle = {
const MIN_AMOUNT_FOR_EXPANDING = 3;
const MIN_AMOUNT_OF_STEPS = 2;
-function InteractiveStepSubHeader({stepNames, startStepIndex = 0, onStepSelected, ref}: InteractiveStepSubHeaderProps) {
+function InteractiveStepSubHeader({stepNames, startStepIndex = 0, onStepSelected}: InteractiveStepSubHeaderProps, ref: ForwardedRef) {
const styles = useThemeStyles();
const containerWidthStyle: ViewStyle = stepNames.length < MIN_AMOUNT_FOR_EXPANDING ? styles.mnw60 : styles.mnw100;
@@ -126,4 +123,4 @@ InteractiveStepSubHeader.displayName = 'InteractiveStepSubHeader';
export type {InteractiveStepSubHeaderProps, InteractiveStepSubHeaderHandle};
-export default InteractiveStepSubHeader;
+export default forwardRef(InteractiveStepSubHeader);
diff --git a/src/components/InteractiveStepWrapper.tsx b/src/components/InteractiveStepWrapper.tsx
index dc5b54669509b..d9f6709c47ec8 100644
--- a/src/components/InteractiveStepWrapper.tsx
+++ b/src/components/InteractiveStepWrapper.tsx
@@ -1,5 +1,4 @@
-import type {ForwardedRef} from 'react';
-import React from 'react';
+import React, {forwardRef} from 'react';
import type {StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';
@@ -57,28 +56,27 @@ type InteractiveStepWrapperProps = {
* This flag can be removed, once all components/screens have switched to edge-to-edge safe area handling.
*/
enableEdgeToEdgeBottomSafeAreaPadding?: boolean;
-
- // Reference to the outer element
- ref?: ForwardedRef;
};
-function InteractiveStepWrapper({
- children,
- wrapperID,
- handleBackButtonPress,
- headerTitle,
- headerSubtitle,
- startStepIndex,
- stepNames,
- shouldEnableMaxHeight,
- shouldShowOfflineIndicator,
- shouldShowOfflineIndicatorInWideScreen,
- shouldEnablePickerAvoiding = false,
- offlineIndicatorStyle,
- shouldKeyboardOffsetBottomSafeAreaPadding,
- enableEdgeToEdgeBottomSafeAreaPadding,
- ref,
-}: InteractiveStepWrapperProps) {
+function InteractiveStepWrapper(
+ {
+ children,
+ wrapperID,
+ handleBackButtonPress,
+ headerTitle,
+ headerSubtitle,
+ startStepIndex,
+ stepNames,
+ shouldEnableMaxHeight,
+ shouldShowOfflineIndicator,
+ shouldShowOfflineIndicatorInWideScreen,
+ shouldEnablePickerAvoiding = false,
+ offlineIndicatorStyle,
+ shouldKeyboardOffsetBottomSafeAreaPadding,
+ enableEdgeToEdgeBottomSafeAreaPadding,
+ }: InteractiveStepWrapperProps,
+ ref: React.ForwardedRef,
+) {
const styles = useThemeStyles();
return (
@@ -114,4 +112,4 @@ function InteractiveStepWrapper({
InteractiveStepWrapper.displayName = 'InteractiveStepWrapper';
-export default InteractiveStepWrapper;
+export default forwardRef(InteractiveStepWrapper);
diff --git a/src/components/MagicCodeInput.tsx b/src/components/MagicCodeInput.tsx
index dea019ea1cd0d..12946ee83693f 100644
--- a/src/components/MagicCodeInput.tsx
+++ b/src/components/MagicCodeInput.tsx
@@ -1,5 +1,5 @@
import type {ForwardedRef, KeyboardEvent} from 'react';
-import React, {useEffect, useImperativeHandle, useRef, useState} from 'react';
+import React, {forwardRef, useEffect, useImperativeHandle, useRef, useState} from 'react';
import type {NativeSyntheticEvent, TextInput as RNTextInput, TextInputFocusEventData, TextInputKeyPressEventData} from 'react-native';
import {StyleSheet, View} from 'react-native';
import {Gesture, GestureDetector} from 'react-native-gesture-handler';
@@ -99,9 +99,6 @@ type MagicCodeInputProps = {
/** TestID for test */
testID?: string;
-
- /** Reference to the outer element */
- ref?: ForwardedRef;
};
type MagicCodeInputHandle = {
@@ -135,23 +132,25 @@ const composeToString = (value: string[]): string => value.map((v) => v ?? CONST
const getInputPlaceholderSlots = (length: number): number[] => Array.from(Array(length).keys());
-function MagicCodeInput({
- value = '',
- name = '',
- autoFocus = true,
- errorText = '',
- shouldSubmitOnComplete = true,
- onChangeText: onChangeTextProp = () => {},
- onFocus: onFocusProps,
- maxLength = CONST.MAGIC_CODE_LENGTH,
- onFulfill = () => {},
- isDisableKeyboard = false,
- lastPressedDigit = '',
- autoComplete,
- hasError = false,
- testID = '',
- ref,
-}: MagicCodeInputProps) {
+function MagicCodeInput(
+ {
+ value = '',
+ name = '',
+ autoFocus = true,
+ errorText = '',
+ shouldSubmitOnComplete = true,
+ onChangeText: onChangeTextProp = () => {},
+ onFocus: onFocusProps,
+ maxLength = CONST.MAGIC_CODE_LENGTH,
+ onFulfill = () => {},
+ isDisableKeyboard = false,
+ lastPressedDigit = '',
+ autoComplete,
+ hasError = false,
+ testID = '',
+ }: MagicCodeInputProps,
+ ref: ForwardedRef,
+) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const inputRef = useRef(null);
@@ -541,5 +540,5 @@ function MagicCodeInput({
MagicCodeInput.displayName = 'MagicCodeInput';
-export default MagicCodeInput;
+export default forwardRef(MagicCodeInput);
export type {AutoCompleteVariant, MagicCodeInputHandle, MagicCodeInputProps};
diff --git a/src/components/PercentageForm.tsx b/src/components/PercentageForm.tsx
index 3c3b78e3ad825..aee231de526ce 100644
--- a/src/components/PercentageForm.tsx
+++ b/src/components/PercentageForm.tsx
@@ -1,5 +1,5 @@
import type {ForwardedRef} from 'react';
-import React, {useCallback, useMemo, useRef} from 'react';
+import React, {forwardRef, useCallback, useMemo, useRef} from 'react';
import useLocalize from '@hooks/useLocalize';
import {replaceAllDigits, stripCommaFromAmount, stripSpacesFromAmount, validatePercentage} from '@libs/MoneyRequestUtils';
import CONST from '@src/CONST';
@@ -18,12 +18,9 @@ type PercentageFormProps = {
/** Custom label for the TextInput */
label?: string;
-
- /** Reference to the outer element */
- ref?: ForwardedRef;
};
-function PercentageForm({value: amount, errorText, onInputChange, label, ref, ...rest}: PercentageFormProps) {
+function PercentageForm({value: amount, errorText, onInputChange, label, ...rest}: PercentageFormProps, forwardedRef: ForwardedRef) {
const {toLocaleDigit, numberFormat} = useLocalize();
const textInput = useRef(null);
@@ -59,14 +56,14 @@ function PercentageForm({value: amount, errorText, onInputChange, label, ref, ..
value={formattedAmount}
onChangeText={setNewAmount}
placeholder={numberFormat(0)}
- ref={(newRef: BaseTextInputRef | null) => {
- if (typeof ref === 'function') {
- ref(newRef);
- } else if (ref && 'current' in ref) {
+ ref={(ref: BaseTextInputRef | null) => {
+ if (typeof forwardedRef === 'function') {
+ forwardedRef(ref);
+ } else if (forwardedRef && 'current' in forwardedRef) {
// eslint-disable-next-line no-param-reassign
- ref.current = newRef;
+ forwardedRef.current = ref;
}
- textInput.current = newRef;
+ textInput.current = ref;
}}
suffixCharacter="%"
keyboardType={CONST.KEYBOARD_TYPE.DECIMAL_PAD}
@@ -81,4 +78,4 @@ function PercentageForm({value: amount, errorText, onInputChange, label, ref, ..
PercentageForm.displayName = 'PercentageForm';
-export default PercentageForm;
+export default forwardRef(PercentageForm);
diff --git a/src/components/RadioButtons.tsx b/src/components/RadioButtons.tsx
index afbc8879c68c6..07e8fe38f772c 100644
--- a/src/components/RadioButtons.tsx
+++ b/src/components/RadioButtons.tsx
@@ -1,4 +1,4 @@
-import React, {useEffect, useState} from 'react';
+import React, {forwardRef, useEffect, useState} from 'react';
import type {ForwardedRef} from 'react';
import {View} from 'react-native';
import type {StyleProp, ViewStyle} from 'react-native';
@@ -33,12 +33,9 @@ type RadioButtonsProps = {
/** The checked value, if you're using this component as a controlled input. */
value?: string;
-
- /** Reference to the outer element */
- ref?: ForwardedRef;
};
-function RadioButtons({items, onPress, defaultCheckedValue = '', radioButtonStyle, errorText, onInputChange = () => {}, value, ref}: RadioButtonsProps) {
+function RadioButtons({items, onPress, defaultCheckedValue = '', radioButtonStyle, errorText, onInputChange = () => {}, value}: RadioButtonsProps, ref: ForwardedRef) {
const styles = useThemeStyles();
const [checkedValue, setCheckedValue] = useState(defaultCheckedValue);
@@ -77,4 +74,4 @@ function RadioButtons({items, onPress, defaultCheckedValue = '', radioButtonStyl
RadioButtons.displayName = 'RadioButtons';
export type {Choice};
-export default RadioButtons;
+export default forwardRef(RadioButtons);
diff --git a/src/components/Share/ShareTabParticipantsSelector.tsx b/src/components/Share/ShareTabParticipantsSelector.tsx
index 015576d5eb450..4d8bee4f9ccef 100644
--- a/src/components/Share/ShareTabParticipantsSelector.tsx
+++ b/src/components/Share/ShareTabParticipantsSelector.tsx
@@ -1,5 +1,4 @@
-import type {Ref} from 'react';
-import React from 'react';
+import React, {forwardRef} from 'react';
import {saveUnknownUserDetails} from '@libs/actions/Share';
import Navigation from '@libs/Navigation/Navigation';
import MoneyRequestParticipantsSelector from '@pages/iou/request/MoneyRequestParticipantsSelector';
@@ -9,14 +8,13 @@ import type ROUTES from '@src/ROUTES';
type ShareTabParticipantsSelectorProps = {
detailsPageRouteObject: typeof ROUTES.SHARE_SUBMIT_DETAILS | typeof ROUTES.SHARE_DETAILS;
- ref?: Ref;
};
type InputFocusRef = {
focus?: () => void;
};
-function ShareTabParticipantsSelectorComponent({detailsPageRouteObject, ref}: ShareTabParticipantsSelectorProps) {
+function ShareTabParticipantsSelectorComponent({detailsPageRouteObject}: ShareTabParticipantsSelectorProps, ref: React.Ref) {
return (
(ShareTabParticipantsSelectorComponent);
diff --git a/src/components/SingleChoiceQuestion.tsx b/src/components/SingleChoiceQuestion.tsx
index a6e29661debd6..e520078504757 100644
--- a/src/components/SingleChoiceQuestion.tsx
+++ b/src/components/SingleChoiceQuestion.tsx
@@ -1,5 +1,5 @@
import type {ForwardedRef} from 'react';
-import React from 'react';
+import React, {forwardRef} from 'react';
// eslint-disable-next-line no-restricted-imports
import type {Text as RNText} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';
@@ -13,10 +13,9 @@ type SingleChoiceQuestionProps = {
possibleAnswers: Choice[];
currentQuestionIndex: number;
onInputChange: (value: string) => void;
- ref?: ForwardedRef;
};
-function SingleChoiceQuestion({prompt, errorText, possibleAnswers, currentQuestionIndex, onInputChange, ref}: SingleChoiceQuestionProps) {
+function SingleChoiceQuestion({prompt, errorText, possibleAnswers, currentQuestionIndex, onInputChange}: SingleChoiceQuestionProps, ref: ForwardedRef) {
const styles = useThemeStyles();
return (
@@ -39,4 +38,4 @@ function SingleChoiceQuestion({prompt, errorText, possibleAnswers, currentQuesti
SingleChoiceQuestion.displayName = 'SingleChoiceQuestion';
-export default SingleChoiceQuestion;
+export default forwardRef(SingleChoiceQuestion);
diff --git a/src/components/StateSelector.tsx b/src/components/StateSelector.tsx
index b32a63523aac0..e2be9281d0bb2 100644
--- a/src/components/StateSelector.tsx
+++ b/src/components/StateSelector.tsx
@@ -35,12 +35,12 @@ type StateSelectorProps = {
/** object to get route details from */
stateSelectorRoute?: typeof ROUTES.SETTINGS_ADDRESS_STATE | typeof ROUTES.MONEY_REQUEST_STATE_SELECTOR;
-
- /** Reference to the outer element */
- ref?: ForwardedRef;
};
-function StateSelector({errorText, onBlur, value: stateCode, label, onInputChange, wrapperStyle, stateSelectorRoute = ROUTES.SETTINGS_ADDRESS_STATE, ref}: StateSelectorProps) {
+function StateSelector(
+ {errorText, onBlur, value: stateCode, label, onInputChange, wrapperStyle, stateSelectorRoute = ROUTES.SETTINGS_ADDRESS_STATE}: StateSelectorProps,
+ ref: ForwardedRef,
+) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {state: stateFromUrl} = useGeographicalStateAndCountryFromRoute();
@@ -100,6 +100,6 @@ function StateSelector({errorText, onBlur, value: stateCode, label, onInputChang
StateSelector.displayName = 'StateSelector';
-export default StateSelector;
+export default React.forwardRef(StateSelector);
export type {State};
diff --git a/src/components/TextLink.tsx b/src/components/TextLink.tsx
index 9dcb790a536e4..715d25e040a94 100644
--- a/src/components/TextLink.tsx
+++ b/src/components/TextLink.tsx
@@ -1,7 +1,7 @@
-import type {KeyboardEvent, KeyboardEventHandler, MouseEventHandler} from 'react';
-import React from 'react';
+import type {ForwardedRef, KeyboardEvent, KeyboardEventHandler, MouseEventHandler} from 'react';
+import React, {forwardRef} from 'react';
// eslint-disable-next-line no-restricted-imports
-import type {GestureResponderEvent, StyleProp, TextStyle} from 'react-native';
+import type {GestureResponderEvent, Text as RNText, StyleProp, TextStyle} from 'react-native';
import useEnvironment from '@hooks/useEnvironment';
import useThemeStyles from '@hooks/useThemeStyles';
import {openLink as openLinkUtil} from '@userActions/Link';
@@ -32,7 +32,7 @@ type TextLinkProps = (LinkProps | PressProps) &
onMouseDown?: MouseEventHandler;
};
-function TextLink({href, onPress, children, style, onMouseDown = (event) => event.preventDefault(), ref, ...rest}: TextLinkProps) {
+function TextLink({href, onPress, children, style, onMouseDown = (event) => event.preventDefault(), ...rest}: TextLinkProps, ref: ForwardedRef) {
const {environmentURL} = useEnvironment();
const styles = useThemeStyles();
@@ -81,4 +81,4 @@ TextLink.displayName = 'TextLink';
export type {LinkProps, PressProps, TextLinkProps};
-export default TextLink;
+export default forwardRef(TextLink);
diff --git a/src/components/TimeModalPicker.tsx b/src/components/TimeModalPicker.tsx
index fdd399b012006..be85fb7198391 100644
--- a/src/components/TimeModalPicker.tsx
+++ b/src/components/TimeModalPicker.tsx
@@ -1,4 +1,4 @@
-import React, {useState} from 'react';
+import React, {forwardRef, useState} from 'react';
import type {ForwardedRef} from 'react';
import {View} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';
@@ -22,12 +22,9 @@ type TimeModalPickerProps = {
/** Label for the picker */
label: string;
-
- /** Reference to the outer element */
- ref?: ForwardedRef;
};
-function TimeModalPicker({value, errorText, label, onInputChange = () => {}, ref}: TimeModalPickerProps) {
+function TimeModalPicker({value, errorText, label, onInputChange = () => {}}: TimeModalPickerProps, ref: ForwardedRef) {
const styles = useThemeStyles();
const [isPickerVisible, setIsPickerVisible] = useState(false);
const currentTime = value ? DateUtils.extractTime12Hour(value) : undefined;
@@ -84,4 +81,4 @@ function TimeModalPicker({value, errorText, label, onInputChange = () => {}, ref
}
TimeModalPicker.displayName = 'TimeModalPicker';
-export default TimeModalPicker;
+export default forwardRef(TimeModalPicker);
diff --git a/src/components/withToggleVisibilityView.tsx b/src/components/withToggleVisibilityView.tsx
index f9703eb34d246..809f2898aaa7a 100644
--- a/src/components/withToggleVisibilityView.tsx
+++ b/src/components/withToggleVisibilityView.tsx
@@ -1,4 +1,4 @@
-import type {ComponentType, ReactElement} from 'react';
+import type {ComponentType, ForwardedRef, ReactElement, RefAttributes} from 'react';
import React from 'react';
import {View} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';
@@ -9,8 +9,10 @@ type WithToggleVisibilityViewProps = {
isVisible?: boolean;
};
-export default function withToggleVisibilityView(WrappedComponent: ComponentType): (props: TProps & WithToggleVisibilityViewProps) => ReactElement | null {
- function WithToggleVisibilityView({isVisible = false, ...rest}: WithToggleVisibilityViewProps) {
+export default function withToggleVisibilityView(
+ WrappedComponent: ComponentType>,
+): (props: TProps & WithToggleVisibilityViewProps & RefAttributes) => ReactElement | null {
+ function WithToggleVisibilityView({isVisible = false, ...rest}: WithToggleVisibilityViewProps, ref: ForwardedRef) {
const styles = useThemeStyles();
return (
(WrappedComponent: Compo
@@ -27,7 +30,7 @@ export default function withToggleVisibilityView(WrappedComponent: Compo
}
WithToggleVisibilityView.displayName = `WithToggleVisibilityViewWithRef(${getComponentDisplayName(WrappedComponent)})`;
- return WithToggleVisibilityView;
+ return React.forwardRef(WithToggleVisibilityView);
}
export type {WithToggleVisibilityViewProps};
diff --git a/src/components/withViewportOffsetTop.tsx b/src/components/withViewportOffsetTop.tsx
index 51e56d447c0fb..d3e9b63ad3eec 100644
--- a/src/components/withViewportOffsetTop.tsx
+++ b/src/components/withViewportOffsetTop.tsx
@@ -1,5 +1,5 @@
-import type {ComponentType} from 'react';
-import React, {useEffect, useState} from 'react';
+import type {ComponentType, ForwardedRef, RefAttributes} from 'react';
+import React, {forwardRef, useEffect, useState} from 'react';
import getComponentDisplayName from '@libs/getComponentDisplayName';
import addViewportResizeListener from '@libs/VisualViewport';
@@ -9,8 +9,8 @@ type ViewportOffsetTopProps = {
viewportOffsetTop: number;
};
-export default function withViewportOffsetTop(WrappedComponent: ComponentType) {
- function WithViewportOffsetTop(props: Omit) {
+export default function withViewportOffsetTop(WrappedComponent: ComponentType>) {
+ function WithViewportOffsetTop(props: Omit, ref: ForwardedRef) {
const [viewportOffsetTop, setViewportOffsetTop] = useState(0);
useEffect(() => {
@@ -30,6 +30,7 @@ export default function withViewportOffsetTop
);
@@ -37,5 +38,5 @@ export default function withViewportOffsetTop