From 2997f6878e19dac95b7599930a17db099e8181b1 Mon Sep 17 00:00:00 2001
From: Viktoryia Kliushun <vikstash@gmail.com>
Date: Tue, 31 Oct 2023 17:50:42 +0100
Subject: [PATCH 01/19] [TS migration] Migrate 'Picker' and 'LocalePicker'
 components

---
 src/components/LocalePicker.js                |  66 ----
 src/components/LocalePicker.tsx               |  62 ++++
 src/components/Picker/BasePicker.js           | 299 ------------------
 src/components/Picker/BasePicker.tsx          | 210 ++++++++++++
 .../{index.native.js => index.native.tsx}     |   7 +-
 src/components/Picker/{index.js => index.tsx} |   9 +-
 src/components/Picker/types.ts                |  96 ++++++
 src/components/Text.tsx                       |   5 +
 8 files changed, 378 insertions(+), 376 deletions(-)
 delete mode 100644 src/components/LocalePicker.js
 create mode 100644 src/components/LocalePicker.tsx
 delete mode 100644 src/components/Picker/BasePicker.js
 create mode 100644 src/components/Picker/BasePicker.tsx
 rename src/components/Picker/{index.native.js => index.native.tsx} (59%)
 rename src/components/Picker/{index.js => index.tsx} (73%)
 create mode 100644 src/components/Picker/types.ts

diff --git a/src/components/LocalePicker.js b/src/components/LocalePicker.js
deleted file mode 100644
index 2c5a6e7b7ec6..000000000000
--- a/src/components/LocalePicker.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import {withOnyx} from 'react-native-onyx';
-import _ from 'underscore';
-import compose from '@libs/compose';
-import styles from '@styles/styles';
-import themeColors from '@styles/themes/default';
-import * as App from '@userActions/App';
-import CONST from '@src/CONST';
-import ONYXKEYS from '@src/ONYXKEYS';
-import Picker from './Picker';
-import withLocalize, {withLocalizePropTypes} from './withLocalize';
-
-const propTypes = {
-    /** Indicates which locale the user currently has selected */
-    preferredLocale: PropTypes.string,
-
-    /** Indicates size of a picker component and whether to render the label or not */
-    size: PropTypes.oneOf(['normal', 'small']),
-
-    ...withLocalizePropTypes,
-};
-
-const defaultProps = {
-    preferredLocale: CONST.LOCALES.DEFAULT,
-    size: 'normal',
-};
-
-function LocalePicker(props) {
-    const localesToLanguages = _.map(CONST.LANGUAGES, (language) => ({
-        value: language,
-        label: props.translate(`languagePage.languages.${language}.label`),
-        keyForList: language,
-        isSelected: props.preferredLocale === language,
-    }));
-    return (
-        <Picker
-            label={props.size === 'normal' ? props.translate('languagePage.language') : null}
-            onInputChange={(locale) => {
-                if (locale === props.preferredLocale) {
-                    return;
-                }
-
-                App.setLocale(locale);
-            }}
-            items={localesToLanguages}
-            size={props.size}
-            value={props.preferredLocale}
-            containerStyles={props.size === 'small' ? [styles.pickerContainerSmall] : []}
-            backgroundColor={themeColors.signInPage}
-        />
-    );
-}
-
-LocalePicker.defaultProps = defaultProps;
-LocalePicker.propTypes = propTypes;
-LocalePicker.displayName = 'LocalePicker';
-
-export default compose(
-    withLocalize,
-    withOnyx({
-        preferredLocale: {
-            key: ONYXKEYS.NVP_PREFERRED_LOCALE,
-        },
-    }),
-)(LocalePicker);
diff --git a/src/components/LocalePicker.tsx b/src/components/LocalePicker.tsx
new file mode 100644
index 000000000000..62dd3123bbce
--- /dev/null
+++ b/src/components/LocalePicker.tsx
@@ -0,0 +1,62 @@
+import React from 'react';
+import {OnyxEntry, withOnyx} from 'react-native-onyx';
+import {ValueOf} from 'type-fest';
+import compose from '@libs/compose';
+import styles from '@styles/styles';
+import themeColors from '@styles/themes/default';
+import * as App from '@userActions/App';
+import CONST from '@src/CONST';
+import ONYXKEYS from '@src/ONYXKEYS';
+import Picker from './Picker';
+import type {PickerSize} from './Picker/types';
+import withLocalize from './withLocalize';
+
+type LocalePickerOnyxProps = {
+    /** Indicates which locale the user currently has selected */
+    preferredLocale: OnyxEntry<ValueOf<typeof CONST.LOCALES>>;
+};
+
+type LocalePickerProps = LocalePickerOnyxProps & {
+    /** Indicates size of a picker component and whether to render the label or not */
+    size?: PickerSize;
+
+    /** Returns translated string for given locale and phrase */
+    translate: (phrase: string, variables?: Record<string, string>) => string;
+};
+
+function LocalePicker({preferredLocale = CONST.LOCALES.DEFAULT, size = 'normal', translate}: LocalePickerProps) {
+    const localesToLanguages = CONST.LANGUAGES.map((language) => ({
+        value: language,
+        label: translate(`languagePage.languages.${language}.label`),
+        keyForList: language,
+        isSelected: preferredLocale === language,
+    }));
+    return (
+        <Picker
+            label={size === 'normal' ? translate('languagePage.language') : null}
+            onInputChange={(locale) => {
+                if (locale === preferredLocale || typeof locale !== 'string') {
+                    return;
+                }
+
+                App.setLocale(locale);
+            }}
+            items={localesToLanguages}
+            size={size}
+            value={preferredLocale}
+            containerStyles={size === 'small' ? [styles.pickerContainerSmall] : []}
+            backgroundColor={themeColors.signInPage}
+        />
+    );
+}
+
+LocalePicker.displayName = 'LocalePicker';
+
+export default compose(
+    withLocalize,
+    withOnyx<LocalePickerProps, LocalePickerOnyxProps>({
+        preferredLocale: {
+            key: ONYXKEYS.NVP_PREFERRED_LOCALE,
+        },
+    }),
+)(LocalePicker);
diff --git a/src/components/Picker/BasePicker.js b/src/components/Picker/BasePicker.js
deleted file mode 100644
index 8e85eb66672a..000000000000
--- a/src/components/Picker/BasePicker.js
+++ /dev/null
@@ -1,299 +0,0 @@
-import PropTypes from 'prop-types';
-import React, {useContext, useEffect, useImperativeHandle, useRef, useState} from 'react';
-import {View} from 'react-native';
-import RNPickerSelect from 'react-native-picker-select';
-import _ from 'underscore';
-import FormHelpMessage from '@components/FormHelpMessage';
-import Icon from '@components/Icon';
-import * as Expensicons from '@components/Icon/Expensicons';
-import refPropTypes from '@components/refPropTypes';
-import {ScrollContext} from '@components/ScrollViewWithContext';
-import Text from '@components/Text';
-import styles from '@styles/styles';
-import themeColors from '@styles/themes/default';
-
-const propTypes = {
-    /** A forwarded ref */
-    forwardedRef: refPropTypes,
-
-    /** BasePicker label */
-    label: PropTypes.string,
-
-    /** Should the picker appear disabled? */
-    isDisabled: PropTypes.bool,
-
-    /** Input value */
-    value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
-
-    /** The items to display in the list of selections */
-    items: PropTypes.arrayOf(
-        PropTypes.shape({
-            /** The value of the item that is being selected */
-            value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
-
-            /** The text to display for the item */
-            label: PropTypes.string.isRequired,
-        }),
-    ).isRequired,
-
-    /** Something to show as the placeholder before something is selected */
-    placeholder: PropTypes.shape({
-        /** The value of the placeholder item, usually an empty string */
-        value: PropTypes.string,
-
-        /** The text to be displayed as the placeholder */
-        label: PropTypes.string,
-    }),
-
-    /** Error text to display */
-    errorText: PropTypes.string,
-
-    /** Customize the BasePicker container */
-    // eslint-disable-next-line react/forbid-prop-types
-    containerStyles: PropTypes.arrayOf(PropTypes.object),
-
-    /** Customize the BasePicker background color */
-    backgroundColor: PropTypes.string,
-
-    /** The ID used to uniquely identify the input in a Form */
-    inputID: PropTypes.string,
-
-    /** Saves a draft of the input value when used in a form */
-    // eslint-disable-next-line react/no-unused-prop-types
-    shouldSaveDraft: PropTypes.bool,
-
-    /** A callback method that is called when the value changes and it receives the selected value as an argument */
-    onInputChange: PropTypes.func.isRequired,
-
-    /** Size of a picker component */
-    size: PropTypes.oneOf(['normal', 'small']),
-
-    /** An icon to display with the picker */
-    icon: PropTypes.func,
-
-    /** Whether we should forward the focus/blur calls to the inner picker * */
-    shouldFocusPicker: PropTypes.bool,
-
-    /** Callback called when click or tap out of BasePicker */
-    onBlur: PropTypes.func,
-
-    /** Additional events passed to the core BasePicker for specific platforms such as web */
-    additionalPickerEvents: PropTypes.func,
-
-    /** Hint text that appears below the picker */
-    hintText: PropTypes.string,
-};
-
-const defaultProps = {
-    forwardedRef: undefined,
-    label: '',
-    isDisabled: false,
-    errorText: '',
-    hintText: '',
-    containerStyles: [],
-    backgroundColor: undefined,
-    inputID: undefined,
-    shouldSaveDraft: false,
-    value: undefined,
-    placeholder: {},
-    size: 'normal',
-    icon: (size) => (
-        <Icon
-            src={Expensicons.DownArrow}
-            // eslint-disable-next-line react/jsx-props-no-spreading
-            {...(size === 'small' ? {width: styles.pickerSmall().icon.width, height: styles.pickerSmall().icon.height} : {})}
-        />
-    ),
-    shouldFocusPicker: false,
-    onBlur: () => {},
-    additionalPickerEvents: () => {},
-};
-
-function BasePicker(props) {
-    const [isHighlighted, setIsHighlighted] = useState(false);
-
-    // reference to the root View
-    const root = useRef(null);
-
-    // reference to @react-native-picker/picker
-    const picker = useRef(null);
-
-    // Windows will reuse the text color of the select for each one of the options
-    // so we might need to color accordingly so it doesn't blend with the background.
-    const placeholder = _.isEmpty(props.placeholder)
-        ? {}
-        : {
-              ...props.placeholder,
-              color: themeColors.pickerOptionsTextColor,
-          };
-
-    useEffect(() => {
-        if (props.value || !props.items || props.items.length !== 1 || !props.onInputChange) {
-            return;
-        }
-
-        // When there is only 1 element in the selector, we do the user a favor and automatically select it for them
-        // so they don't have to spend extra time selecting the only possible value.
-        props.onInputChange(props.items[0].value, 0);
-
-        // eslint-disable-next-line react-hooks/exhaustive-deps
-    }, [props.items]);
-
-    const context = useContext(ScrollContext);
-
-    /**
-     * Forms use inputID to set values. But BasePicker passes an index as the second parameter to onInputChange
-     * We are overriding this behavior to make BasePicker work with Form
-     * @param {String} value
-     * @param {Number} index
-     */
-    const onInputChange = (value, index) => {
-        if (props.inputID) {
-            props.onInputChange(value);
-            return;
-        }
-
-        props.onInputChange(value, index);
-    };
-
-    const enableHighlight = () => {
-        setIsHighlighted(true);
-    };
-
-    const disableHighlight = () => {
-        setIsHighlighted(false);
-    };
-
-    useImperativeHandle(props.forwardedRef, () => ({
-        /**
-         * Focuses the picker (if configured to do so)
-         *
-         * This method is used by Form
-         */
-        focus() {
-            if (!props.shouldFocusPicker) {
-                return;
-            }
-
-            // Defer the focusing to work around a bug on Mobile Safari, where focusing the `select` element in the
-            // same task when we scrolled to it left that element in a glitched state, where the dropdown list can't
-            // be opened until the element gets re-focused
-            _.defer(() => {
-                picker.current.focus();
-            });
-        },
-
-        /**
-         * Like measure(), but measures the view relative to an ancestor
-         *
-         * This method is used by Form when scrolling to the input
-         *
-         * @param {Object} relativeToNativeComponentRef - reference to an ancestor
-         * @param {function(x: number, y: number, width: number, height: number): void} onSuccess - callback called on success
-         * @param {function(): void} onFail - callback called on failure
-         */
-        measureLayout(relativeToNativeComponentRef, onSuccess, onFail) {
-            if (!root.current) {
-                return;
-            }
-
-            root.current.measureLayout(relativeToNativeComponentRef, onSuccess, onFail);
-        },
-    }));
-
-    const hasError = !_.isEmpty(props.errorText);
-
-    if (props.isDisabled) {
-        return (
-            <View>
-                {Boolean(props.label) && (
-                    <Text
-                        style={[styles.textLabelSupporting, styles.mb1]}
-                        numberOfLines={1}
-                    >
-                        {props.label}
-                    </Text>
-                )}
-                <Text numberOfLines={1}>{props.value}</Text>
-                {Boolean(props.hintText) && <Text style={[styles.textLabel, styles.colorMuted, styles.mt2]}>{props.hintText}</Text>}
-            </View>
-        );
-    }
-
-    return (
-        <>
-            <View
-                ref={root}
-                style={[
-                    styles.pickerContainer,
-                    props.isDisabled && styles.inputDisabled,
-                    ...props.containerStyles,
-                    isHighlighted && styles.borderColorFocus,
-                    hasError && styles.borderColorDanger,
-                ]}
-            >
-                {props.label && (
-                    <Text
-                        pointerEvents="none"
-                        style={[styles.pickerLabel, styles.textLabelSupporting]}
-                    >
-                        {props.label}
-                    </Text>
-                )}
-                <RNPickerSelect
-                    onValueChange={onInputChange}
-                    // We add a text color to prevent white text on white background dropdown items on Windows
-                    items={_.map(props.items, (item) => ({...item, color: themeColors.pickerOptionsTextColor}))}
-                    style={props.size === 'normal' ? styles.picker(props.isDisabled, props.backgroundColor) : styles.pickerSmall(props.backgroundColor)}
-                    useNativeAndroidPickerStyle={false}
-                    placeholder={placeholder}
-                    value={props.value}
-                    Icon={() => props.icon(props.size)}
-                    disabled={props.isDisabled}
-                    fixAndroidTouchableBug
-                    onOpen={enableHighlight}
-                    onClose={disableHighlight}
-                    textInputProps={{
-                        allowFontScaling: false,
-                    }}
-                    pickerProps={{
-                        ref: picker,
-                        tabIndex: -1,
-                        onFocus: enableHighlight,
-                        onBlur: () => {
-                            disableHighlight();
-                            props.onBlur();
-                        },
-                        ...props.additionalPickerEvents(enableHighlight, (value, index) => {
-                            onInputChange(value, index);
-                            disableHighlight();
-                        }),
-                    }}
-                    scrollViewRef={context && context.scrollViewRef}
-                    scrollViewContentOffsetY={context && context.contentOffsetY}
-                />
-            </View>
-            <FormHelpMessage message={props.errorText} />
-            {Boolean(props.hintText) && <Text style={[styles.textLabel, styles.colorMuted, styles.mt2]}>{props.hintText}</Text>}
-        </>
-    );
-}
-
-BasePicker.propTypes = propTypes;
-BasePicker.defaultProps = defaultProps;
-BasePicker.displayName = 'BasePicker';
-
-const BasePickerWithRef = React.forwardRef((props, ref) => (
-    <BasePicker
-        // eslint-disable-next-line react/jsx-props-no-spreading
-        {...props}
-        // Forward the ref to BasePicker, as we implement imperative methods there
-        forwardedRef={ref}
-        // eslint-disable-next-line react/prop-types
-        key={props.inputID}
-    />
-));
-
-BasePickerWithRef.displayName = 'BasePickerWithRef';
-
-export default BasePickerWithRef;
diff --git a/src/components/Picker/BasePicker.tsx b/src/components/Picker/BasePicker.tsx
new file mode 100644
index 000000000000..96521fd11bbe
--- /dev/null
+++ b/src/components/Picker/BasePicker.tsx
@@ -0,0 +1,210 @@
+import {Picker} from '@react-native-picker/picker';
+import lodashDefer from 'lodash/defer';
+import React, {useContext, useEffect, useImperativeHandle, useRef, useState} from 'react';
+import {View} from 'react-native';
+import RNPickerSelect from 'react-native-picker-select';
+import FormHelpMessage from '@components/FormHelpMessage';
+import Icon from '@components/Icon';
+import * as Expensicons from '@components/Icon/Expensicons';
+import {ScrollContext} from '@components/ScrollViewWithContext';
+import Text from '@components/Text';
+import styles from '@styles/styles';
+import themeColors from '@styles/themes/default';
+import type {BasePickerHandle, BasePickerProps} from './types';
+
+function BasePicker({
+    items,
+    forwardedRef,
+    backgroundColor,
+    inputID,
+    value,
+    onInputChange,
+    label = '',
+    isDisabled = false,
+    errorText = '',
+    hintText = '',
+    containerStyles = [],
+    placeholder = {},
+    size = 'normal',
+    icon = (iconSize) => (
+        <Icon
+            src={Expensicons.DownArrow}
+            // eslint-disable-next-line react/jsx-props-no-spreading
+            {...(iconSize === 'small' ? {width: styles.pickerSmall().icon.width, height: styles.pickerSmall().icon.height} : {})}
+        />
+    ),
+    shouldFocusPicker = false,
+    onBlur = () => {},
+    additionalPickerEvents = () => {},
+}: BasePickerProps) {
+    const [isHighlighted, setIsHighlighted] = useState(false);
+
+    // reference to the root View
+    const root = useRef<View>(null);
+
+    // reference to @react-native-picker/picker
+    const picker = useRef<Picker<BasePickerProps>>(null);
+
+    // Windows will reuse the text color of the select for each one of the options
+    // so we might need to color accordingly so it doesn't blend with the background.
+    const pickerPlaceholder = Object.keys(placeholder).length ? {...placeholder, color: themeColors.pickerOptionsTextColor} : {};
+
+    useEffect(() => {
+        if (!!value || !items || items.length !== 1 || !onInputChange) {
+            return;
+        }
+
+        // When there is only 1 element in the selector, we do the user a favor and automatically select it for them
+        // so they don't have to spend extra time selecting the only possible value.
+        onInputChange(items[0].value, 0);
+
+        // eslint-disable-next-line react-hooks/exhaustive-deps
+    }, [items]);
+
+    const context = useContext(ScrollContext);
+
+    /**
+     * Forms use inputID to set values. But BasePicker passes an index as the second parameter to onValueChange
+     * We are overriding this behavior to make BasePicker work with Form
+     */
+    const onValueChange = (inputValue: string, index: number) => {
+        if (inputID) {
+            onInputChange(inputValue);
+            return;
+        }
+
+        onInputChange(inputValue, index);
+    };
+
+    const enableHighlight = () => {
+        setIsHighlighted(true);
+    };
+
+    const disableHighlight = () => {
+        setIsHighlighted(false);
+    };
+
+    useImperativeHandle(forwardedRef, () => ({
+        /**
+         * Focuses the picker (if configured to do so)
+         *
+         * This method is used by Form
+         */
+        focus() {
+            if (!shouldFocusPicker) {
+                return;
+            }
+
+            // Defer the focusing to work around a bug on Mobile Safari, where focusing the `select` element in the
+            // same task when we scrolled to it left that element in a glitched state, where the dropdown list can't
+            // be opened until the element gets re-focused
+            lodashDefer(() => {
+                picker.current?.focus();
+            });
+        },
+
+        /**
+         * Like measure(), but measures the view relative to an ancestor
+         *
+         * This method is used by Form when scrolling to the input
+         *
+         * @param relativeToNativeComponentRef - reference to an ancestor
+         * @param onSuccess - callback called on success
+         * @param onFail - callback called on failure
+         */
+        measureLayout(relativeToNativeComponentRef, onSuccess, onFail) {
+            if (!root.current) {
+                return;
+            }
+
+            root.current.measureLayout(relativeToNativeComponentRef, onSuccess, onFail);
+        },
+    }));
+
+    const hasError = !!errorText;
+
+    if (isDisabled) {
+        return (
+            <View>
+                {Boolean(label) && (
+                    <Text
+                        style={[styles.textLabelSupporting, styles.mb1]}
+                        numberOfLines={1}
+                    >
+                        {label}
+                    </Text>
+                )}
+                <Text numberOfLines={1}>{value}</Text>
+                {Boolean(hintText) && <Text style={[styles.textLabel, styles.colorMuted, styles.mt2]}>{hintText}</Text>}
+            </View>
+        );
+    }
+
+    return (
+        <>
+            <View
+                ref={root}
+                style={[styles.pickerContainer, isDisabled && styles.inputDisabled, ...containerStyles, isHighlighted && styles.borderColorFocus, hasError && styles.borderColorDanger]}
+            >
+                {label && (
+                    <Text
+                        pointerEvents="none"
+                        style={[styles.pickerLabel, styles.textLabelSupporting]}
+                    >
+                        {label}
+                    </Text>
+                )}
+                <RNPickerSelect
+                    onValueChange={onValueChange}
+                    // We add a text color to prevent white text on white background dropdown items on Windows
+                    items={items.map((item) => ({...item, color: themeColors.pickerOptionsTextColor}))}
+                    style={size === 'normal' ? styles.picker(isDisabled, backgroundColor) : styles.pickerSmall(backgroundColor)}
+                    useNativeAndroidPickerStyle={false}
+                    placeholder={pickerPlaceholder}
+                    value={value}
+                    // TODO: update the icon logic to escape ts error
+                    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+                    // @ts-ignore
+                    Icon={() => icon(size)}
+                    disabled={isDisabled}
+                    fixAndroidTouchableBug
+                    onOpen={enableHighlight}
+                    onClose={disableHighlight}
+                    textInputProps={{
+                        allowFontScaling: false,
+                    }}
+                    pickerProps={{
+                        // TODO: resolve 'ref does not exist in type CustomPickerProps' ts error
+                        // ref: picker,
+                        tabIndex: -1,
+                        onFocus: enableHighlight,
+                        onBlur: () => {
+                            disableHighlight();
+                            onBlur();
+                        },
+                        ...additionalPickerEvents(enableHighlight, (inputValue, index) => {
+                            onValueChange(inputValue, index);
+                            disableHighlight();
+                        }),
+                    }}
+                    scrollViewRef={context?.scrollViewRef}
+                    scrollViewContentOffsetY={context?.contentOffsetY}
+                />
+            </View>
+            <FormHelpMessage message={errorText} />
+            {Boolean(hintText) && <Text style={[styles.textLabel, styles.colorMuted, styles.mt2]}>{hintText}</Text>}
+        </>
+    );
+}
+
+BasePicker.displayName = 'BasePicker';
+
+export default React.forwardRef<BasePickerHandle, BasePickerProps>((props, ref) => (
+    <BasePicker
+        // eslint-disable-next-line react/jsx-props-no-spreading
+        {...props}
+        // Forward the ref to BasePicker, as we implement imperative methods there
+        forwardedRef={ref}
+        key={props.inputID}
+    />
+));
diff --git a/src/components/Picker/index.native.js b/src/components/Picker/index.native.tsx
similarity index 59%
rename from src/components/Picker/index.native.js
rename to src/components/Picker/index.native.tsx
index f441609fd4d0..f974130a291a 100644
--- a/src/components/Picker/index.native.js
+++ b/src/components/Picker/index.native.tsx
@@ -1,14 +1,11 @@
 import React, {forwardRef} from 'react';
 import BasePicker from './BasePicker';
+import {BasePickerHandle, BasePickerProps} from './types';
 
-const BasePickerWithRef = forwardRef((props, ref) => (
+export default forwardRef<BasePickerHandle, BasePickerProps>((props, ref) => (
     <BasePicker
         // eslint-disable-next-line react/jsx-props-no-spreading
         {...props}
         ref={ref}
     />
 ));
-
-BasePickerWithRef.displayName = 'BasePickerWithRef';
-
-export default BasePickerWithRef;
diff --git a/src/components/Picker/index.js b/src/components/Picker/index.tsx
similarity index 73%
rename from src/components/Picker/index.js
rename to src/components/Picker/index.tsx
index 8e49a42e8932..e3504bf4842c 100644
--- a/src/components/Picker/index.js
+++ b/src/components/Picker/index.tsx
@@ -1,7 +1,8 @@
 import React, {forwardRef} from 'react';
 import BasePicker from './BasePicker';
+import type {AdditionalPickerEvents, BasePickerHandle, BasePickerProps, OnChange, OnMouseDown} from './types';
 
-const additionalPickerEvents = (onMouseDown, onChange) => ({
+const additionalPickerEvents = (onMouseDown: OnMouseDown, onChange: OnChange): AdditionalPickerEvents => ({
     onMouseDown,
     onChange: (e) => {
         if (e.target.selectedIndex === undefined) {
@@ -13,7 +14,7 @@ const additionalPickerEvents = (onMouseDown, onChange) => ({
     },
 });
 
-const BasePickerWithRef = forwardRef((props, ref) => (
+export default forwardRef<BasePickerHandle, BasePickerProps>((props, ref) => (
     <BasePicker
         // eslint-disable-next-line react/jsx-props-no-spreading
         {...props}
@@ -26,7 +27,3 @@ const BasePickerWithRef = forwardRef((props, ref) => (
         additionalPickerEvents={additionalPickerEvents}
     />
 ));
-
-BasePickerWithRef.displayName = 'BasePickerWithRef';
-
-export default BasePickerWithRef;
diff --git a/src/components/Picker/types.ts b/src/components/Picker/types.ts
new file mode 100644
index 000000000000..53f45f23a0ca
--- /dev/null
+++ b/src/components/Picker/types.ts
@@ -0,0 +1,96 @@
+import {ChangeEvent, Component, ForwardedRef, ReactElement} from 'react';
+import {MeasureLayoutOnSuccessCallback, NativeMethods, ViewStyle} from 'react-native';
+
+type MeasureLayoutOnFailCallback = () => void;
+
+type RelativeToNativeComponentRef = (Component<unknown> & Readonly<NativeMethods>) | number;
+
+type BasePickerHandle = {
+    focus: () => void;
+    measureLayout: (relativeToNativeComponentRef: RelativeToNativeComponentRef, onSuccess: MeasureLayoutOnSuccessCallback, onFail: MeasureLayoutOnFailCallback) => void;
+};
+
+type OnMouseDown = () => void;
+
+type OnChange = (value: string, index: number) => void;
+
+type AdditionalPickerEvents = {
+    onMouseDown?: OnMouseDown;
+    onChange?: (event: ChangeEvent<HTMLSelectElement>) => void;
+};
+
+type AdditionalPickerEventsCallback = (onMouseDown: OnMouseDown, onChange: OnChange) => AdditionalPickerEvents;
+
+type DefaultPickerEventsCallback = () => void;
+
+type PickerSize = 'normal' | 'small';
+
+type PickerItem = {
+    /** The value of the item that is being selected */
+    value: string | number;
+
+    /** The text to display for the item */
+    label: string;
+};
+
+type PickerPlaceholder = {
+    /** The value of the placeholder item, usually an empty string */
+    value?: string;
+
+    /** The text to be displayed as the placeholder */
+    label?: string;
+};
+
+type BasePickerProps = {
+    forwardedRef?: ForwardedRef<BasePickerHandle>;
+
+    /** BasePicker label */
+    label?: string | null;
+
+    /** Should the picker appear disabled? */
+    isDisabled?: boolean;
+
+    /** Input value */
+    value?: string | null | number;
+
+    /** The items to display in the list of selections */
+    items: PickerItem[];
+
+    /** Something to show as the placeholder before something is selected */
+    placeholder?: PickerPlaceholder;
+
+    /** Error text to display */
+    errorText?: string;
+
+    /** Customize the BasePicker container */
+    containerStyles?: ViewStyle[];
+
+    /** Customize the BasePicker background color */
+    backgroundColor?: string;
+
+    /** The ID used to uniquely identify the input in a Form */
+    inputID?: string;
+
+    /** A callback method that is called when the value changes and it receives the selected value as an argument */
+    onInputChange: (value: string | number, index?: number) => void;
+
+    /** Size of a picker component */
+    size?: PickerSize;
+
+    /** An icon to display with the picker */
+    icon?: (size: PickerSize) => ReactElement;
+
+    /** Whether we should forward the focus/blur calls to the inner picker * */
+    shouldFocusPicker?: boolean;
+
+    /** Callback called when click or tap out of BasePicker */
+    onBlur?: () => void;
+
+    /** Additional events passed to the core BasePicker for specific platforms such as web */
+    additionalPickerEvents?: AdditionalPickerEventsCallback | DefaultPickerEventsCallback;
+
+    /** Hint text that appears below the picker */
+    hintText?: string;
+};
+
+export type {BasePickerHandle, BasePickerProps, AdditionalPickerEventsCallback, PickerSize, AdditionalPickerEvents, OnMouseDown, OnChange};
diff --git a/src/components/Text.tsx b/src/components/Text.tsx
index 80181156ee3a..a0aa90cb502d 100644
--- a/src/components/Text.tsx
+++ b/src/components/Text.tsx
@@ -12,13 +12,18 @@ type TextProps = RNTextProps & {
 
     /** The size of the text */
     fontSize?: number;
+
     /** The alignment of the text */
     textAlign?: 'left' | 'right' | 'auto' | 'center' | 'justify';
+
     /** Any children to display */
     children: React.ReactNode;
 
     /** The family of the font to use */
     family?: keyof typeof fontFamily;
+
+    /** Pointer events property to the Text element */
+    pointerEvents?: 'box-none' | 'none' | 'box-only' | 'auto';
 };
 
 function Text(

From 25f87b641f7b80deb4b86db6e94200ca9cb1b5b3 Mon Sep 17 00:00:00 2001
From: Viktoryia Kliushun <vikstash@gmail.com>
Date: Tue, 31 Oct 2023 18:00:24 +0100
Subject: [PATCH 02/19] Update picker style type

---
 src/components/LocalePicker.tsx      | 2 +-
 src/components/Picker/BasePicker.tsx | 2 +-
 src/components/Picker/types.ts       | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/components/LocalePicker.tsx b/src/components/LocalePicker.tsx
index 62dd3123bbce..a531674003bd 100644
--- a/src/components/LocalePicker.tsx
+++ b/src/components/LocalePicker.tsx
@@ -44,7 +44,7 @@ function LocalePicker({preferredLocale = CONST.LOCALES.DEFAULT, size = 'normal',
             items={localesToLanguages}
             size={size}
             value={preferredLocale}
-            containerStyles={size === 'small' ? [styles.pickerContainerSmall] : []}
+            containerStyles={size === 'small' ? styles.pickerContainerSmall : {}}
             backgroundColor={themeColors.signInPage}
         />
     );
diff --git a/src/components/Picker/BasePicker.tsx b/src/components/Picker/BasePicker.tsx
index 96521fd11bbe..956d3a08e6d9 100644
--- a/src/components/Picker/BasePicker.tsx
+++ b/src/components/Picker/BasePicker.tsx
@@ -144,7 +144,7 @@ function BasePicker({
         <>
             <View
                 ref={root}
-                style={[styles.pickerContainer, isDisabled && styles.inputDisabled, ...containerStyles, isHighlighted && styles.borderColorFocus, hasError && styles.borderColorDanger]}
+                style={[styles.pickerContainer, isDisabled && styles.inputDisabled, containerStyles, isHighlighted && styles.borderColorFocus, hasError && styles.borderColorDanger]}
             >
                 {label && (
                     <Text
diff --git a/src/components/Picker/types.ts b/src/components/Picker/types.ts
index 53f45f23a0ca..56ffc16e8b39 100644
--- a/src/components/Picker/types.ts
+++ b/src/components/Picker/types.ts
@@ -1,5 +1,5 @@
 import {ChangeEvent, Component, ForwardedRef, ReactElement} from 'react';
-import {MeasureLayoutOnSuccessCallback, NativeMethods, ViewStyle} from 'react-native';
+import {MeasureLayoutOnSuccessCallback, NativeMethods, StyleProp, ViewStyle} from 'react-native';
 
 type MeasureLayoutOnFailCallback = () => void;
 
@@ -63,7 +63,7 @@ type BasePickerProps = {
     errorText?: string;
 
     /** Customize the BasePicker container */
-    containerStyles?: ViewStyle[];
+    containerStyles?: StyleProp<ViewStyle>;
 
     /** Customize the BasePicker background color */
     backgroundColor?: string;

From 80816b6bbf91fb1f12b419645673423b85b1fe35 Mon Sep 17 00:00:00 2001
From: Viktoryia Kliushun <vikstash@gmail.com>
Date: Thu, 2 Nov 2023 19:43:29 +0300
Subject: [PATCH 03/19] Update TODO messages

---
 src/components/Picker/BasePicker.tsx | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/components/Picker/BasePicker.tsx b/src/components/Picker/BasePicker.tsx
index 956d3a08e6d9..3b49c727e83e 100644
--- a/src/components/Picker/BasePicker.tsx
+++ b/src/components/Picker/BasePicker.tsx
@@ -162,7 +162,7 @@ function BasePicker({
                     useNativeAndroidPickerStyle={false}
                     placeholder={pickerPlaceholder}
                     value={value}
-                    // TODO: update the icon logic to escape ts error
+                    // TODO: update the icon type in the lib to escape ts error
                     // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                     // @ts-ignore
                     Icon={() => icon(size)}
@@ -187,6 +187,7 @@ function BasePicker({
                             disableHighlight();
                         }),
                     }}
+                    // TODO: for some reasons scrollViewRef and scrollViewContentOffsetY are not found in RNPickerSelect props, need to do updates on the lib side
                     scrollViewRef={context?.scrollViewRef}
                     scrollViewContentOffsetY={context?.contentOffsetY}
                 />

From 4a1773e33150510175886cd0c40f6e20d9c79554 Mon Sep 17 00:00:00 2001
From: Viktoryia Kliushun <vikstash@gmail.com>
Date: Thu, 2 Nov 2023 20:17:51 +0300
Subject: [PATCH 04/19] Update react-native-picker-select lib version to
 resolve ts issues

---
 package-lock.json                    | 14 +++++++-------
 package.json                         |  2 +-
 src/components/Picker/BasePicker.tsx |  1 -
 3 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 690e658fa476..28946fa3e429 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -99,7 +99,7 @@
         "react-native-pdf": "^6.7.1",
         "react-native-performance": "^5.1.0",
         "react-native-permissions": "^3.9.3",
-        "react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#eae05855286dc699954415cc1d629bfd8e8e47e2",
+        "react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#83c7f0ea8ee90932b57a7d9beb85ecb93d5ef03c",
         "react-native-plaid-link-sdk": "^10.0.0",
         "react-native-qrcode-svg": "^6.2.0",
         "react-native-quick-sqlite": "^8.0.0-beta.2",
@@ -44896,9 +44896,9 @@
       }
     },
     "node_modules/react-native-picker-select": {
-      "version": "8.0.4",
-      "resolved": "git+ssh://git@github.com/Expensify/react-native-picker-select.git#eae05855286dc699954415cc1d629bfd8e8e47e2",
-      "integrity": "sha512-3U/mtHN/pKC5yXtJnqj5rre8+4YPSqoXCn/3qKjb5u8BMIiuc5H3KJ0ZbKlZEg/8Uh4j0cvrtcNasdPgMqRgCQ==",
+      "version": "8.1.0",
+      "resolved": "git+ssh://git@github.com/Expensify/react-native-picker-select.git#83c7f0ea8ee90932b57a7d9beb85ecb93d5ef03c",
+      "integrity": "sha512-3QMmP/fjkq6+AaNXR09SCGajcbC11lVgtIvpuLoJHyI66Btj9/CSe19T7nvSVL4q2mQLKrGtpfwuhYIREzXWgw==",
       "license": "MIT",
       "dependencies": {
         "lodash.isequal": "^4.5.0"
@@ -85483,9 +85483,9 @@
       "requires": {}
     },
     "react-native-picker-select": {
-      "version": "git+ssh://git@github.com/Expensify/react-native-picker-select.git#eae05855286dc699954415cc1d629bfd8e8e47e2",
-      "integrity": "sha512-3U/mtHN/pKC5yXtJnqj5rre8+4YPSqoXCn/3qKjb5u8BMIiuc5H3KJ0ZbKlZEg/8Uh4j0cvrtcNasdPgMqRgCQ==",
-      "from": "react-native-picker-select@git+https://github.com/Expensify/react-native-picker-select.git#eae05855286dc699954415cc1d629bfd8e8e47e2",
+      "version": "git+ssh://git@github.com/Expensify/react-native-picker-select.git#83c7f0ea8ee90932b57a7d9beb85ecb93d5ef03c",
+      "integrity": "sha512-3QMmP/fjkq6+AaNXR09SCGajcbC11lVgtIvpuLoJHyI66Btj9/CSe19T7nvSVL4q2mQLKrGtpfwuhYIREzXWgw==",
+      "from": "react-native-picker-select@git+https://github.com/Expensify/react-native-picker-select.git#83c7f0ea8ee90932b57a7d9beb85ecb93d5ef03c",
       "requires": {
         "lodash.isequal": "^4.5.0"
       }
diff --git a/package.json b/package.json
index a8143fd2a809..6133f0386709 100644
--- a/package.json
+++ b/package.json
@@ -146,7 +146,7 @@
     "react-native-pdf": "^6.7.1",
     "react-native-performance": "^5.1.0",
     "react-native-permissions": "^3.9.3",
-    "react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#eae05855286dc699954415cc1d629bfd8e8e47e2",
+    "react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#83c7f0ea8ee90932b57a7d9beb85ecb93d5ef03c",
     "react-native-plaid-link-sdk": "^10.0.0",
     "react-native-qrcode-svg": "^6.2.0",
     "react-native-quick-sqlite": "^8.0.0-beta.2",
diff --git a/src/components/Picker/BasePicker.tsx b/src/components/Picker/BasePicker.tsx
index 3b49c727e83e..e510ac485c33 100644
--- a/src/components/Picker/BasePicker.tsx
+++ b/src/components/Picker/BasePicker.tsx
@@ -187,7 +187,6 @@ function BasePicker({
                             disableHighlight();
                         }),
                     }}
-                    // TODO: for some reasons scrollViewRef and scrollViewContentOffsetY are not found in RNPickerSelect props, need to do updates on the lib side
                     scrollViewRef={context?.scrollViewRef}
                     scrollViewContentOffsetY={context?.contentOffsetY}
                 />

From a8b8188970d8d35dba7c53001136be8419e6be3f Mon Sep 17 00:00:00 2001
From: Viktoryia Kliushun <vikstash@gmail.com>
Date: Mon, 6 Nov 2023 14:19:04 +0100
Subject: [PATCH 05/19] Update react-native-picker-select lib to fix TS issues

---
 package-lock.json                    | 12 ++++++------
 package.json                         |  2 +-
 src/components/Picker/BasePicker.tsx |  9 ++-------
 3 files changed, 9 insertions(+), 14 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 778dd98b648d..409bf4cfc82e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -101,7 +101,7 @@
         "react-native-pdf": "^6.7.1",
         "react-native-performance": "^5.1.0",
         "react-native-permissions": "^3.9.3",
-        "react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#83c7f0ea8ee90932b57a7d9beb85ecb93d5ef03c",
+        "react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#0d15d4618f58e99c1261921111e68ee85bb3c2a8",
         "react-native-plaid-link-sdk": "^10.0.0",
         "react-native-qrcode-svg": "^6.2.0",
         "react-native-quick-sqlite": "^8.0.0-beta.2",
@@ -44896,8 +44896,8 @@
     },
     "node_modules/react-native-picker-select": {
       "version": "8.1.0",
-      "resolved": "git+ssh://git@github.com/Expensify/react-native-picker-select.git#83c7f0ea8ee90932b57a7d9beb85ecb93d5ef03c",
-      "integrity": "sha512-3QMmP/fjkq6+AaNXR09SCGajcbC11lVgtIvpuLoJHyI66Btj9/CSe19T7nvSVL4q2mQLKrGtpfwuhYIREzXWgw==",
+      "resolved": "git+ssh://git@github.com/Expensify/react-native-picker-select.git#0d15d4618f58e99c1261921111e68ee85bb3c2a8",
+      "integrity": "sha512-ly0ZCt3K4RX7t9lfSb2OSGAw0cv8UqdMoxNfh5j+KujYYq+N8VsI9O/lmqquNeX/AMp5hM3fjetEWue4nZw/hA==",
       "license": "MIT",
       "dependencies": {
         "lodash.isequal": "^4.5.0"
@@ -85460,9 +85460,9 @@
       "requires": {}
     },
     "react-native-picker-select": {
-      "version": "git+ssh://git@github.com/Expensify/react-native-picker-select.git#83c7f0ea8ee90932b57a7d9beb85ecb93d5ef03c",
-      "integrity": "sha512-3QMmP/fjkq6+AaNXR09SCGajcbC11lVgtIvpuLoJHyI66Btj9/CSe19T7nvSVL4q2mQLKrGtpfwuhYIREzXWgw==",
-      "from": "react-native-picker-select@git+https://github.com/Expensify/react-native-picker-select.git#83c7f0ea8ee90932b57a7d9beb85ecb93d5ef03c",
+      "version": "git+ssh://git@github.com/Expensify/react-native-picker-select.git#0d15d4618f58e99c1261921111e68ee85bb3c2a8",
+      "integrity": "sha512-ly0ZCt3K4RX7t9lfSb2OSGAw0cv8UqdMoxNfh5j+KujYYq+N8VsI9O/lmqquNeX/AMp5hM3fjetEWue4nZw/hA==",
+      "from": "react-native-picker-select@git+https://github.com/Expensify/react-native-picker-select.git#0d15d4618f58e99c1261921111e68ee85bb3c2a8",
       "requires": {
         "lodash.isequal": "^4.5.0"
       }
diff --git a/package.json b/package.json
index 94b94019abff..fa70af341e25 100644
--- a/package.json
+++ b/package.json
@@ -149,7 +149,7 @@
     "react-native-pdf": "^6.7.1",
     "react-native-performance": "^5.1.0",
     "react-native-permissions": "^3.9.3",
-    "react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#83c7f0ea8ee90932b57a7d9beb85ecb93d5ef03c",
+    "react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#0d15d4618f58e99c1261921111e68ee85bb3c2a8",
     "react-native-plaid-link-sdk": "^10.0.0",
     "react-native-qrcode-svg": "^6.2.0",
     "react-native-quick-sqlite": "^8.0.0-beta.2",
diff --git a/src/components/Picker/BasePicker.tsx b/src/components/Picker/BasePicker.tsx
index e510ac485c33..41fd16319813 100644
--- a/src/components/Picker/BasePicker.tsx
+++ b/src/components/Picker/BasePicker.tsx
@@ -1,4 +1,3 @@
-import {Picker} from '@react-native-picker/picker';
 import lodashDefer from 'lodash/defer';
 import React, {useContext, useEffect, useImperativeHandle, useRef, useState} from 'react';
 import {View} from 'react-native';
@@ -43,7 +42,7 @@ function BasePicker({
     const root = useRef<View>(null);
 
     // reference to @react-native-picker/picker
-    const picker = useRef<Picker<BasePickerProps>>(null);
+    const picker = useRef<RNPickerSelect>(null);
 
     // Windows will reuse the text color of the select for each one of the options
     // so we might need to color accordingly so it doesn't blend with the background.
@@ -162,9 +161,6 @@ function BasePicker({
                     useNativeAndroidPickerStyle={false}
                     placeholder={pickerPlaceholder}
                     value={value}
-                    // TODO: update the icon type in the lib to escape ts error
-                    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
-                    // @ts-ignore
                     Icon={() => icon(size)}
                     disabled={isDisabled}
                     fixAndroidTouchableBug
@@ -174,8 +170,7 @@ function BasePicker({
                         allowFontScaling: false,
                     }}
                     pickerProps={{
-                        // TODO: resolve 'ref does not exist in type CustomPickerProps' ts error
-                        // ref: picker,
+                        ref: picker,
                         tabIndex: -1,
                         onFocus: enableHighlight,
                         onBlur: () => {

From e3769180841e2b1d8eb6d0172207d5831a9d67f9 Mon Sep 17 00:00:00 2001
From: Viktoryia Kliushun <vikstash@gmail.com>
Date: Thu, 9 Nov 2023 12:00:59 +0100
Subject: [PATCH 06/19] Update forwardRef logic

---
 src/components/Picker/BasePicker.tsx   | 81 +++++++++++---------------
 src/components/Picker/index.native.tsx | 20 ++++---
 src/components/Picker/index.tsx        | 32 +++++-----
 src/components/Picker/types.ts         |  4 +-
 4 files changed, 65 insertions(+), 72 deletions(-)

diff --git a/src/components/Picker/BasePicker.tsx b/src/components/Picker/BasePicker.tsx
index 41fd16319813..aba14d3b74a2 100644
--- a/src/components/Picker/BasePicker.tsx
+++ b/src/components/Picker/BasePicker.tsx
@@ -1,5 +1,5 @@
 import lodashDefer from 'lodash/defer';
-import React, {useContext, useEffect, useImperativeHandle, useRef, useState} from 'react';
+import React, {ForwardedRef, forwardRef, useContext, useEffect, useImperativeHandle, useRef, useState} from 'react';
 import {View} from 'react-native';
 import RNPickerSelect from 'react-native-picker-select';
 import FormHelpMessage from '@components/FormHelpMessage';
@@ -11,31 +11,33 @@ import styles from '@styles/styles';
 import themeColors from '@styles/themes/default';
 import type {BasePickerHandle, BasePickerProps} from './types';
 
-function BasePicker({
-    items,
-    forwardedRef,
-    backgroundColor,
-    inputID,
-    value,
-    onInputChange,
-    label = '',
-    isDisabled = false,
-    errorText = '',
-    hintText = '',
-    containerStyles = [],
-    placeholder = {},
-    size = 'normal',
-    icon = (iconSize) => (
-        <Icon
-            src={Expensicons.DownArrow}
-            // eslint-disable-next-line react/jsx-props-no-spreading
-            {...(iconSize === 'small' ? {width: styles.pickerSmall().icon.width, height: styles.pickerSmall().icon.height} : {})}
-        />
-    ),
-    shouldFocusPicker = false,
-    onBlur = () => {},
-    additionalPickerEvents = () => {},
-}: BasePickerProps) {
+function BasePicker(
+    {
+        items,
+        backgroundColor,
+        inputID,
+        value,
+        onInputChange,
+        label = '',
+        isDisabled = false,
+        errorText = '',
+        hintText = '',
+        containerStyles = [],
+        placeholder = {},
+        size = 'normal',
+        icon = (iconSize) => (
+            <Icon
+                src={Expensicons.DownArrow}
+                // eslint-disable-next-line react/jsx-props-no-spreading
+                {...(iconSize === 'small' ? {width: styles.pickerSmall().icon.width, height: styles.pickerSmall().icon.height} : {})}
+            />
+        ),
+        shouldFocusPicker = false,
+        onBlur = () => {},
+        additionalPickerEvents = () => {},
+    }: BasePickerProps,
+    ref: ForwardedRef<BasePickerHandle>,
+) {
     const [isHighlighted, setIsHighlighted] = useState(false);
 
     // reference to the root View
@@ -83,7 +85,7 @@ function BasePicker({
         setIsHighlighted(false);
     };
 
-    useImperativeHandle(forwardedRef, () => ({
+    useImperativeHandle(ref, () => ({
         /**
          * Focuses the picker (if configured to do so)
          *
@@ -124,7 +126,7 @@ function BasePicker({
 
     if (isDisabled) {
         return (
-            <View>
+            <View key={inputID}>
                 {Boolean(label) && (
                     <Text
                         style={[styles.textLabelSupporting, styles.mb1]}
@@ -140,19 +142,12 @@ function BasePicker({
     }
 
     return (
-        <>
+        <View key={inputID}>
             <View
                 ref={root}
                 style={[styles.pickerContainer, isDisabled && styles.inputDisabled, containerStyles, isHighlighted && styles.borderColorFocus, hasError && styles.borderColorDanger]}
             >
-                {label && (
-                    <Text
-                        pointerEvents="none"
-                        style={[styles.pickerLabel, styles.textLabelSupporting]}
-                    >
-                        {label}
-                    </Text>
-                )}
+                {label && <Text style={[styles.pickerLabel, styles.textLabelSupporting, styles.pointerEventsNone]}>{label}</Text>}
                 <RNPickerSelect
                     onValueChange={onValueChange}
                     // We add a text color to prevent white text on white background dropdown items on Windows
@@ -188,18 +183,10 @@ function BasePicker({
             </View>
             <FormHelpMessage message={errorText} />
             {Boolean(hintText) && <Text style={[styles.textLabel, styles.colorMuted, styles.mt2]}>{hintText}</Text>}
-        </>
+        </View>
     );
 }
 
 BasePicker.displayName = 'BasePicker';
 
-export default React.forwardRef<BasePickerHandle, BasePickerProps>((props, ref) => (
-    <BasePicker
-        // eslint-disable-next-line react/jsx-props-no-spreading
-        {...props}
-        // Forward the ref to BasePicker, as we implement imperative methods there
-        forwardedRef={ref}
-        key={props.inputID}
-    />
-));
+export default forwardRef(BasePicker);
diff --git a/src/components/Picker/index.native.tsx b/src/components/Picker/index.native.tsx
index f974130a291a..e0b8335215b4 100644
--- a/src/components/Picker/index.native.tsx
+++ b/src/components/Picker/index.native.tsx
@@ -1,11 +1,15 @@
-import React, {forwardRef} from 'react';
+import React, {ForwardedRef, forwardRef} from 'react';
 import BasePicker from './BasePicker';
 import {BasePickerHandle, BasePickerProps} from './types';
 
-export default forwardRef<BasePickerHandle, BasePickerProps>((props, ref) => (
-    <BasePicker
-        // eslint-disable-next-line react/jsx-props-no-spreading
-        {...props}
-        ref={ref}
-    />
-));
+function Picker(props: BasePickerProps, ref: ForwardedRef<BasePickerHandle>) {
+    return (
+        <BasePicker
+            // eslint-disable-next-line react/jsx-props-no-spreading
+            {...props}
+            ref={ref}
+        />
+    );
+}
+
+export default forwardRef(Picker);
diff --git a/src/components/Picker/index.tsx b/src/components/Picker/index.tsx
index e3504bf4842c..cbd09d0c0b45 100644
--- a/src/components/Picker/index.tsx
+++ b/src/components/Picker/index.tsx
@@ -1,4 +1,4 @@
-import React, {forwardRef} from 'react';
+import React, {ForwardedRef, forwardRef} from 'react';
 import BasePicker from './BasePicker';
 import type {AdditionalPickerEvents, BasePickerHandle, BasePickerProps, OnChange, OnMouseDown} from './types';
 
@@ -14,16 +14,20 @@ const additionalPickerEvents = (onMouseDown: OnMouseDown, onChange: OnChange): A
     },
 });
 
-export default forwardRef<BasePickerHandle, BasePickerProps>((props, ref) => (
-    <BasePicker
-        // eslint-disable-next-line react/jsx-props-no-spreading
-        {...props}
-        // Forward the ref to Picker, as we implement imperative methods there
-        ref={ref}
-        // On the Web, focusing the inner picker improves the accessibility,
-        // but doesn't open the picker (which we don't want), like it does on
-        // Native.
-        shouldFocusPicker
-        additionalPickerEvents={additionalPickerEvents}
-    />
-));
+function Picker(props: BasePickerProps, ref: ForwardedRef<BasePickerHandle>) {
+    return (
+        <BasePicker
+            // eslint-disable-next-line react/jsx-props-no-spreading
+            {...props}
+            // Forward the ref to Picker, as we implement imperative methods there
+            ref={ref}
+            // On the Web, focusing the inner picker improves the accessibility,
+            // but doesn't open the picker (which we don't want), like it does on
+            // Native.
+            shouldFocusPicker
+            additionalPickerEvents={additionalPickerEvents}
+        />
+    );
+}
+
+export default forwardRef(Picker);
diff --git a/src/components/Picker/types.ts b/src/components/Picker/types.ts
index 56ffc16e8b39..003b076ac922 100644
--- a/src/components/Picker/types.ts
+++ b/src/components/Picker/types.ts
@@ -1,4 +1,4 @@
-import {ChangeEvent, Component, ForwardedRef, ReactElement} from 'react';
+import {ChangeEvent, Component, ReactElement} from 'react';
 import {MeasureLayoutOnSuccessCallback, NativeMethods, StyleProp, ViewStyle} from 'react-native';
 
 type MeasureLayoutOnFailCallback = () => void;
@@ -42,8 +42,6 @@ type PickerPlaceholder = {
 };
 
 type BasePickerProps = {
-    forwardedRef?: ForwardedRef<BasePickerHandle>;
-
     /** BasePicker label */
     label?: string | null;
 

From f17b26ef61b4dfed2befc2b83eeca16af6f111c6 Mon Sep 17 00:00:00 2001
From: Viktoryia Kliushun <vikstash@gmail.com>
Date: Fri, 17 Nov 2023 13:20:08 +0100
Subject: [PATCH 07/19] Updates after merging main

---
 src/components/LocalePicker.tsx      | 26 +++++++++++++++-----------
 src/components/Picker/BasePicker.tsx |  6 +++---
 2 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/src/components/LocalePicker.tsx b/src/components/LocalePicker.tsx
index a531674003bd..164c1688591a 100644
--- a/src/components/LocalePicker.tsx
+++ b/src/components/LocalePicker.tsx
@@ -2,11 +2,12 @@ import React from 'react';
 import {OnyxEntry, withOnyx} from 'react-native-onyx';
 import {ValueOf} from 'type-fest';
 import compose from '@libs/compose';
-import styles from '@styles/styles';
-import themeColors from '@styles/themes/default';
+import useTheme from '@styles/themes/useTheme';
+import useThemeStyles from '@styles/useThemeStyles';
 import * as App from '@userActions/App';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
+import type {LocaleContextProps} from './LocaleContextProvider';
 import Picker from './Picker';
 import type {PickerSize} from './Picker/types';
 import withLocalize from './withLocalize';
@@ -16,15 +17,15 @@ type LocalePickerOnyxProps = {
     preferredLocale: OnyxEntry<ValueOf<typeof CONST.LOCALES>>;
 };
 
-type LocalePickerProps = LocalePickerOnyxProps & {
-    /** Indicates size of a picker component and whether to render the label or not */
-    size?: PickerSize;
-
-    /** Returns translated string for given locale and phrase */
-    translate: (phrase: string, variables?: Record<string, string>) => string;
-};
+type LocalePickerProps = LocaleContextProps &
+    LocalePickerOnyxProps & {
+        /** Indicates size of a picker component and whether to render the label or not */
+        size?: PickerSize;
+    };
 
 function LocalePicker({preferredLocale = CONST.LOCALES.DEFAULT, size = 'normal', translate}: LocalePickerProps) {
+    const theme = useTheme();
+    const styles = useThemeStyles();
     const localesToLanguages = CONST.LANGUAGES.map((language) => ({
         value: language,
         label: translate(`languagePage.languages.${language}.label`),
@@ -39,13 +40,13 @@ function LocalePicker({preferredLocale = CONST.LOCALES.DEFAULT, size = 'normal',
                     return;
                 }
 
-                App.setLocale(locale);
+                App.setLocale(locale as ValueOf<typeof CONST.LOCALES>);
             }}
             items={localesToLanguages}
             size={size}
             value={preferredLocale}
             containerStyles={size === 'small' ? styles.pickerContainerSmall : {}}
-            backgroundColor={themeColors.signInPage}
+            backgroundColor={theme.signInPage}
         />
     );
 }
@@ -53,6 +54,9 @@ function LocalePicker({preferredLocale = CONST.LOCALES.DEFAULT, size = 'normal',
 LocalePicker.displayName = 'LocalePicker';
 
 export default compose(
+    // TODO: think how this can be fixed
+    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+    // @ts-ignore
     withLocalize,
     withOnyx<LocalePickerProps, LocalePickerOnyxProps>({
         preferredLocale: {
diff --git a/src/components/Picker/BasePicker.tsx b/src/components/Picker/BasePicker.tsx
index aba14d3b74a2..de7101b07628 100644
--- a/src/components/Picker/BasePicker.tsx
+++ b/src/components/Picker/BasePicker.tsx
@@ -1,6 +1,6 @@
 import lodashDefer from 'lodash/defer';
-import React, {ForwardedRef, forwardRef, useContext, useEffect, useImperativeHandle, useRef, useState} from 'react';
-import {View} from 'react-native';
+import React, {ForwardedRef, forwardRef, RefObject, useContext, useEffect, useImperativeHandle, useRef, useState} from 'react';
+import {ScrollView, View} from 'react-native';
 import RNPickerSelect from 'react-native-picker-select';
 import FormHelpMessage from '@components/FormHelpMessage';
 import Icon from '@components/Icon';
@@ -177,7 +177,7 @@ function BasePicker(
                             disableHighlight();
                         }),
                     }}
-                    scrollViewRef={context?.scrollViewRef}
+                    scrollViewRef={context?.scrollViewRef as RefObject<ScrollView>}
                     scrollViewContentOffsetY={context?.contentOffsetY}
                 />
             </View>

From 548c98a6242843b82faac005714fc1cfcafee7a5 Mon Sep 17 00:00:00 2001
From: Viktoryia Kliushun <vikstash@gmail.com>
Date: Fri, 17 Nov 2023 13:34:16 +0100
Subject: [PATCH 08/19] Trigger snyk checks


From a73ba69f65ccfb0cfac52b60e17fc98333c6c477 Mon Sep 17 00:00:00 2001
From: Viktoryia Kliushun <vikstash@gmail.com>
Date: Mon, 20 Nov 2023 13:20:02 +0100
Subject: [PATCH 09/19] Replace withLocalize HOC with hook usage

---
 src/components/LocalePicker.tsx | 33 +++++++++++++--------------------
 src/components/Picker/types.ts  |  2 +-
 2 files changed, 14 insertions(+), 21 deletions(-)

diff --git a/src/components/LocalePicker.tsx b/src/components/LocalePicker.tsx
index 164c1688591a..64a4b8e3f5bd 100644
--- a/src/components/LocalePicker.tsx
+++ b/src/components/LocalePicker.tsx
@@ -1,37 +1,36 @@
 import React from 'react';
 import {OnyxEntry, withOnyx} from 'react-native-onyx';
 import {ValueOf} from 'type-fest';
-import compose from '@libs/compose';
+import useLocalize from '@hooks/useLocalize';
 import useTheme from '@styles/themes/useTheme';
 import useThemeStyles from '@styles/useThemeStyles';
 import * as App from '@userActions/App';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
-import type {LocaleContextProps} from './LocaleContextProvider';
 import Picker from './Picker';
 import type {PickerSize} from './Picker/types';
-import withLocalize from './withLocalize';
 
 type LocalePickerOnyxProps = {
     /** Indicates which locale the user currently has selected */
     preferredLocale: OnyxEntry<ValueOf<typeof CONST.LOCALES>>;
 };
 
-type LocalePickerProps = LocaleContextProps &
-    LocalePickerOnyxProps & {
-        /** Indicates size of a picker component and whether to render the label or not */
-        size?: PickerSize;
-    };
+type LocalePickerProps = LocalePickerOnyxProps & {
+    /** Indicates size of a picker component and whether to render the label or not */
+    size?: PickerSize;
+};
 
-function LocalePicker({preferredLocale = CONST.LOCALES.DEFAULT, size = 'normal', translate}: LocalePickerProps) {
+function LocalePicker({preferredLocale = CONST.LOCALES.DEFAULT, size = 'normal'}: LocalePickerProps) {
     const theme = useTheme();
     const styles = useThemeStyles();
+    const {translate} = useLocalize();
     const localesToLanguages = CONST.LANGUAGES.map((language) => ({
         value: language,
         label: translate(`languagePage.languages.${language}.label`),
         keyForList: language,
         isSelected: preferredLocale === language,
     }));
+
     return (
         <Picker
             label={size === 'normal' ? translate('languagePage.language') : null}
@@ -53,14 +52,8 @@ function LocalePicker({preferredLocale = CONST.LOCALES.DEFAULT, size = 'normal',
 
 LocalePicker.displayName = 'LocalePicker';
 
-export default compose(
-    // TODO: think how this can be fixed
-    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
-    // @ts-ignore
-    withLocalize,
-    withOnyx<LocalePickerProps, LocalePickerOnyxProps>({
-        preferredLocale: {
-            key: ONYXKEYS.NVP_PREFERRED_LOCALE,
-        },
-    }),
-)(LocalePicker);
+export default withOnyx<LocalePickerProps, LocalePickerOnyxProps>({
+    preferredLocale: {
+        key: ONYXKEYS.NVP_PREFERRED_LOCALE,
+    },
+})(LocalePicker);
diff --git a/src/components/Picker/types.ts b/src/components/Picker/types.ts
index 003b076ac922..86d1e240723f 100644
--- a/src/components/Picker/types.ts
+++ b/src/components/Picker/types.ts
@@ -49,7 +49,7 @@ type BasePickerProps = {
     isDisabled?: boolean;
 
     /** Input value */
-    value?: string | null | number;
+    value?: string | number | null;
 
     /** The items to display in the list of selections */
     items: PickerItem[];

From da35fc054e1bcc3e753229a6783fc32de6ad979c Mon Sep 17 00:00:00 2001
From: Viktoryia Kliushun <vikstash@gmail.com>
Date: Tue, 21 Nov 2023 10:26:11 +0100
Subject: [PATCH 10/19] Update picker props type to be generic

---
 src/components/LocalePicker.tsx        |  4 ++--
 src/components/Picker/BasePicker.tsx   | 10 +++++-----
 src/components/Picker/index.native.tsx |  4 ++--
 src/components/Picker/index.tsx        | 26 +++++++++++++-------------
 src/components/Picker/types.ts         | 18 +++++++++---------
 5 files changed, 31 insertions(+), 31 deletions(-)

diff --git a/src/components/LocalePicker.tsx b/src/components/LocalePicker.tsx
index 64a4b8e3f5bd..6a12df9ef5dd 100644
--- a/src/components/LocalePicker.tsx
+++ b/src/components/LocalePicker.tsx
@@ -35,11 +35,11 @@ function LocalePicker({preferredLocale = CONST.LOCALES.DEFAULT, size = 'normal'}
         <Picker
             label={size === 'normal' ? translate('languagePage.language') : null}
             onInputChange={(locale) => {
-                if (locale === preferredLocale || typeof locale !== 'string') {
+                if (locale === preferredLocale) {
                     return;
                 }
 
-                App.setLocale(locale as ValueOf<typeof CONST.LOCALES>);
+                App.setLocale(locale);
             }}
             items={localesToLanguages}
             size={size}
diff --git a/src/components/Picker/BasePicker.tsx b/src/components/Picker/BasePicker.tsx
index de7101b07628..24ec13c2648c 100644
--- a/src/components/Picker/BasePicker.tsx
+++ b/src/components/Picker/BasePicker.tsx
@@ -1,5 +1,5 @@
 import lodashDefer from 'lodash/defer';
-import React, {ForwardedRef, forwardRef, RefObject, useContext, useEffect, useImperativeHandle, useRef, useState} from 'react';
+import React, {ForwardedRef, forwardRef, ReactNode, RefObject, useContext, useEffect, useImperativeHandle, useRef, useState} from 'react';
 import {ScrollView, View} from 'react-native';
 import RNPickerSelect from 'react-native-picker-select';
 import FormHelpMessage from '@components/FormHelpMessage';
@@ -11,7 +11,7 @@ import styles from '@styles/styles';
 import themeColors from '@styles/themes/default';
 import type {BasePickerHandle, BasePickerProps} from './types';
 
-function BasePicker(
+function BasePicker<TPickerValue>(
     {
         items,
         backgroundColor,
@@ -35,7 +35,7 @@ function BasePicker(
         shouldFocusPicker = false,
         onBlur = () => {},
         additionalPickerEvents = () => {},
-    }: BasePickerProps,
+    }: BasePickerProps<TPickerValue>,
     ref: ForwardedRef<BasePickerHandle>,
 ) {
     const [isHighlighted, setIsHighlighted] = useState(false);
@@ -68,7 +68,7 @@ function BasePicker(
      * Forms use inputID to set values. But BasePicker passes an index as the second parameter to onValueChange
      * We are overriding this behavior to make BasePicker work with Form
      */
-    const onValueChange = (inputValue: string, index: number) => {
+    const onValueChange = (inputValue: TPickerValue, index: number) => {
         if (inputID) {
             onInputChange(inputValue);
             return;
@@ -135,7 +135,7 @@ function BasePicker(
                         {label}
                     </Text>
                 )}
-                <Text numberOfLines={1}>{value}</Text>
+                <Text numberOfLines={1}>{value as ReactNode}</Text>
                 {Boolean(hintText) && <Text style={[styles.textLabel, styles.colorMuted, styles.mt2]}>{hintText}</Text>}
             </View>
         );
diff --git a/src/components/Picker/index.native.tsx b/src/components/Picker/index.native.tsx
index e0b8335215b4..10ce8dcb2bba 100644
--- a/src/components/Picker/index.native.tsx
+++ b/src/components/Picker/index.native.tsx
@@ -2,9 +2,9 @@ import React, {ForwardedRef, forwardRef} from 'react';
 import BasePicker from './BasePicker';
 import {BasePickerHandle, BasePickerProps} from './types';
 
-function Picker(props: BasePickerProps, ref: ForwardedRef<BasePickerHandle>) {
+function Picker<TPickerValue>(props: BasePickerProps<TPickerValue>, ref: ForwardedRef<BasePickerHandle>) {
     return (
-        <BasePicker
+        <BasePicker<TPickerValue>
             // eslint-disable-next-line react/jsx-props-no-spreading
             {...props}
             ref={ref}
diff --git a/src/components/Picker/index.tsx b/src/components/Picker/index.tsx
index cbd09d0c0b45..5a27b54380cc 100644
--- a/src/components/Picker/index.tsx
+++ b/src/components/Picker/index.tsx
@@ -2,21 +2,21 @@ import React, {ForwardedRef, forwardRef} from 'react';
 import BasePicker from './BasePicker';
 import type {AdditionalPickerEvents, BasePickerHandle, BasePickerProps, OnChange, OnMouseDown} from './types';
 
-const additionalPickerEvents = (onMouseDown: OnMouseDown, onChange: OnChange): AdditionalPickerEvents => ({
-    onMouseDown,
-    onChange: (e) => {
-        if (e.target.selectedIndex === undefined) {
-            return;
-        }
-        const index = e.target.selectedIndex;
-        const value = e.target.options[index].value;
-        onChange(value, index);
-    },
-});
+function Picker<TPickerValue>(props: BasePickerProps<TPickerValue>, ref: ForwardedRef<BasePickerHandle>) {
+    const additionalPickerEvents = (onMouseDown: OnMouseDown, onChange: OnChange<TPickerValue>): AdditionalPickerEvents => ({
+        onMouseDown,
+        onChange: (e) => {
+            if (e.target.selectedIndex === undefined) {
+                return;
+            }
+            const index = e.target.selectedIndex;
+            const value = e.target.options[index].value;
+            onChange(value as TPickerValue, index);
+        },
+    });
 
-function Picker(props: BasePickerProps, ref: ForwardedRef<BasePickerHandle>) {
     return (
-        <BasePicker
+        <BasePicker<TPickerValue>
             // eslint-disable-next-line react/jsx-props-no-spreading
             {...props}
             // Forward the ref to Picker, as we implement imperative methods there
diff --git a/src/components/Picker/types.ts b/src/components/Picker/types.ts
index 86d1e240723f..98d1637ff7b3 100644
--- a/src/components/Picker/types.ts
+++ b/src/components/Picker/types.ts
@@ -12,22 +12,22 @@ type BasePickerHandle = {
 
 type OnMouseDown = () => void;
 
-type OnChange = (value: string, index: number) => void;
+type OnChange<TPickerValue> = (value: TPickerValue, index: number) => void;
 
 type AdditionalPickerEvents = {
     onMouseDown?: OnMouseDown;
     onChange?: (event: ChangeEvent<HTMLSelectElement>) => void;
 };
 
-type AdditionalPickerEventsCallback = (onMouseDown: OnMouseDown, onChange: OnChange) => AdditionalPickerEvents;
+type AdditionalPickerEventsCallback<TPickerValue> = (onMouseDown: OnMouseDown, onChange: OnChange<TPickerValue>) => AdditionalPickerEvents;
 
 type DefaultPickerEventsCallback = () => void;
 
 type PickerSize = 'normal' | 'small';
 
-type PickerItem = {
+type PickerItem<TPickerValue> = {
     /** The value of the item that is being selected */
-    value: string | number;
+    value: TPickerValue;
 
     /** The text to display for the item */
     label: string;
@@ -41,7 +41,7 @@ type PickerPlaceholder = {
     label?: string;
 };
 
-type BasePickerProps = {
+type BasePickerProps<TPickerValue> = {
     /** BasePicker label */
     label?: string | null;
 
@@ -49,10 +49,10 @@ type BasePickerProps = {
     isDisabled?: boolean;
 
     /** Input value */
-    value?: string | number | null;
+    value?: TPickerValue | null;
 
     /** The items to display in the list of selections */
-    items: PickerItem[];
+    items: Array<PickerItem<TPickerValue>>;
 
     /** Something to show as the placeholder before something is selected */
     placeholder?: PickerPlaceholder;
@@ -70,7 +70,7 @@ type BasePickerProps = {
     inputID?: string;
 
     /** A callback method that is called when the value changes and it receives the selected value as an argument */
-    onInputChange: (value: string | number, index?: number) => void;
+    onInputChange: (value: TPickerValue, index?: number) => void;
 
     /** Size of a picker component */
     size?: PickerSize;
@@ -85,7 +85,7 @@ type BasePickerProps = {
     onBlur?: () => void;
 
     /** Additional events passed to the core BasePicker for specific platforms such as web */
-    additionalPickerEvents?: AdditionalPickerEventsCallback | DefaultPickerEventsCallback;
+    additionalPickerEvents?: AdditionalPickerEventsCallback<TPickerValue> | DefaultPickerEventsCallback;
 
     /** Hint text that appears below the picker */
     hintText?: string;

From aac29daa139152ff515f98be82ce02fb8908a6fe Mon Sep 17 00:00:00 2001
From: Viktoryia Kliushun <vikstash@gmail.com>
Date: Tue, 21 Nov 2023 11:24:09 +0100
Subject: [PATCH 11/19] Create separate function for default picker icon

---
 src/components/Picker/BasePicker.tsx | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/src/components/Picker/BasePicker.tsx b/src/components/Picker/BasePicker.tsx
index 24ec13c2648c..cc335ff1ac5f 100644
--- a/src/components/Picker/BasePicker.tsx
+++ b/src/components/Picker/BasePicker.tsx
@@ -1,5 +1,5 @@
 import lodashDefer from 'lodash/defer';
-import React, {ForwardedRef, forwardRef, ReactNode, RefObject, useContext, useEffect, useImperativeHandle, useRef, useState} from 'react';
+import React, {ForwardedRef, forwardRef, ReactElement, ReactNode, RefObject, useContext, useEffect, useImperativeHandle, useRef, useState} from 'react';
 import {ScrollView, View} from 'react-native';
 import RNPickerSelect from 'react-native-picker-select';
 import FormHelpMessage from '@components/FormHelpMessage';
@@ -9,7 +9,15 @@ import {ScrollContext} from '@components/ScrollViewWithContext';
 import Text from '@components/Text';
 import styles from '@styles/styles';
 import themeColors from '@styles/themes/default';
-import type {BasePickerHandle, BasePickerProps} from './types';
+import type {BasePickerHandle, BasePickerProps, PickerSize} from './types';
+
+const getDefaultPickerIcon = (iconSize: PickerSize): ReactElement => (
+    <Icon
+        src={Expensicons.DownArrow}
+        // eslint-disable-next-line react/jsx-props-no-spreading
+        {...(iconSize === 'small' ? {width: styles.pickerSmall().icon.width, height: styles.pickerSmall().icon.height} : {})}
+    />
+);
 
 function BasePicker<TPickerValue>(
     {
@@ -25,13 +33,7 @@ function BasePicker<TPickerValue>(
         containerStyles = [],
         placeholder = {},
         size = 'normal',
-        icon = (iconSize) => (
-            <Icon
-                src={Expensicons.DownArrow}
-                // eslint-disable-next-line react/jsx-props-no-spreading
-                {...(iconSize === 'small' ? {width: styles.pickerSmall().icon.width, height: styles.pickerSmall().icon.height} : {})}
-            />
-        ),
+        icon = getDefaultPickerIcon,
         shouldFocusPicker = false,
         onBlur = () => {},
         additionalPickerEvents = () => {},

From a53ec873be6419793297ec4f91fad3d5ddb5ad07 Mon Sep 17 00:00:00 2001
From: Viktoryia Kliushun <vikstash@gmail.com>
Date: Tue, 21 Nov 2023 15:07:19 +0100
Subject: [PATCH 12/19] Create separate Locale type and add useScroll hook

---
 src/ONYXKEYS.ts                          | 2 +-
 src/components/LocalePicker.tsx          | 4 ++--
 src/components/Picker/BasePicker.tsx     | 8 ++++----
 src/components/ScrollViewWithContext.tsx | 1 +
 src/hooks/useScroll.ts                   | 6 ++++++
 src/types/onyx/Locale.ts                 | 6 ++++++
 src/types/onyx/index.ts                  | 2 ++
 7 files changed, 22 insertions(+), 7 deletions(-)
 create mode 100644 src/hooks/useScroll.ts
 create mode 100644 src/types/onyx/Locale.ts

diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index a5a969adb833..91d054d6fad4 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -379,7 +379,7 @@ type OnyxValues = {
     [ONYXKEYS.IS_PLAID_DISABLED]: boolean;
     [ONYXKEYS.PLAID_LINK_TOKEN]: string;
     [ONYXKEYS.ONFIDO_TOKEN]: string;
-    [ONYXKEYS.NVP_PREFERRED_LOCALE]: ValueOf<typeof CONST.LOCALES>;
+    [ONYXKEYS.NVP_PREFERRED_LOCALE]: OnyxTypes.Locale;
     [ONYXKEYS.USER_WALLET]: OnyxTypes.UserWallet;
     [ONYXKEYS.WALLET_ONFIDO]: OnyxTypes.WalletOnfido;
     [ONYXKEYS.WALLET_ADDITIONAL_DETAILS]: OnyxTypes.WalletAdditionalDetails;
diff --git a/src/components/LocalePicker.tsx b/src/components/LocalePicker.tsx
index 6a12df9ef5dd..c04b0131744f 100644
--- a/src/components/LocalePicker.tsx
+++ b/src/components/LocalePicker.tsx
@@ -1,18 +1,18 @@
 import React from 'react';
 import {OnyxEntry, withOnyx} from 'react-native-onyx';
-import {ValueOf} from 'type-fest';
 import useLocalize from '@hooks/useLocalize';
 import useTheme from '@styles/themes/useTheme';
 import useThemeStyles from '@styles/useThemeStyles';
 import * as App from '@userActions/App';
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
+import type {Locale} from '@src/types/onyx';
 import Picker from './Picker';
 import type {PickerSize} from './Picker/types';
 
 type LocalePickerOnyxProps = {
     /** Indicates which locale the user currently has selected */
-    preferredLocale: OnyxEntry<ValueOf<typeof CONST.LOCALES>>;
+    preferredLocale: OnyxEntry<Locale>;
 };
 
 type LocalePickerProps = LocalePickerOnyxProps & {
diff --git a/src/components/Picker/BasePicker.tsx b/src/components/Picker/BasePicker.tsx
index cc335ff1ac5f..1846c623ec6e 100644
--- a/src/components/Picker/BasePicker.tsx
+++ b/src/components/Picker/BasePicker.tsx
@@ -1,12 +1,12 @@
 import lodashDefer from 'lodash/defer';
-import React, {ForwardedRef, forwardRef, ReactElement, ReactNode, RefObject, useContext, useEffect, useImperativeHandle, useRef, useState} from 'react';
+import React, {ForwardedRef, forwardRef, ReactElement, ReactNode, RefObject, useEffect, useImperativeHandle, useRef, useState} from 'react';
 import {ScrollView, View} from 'react-native';
 import RNPickerSelect from 'react-native-picker-select';
 import FormHelpMessage from '@components/FormHelpMessage';
 import Icon from '@components/Icon';
 import * as Expensicons from '@components/Icon/Expensicons';
-import {ScrollContext} from '@components/ScrollViewWithContext';
 import Text from '@components/Text';
+import useScroll from '@hooks/useScroll';
 import styles from '@styles/styles';
 import themeColors from '@styles/themes/default';
 import type {BasePickerHandle, BasePickerProps, PickerSize} from './types';
@@ -30,7 +30,7 @@ function BasePicker<TPickerValue>(
         isDisabled = false,
         errorText = '',
         hintText = '',
-        containerStyles = [],
+        containerStyles,
         placeholder = {},
         size = 'normal',
         icon = getDefaultPickerIcon,
@@ -64,7 +64,7 @@ function BasePicker<TPickerValue>(
         // eslint-disable-next-line react-hooks/exhaustive-deps
     }, [items]);
 
-    const context = useContext(ScrollContext);
+    const context = useScroll();
 
     /**
      * Forms use inputID to set values. But BasePicker passes an index as the second parameter to onValueChange
diff --git a/src/components/ScrollViewWithContext.tsx b/src/components/ScrollViewWithContext.tsx
index 6122d5508a38..7c75ae2f71b2 100644
--- a/src/components/ScrollViewWithContext.tsx
+++ b/src/components/ScrollViewWithContext.tsx
@@ -63,3 +63,4 @@ ScrollViewWithContextWithRef.displayName = 'ScrollViewWithContextWithRef';
 
 export default React.forwardRef(ScrollViewWithContextWithRef);
 export {ScrollContext};
+export type {ScrollContextValue};
diff --git a/src/hooks/useScroll.ts b/src/hooks/useScroll.ts
new file mode 100644
index 000000000000..0b6733753893
--- /dev/null
+++ b/src/hooks/useScroll.ts
@@ -0,0 +1,6 @@
+import {useContext} from 'react';
+import {ScrollContext, ScrollContextValue} from '@components/ScrollViewWithContext';
+
+export default function useScroll(): ScrollContextValue {
+    return useContext(ScrollContext);
+}
diff --git a/src/types/onyx/Locale.ts b/src/types/onyx/Locale.ts
new file mode 100644
index 000000000000..1a5124684995
--- /dev/null
+++ b/src/types/onyx/Locale.ts
@@ -0,0 +1,6 @@
+import {ValueOf} from 'type-fest';
+import CONST from '@src/CONST';
+
+type Locale = ValueOf<typeof CONST.LOCALES>;
+
+export default Locale;
diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts
index 87cf24d6dec7..bb779b3ab58a 100644
--- a/src/types/onyx/index.ts
+++ b/src/types/onyx/index.ts
@@ -12,6 +12,7 @@ import Form, {AddDebitCardForm, DateOfBirthForm} from './Form';
 import FrequentlyUsedEmoji from './FrequentlyUsedEmoji';
 import Fund from './Fund';
 import IOU from './IOU';
+import Locale from './Locale';
 import Login from './Login';
 import MapboxAccessToken from './MapboxAccessToken';
 import Modal from './Modal';
@@ -67,6 +68,7 @@ export type {
     FrequentlyUsedEmoji,
     Fund,
     IOU,
+    Locale,
     Login,
     MapboxAccessToken,
     Modal,

From 8914066ed24b727eba10f94dda2bf07bc4171fa0 Mon Sep 17 00:00:00 2001
From: Viktoryia Kliushun <vikstash@gmail.com>
Date: Tue, 21 Nov 2023 15:18:19 +0100
Subject: [PATCH 13/19] Rename custom hook

---
 src/components/Picker/BasePicker.tsx            | 4 ++--
 src/hooks/{useScroll.ts => useScrollContext.ts} | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)
 rename src/hooks/{useScroll.ts => useScrollContext.ts} (71%)

diff --git a/src/components/Picker/BasePicker.tsx b/src/components/Picker/BasePicker.tsx
index 1846c623ec6e..6d3fbfbbc410 100644
--- a/src/components/Picker/BasePicker.tsx
+++ b/src/components/Picker/BasePicker.tsx
@@ -6,7 +6,7 @@ import FormHelpMessage from '@components/FormHelpMessage';
 import Icon from '@components/Icon';
 import * as Expensicons from '@components/Icon/Expensicons';
 import Text from '@components/Text';
-import useScroll from '@hooks/useScroll';
+import useScrollContext from '@hooks/useScrollContext';
 import styles from '@styles/styles';
 import themeColors from '@styles/themes/default';
 import type {BasePickerHandle, BasePickerProps, PickerSize} from './types';
@@ -64,7 +64,7 @@ function BasePicker<TPickerValue>(
         // eslint-disable-next-line react-hooks/exhaustive-deps
     }, [items]);
 
-    const context = useScroll();
+    const context = useScrollContext();
 
     /**
      * Forms use inputID to set values. But BasePicker passes an index as the second parameter to onValueChange
diff --git a/src/hooks/useScroll.ts b/src/hooks/useScrollContext.ts
similarity index 71%
rename from src/hooks/useScroll.ts
rename to src/hooks/useScrollContext.ts
index 0b6733753893..711c8326bdff 100644
--- a/src/hooks/useScroll.ts
+++ b/src/hooks/useScrollContext.ts
@@ -1,6 +1,6 @@
 import {useContext} from 'react';
 import {ScrollContext, ScrollContextValue} from '@components/ScrollViewWithContext';
 
-export default function useScroll(): ScrollContextValue {
+export default function useScrollContext(): ScrollContextValue {
     return useContext(ScrollContext);
 }

From e9b526cb95e88959f4e996c297f1e5ab453dc842 Mon Sep 17 00:00:00 2001
From: Viktoryia Kliushun <vikstash@gmail.com>
Date: Wed, 22 Nov 2023 12:25:34 +0100
Subject: [PATCH 14/19] Minor code improvements

---
 src/components/Picker/BasePicker.tsx | 2 +-
 src/components/Picker/types.ts       | 3 +++
 src/components/Text.tsx              | 3 ---
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/components/Picker/BasePicker.tsx b/src/components/Picker/BasePicker.tsx
index 6d3fbfbbc410..40ef265c999c 100644
--- a/src/components/Picker/BasePicker.tsx
+++ b/src/components/Picker/BasePicker.tsx
@@ -50,7 +50,7 @@ function BasePicker<TPickerValue>(
 
     // Windows will reuse the text color of the select for each one of the options
     // so we might need to color accordingly so it doesn't blend with the background.
-    const pickerPlaceholder = Object.keys(placeholder).length ? {...placeholder, color: themeColors.pickerOptionsTextColor} : {};
+    const pickerPlaceholder = Object.keys(placeholder).length > 0 ? {...placeholder, color: themeColors.pickerOptionsTextColor} : {};
 
     useEffect(() => {
         if (!!value || !items || items.length !== 1 || !onInputChange) {
diff --git a/src/components/Picker/types.ts b/src/components/Picker/types.ts
index 98d1637ff7b3..58eed0371893 100644
--- a/src/components/Picker/types.ts
+++ b/src/components/Picker/types.ts
@@ -69,6 +69,9 @@ type BasePickerProps<TPickerValue> = {
     /** The ID used to uniquely identify the input in a Form */
     inputID?: string;
 
+    /** Saves a draft of the input value when used in a form */
+    shouldSaveDraft?: boolean;
+
     /** A callback method that is called when the value changes and it receives the selected value as an argument */
     onInputChange: (value: TPickerValue, index?: number) => void;
 
diff --git a/src/components/Text.tsx b/src/components/Text.tsx
index a4262e6440ad..96a6f535877a 100644
--- a/src/components/Text.tsx
+++ b/src/components/Text.tsx
@@ -21,9 +21,6 @@ type TextProps = RNTextProps & {
 
     /** The family of the font to use */
     family?: keyof typeof fontFamily;
-
-    /** Pointer events property to the Text element */
-    pointerEvents?: 'box-none' | 'none' | 'box-only' | 'auto';
 };
 
 function Text({color, fontSize = variables.fontSizeNormal, textAlign = 'left', children = null, family = 'EXP_NEUE', style = {}, ...props}: TextProps, ref: ForwardedRef<RNText>) {

From b6edfdcc48cb683841dc644f69ddd690f420f16d Mon Sep 17 00:00:00 2001
From: Viktoryia Kliushun <vikstash@gmail.com>
Date: Wed, 22 Nov 2023 13:16:04 +0100
Subject: [PATCH 15/19] Update key usage

---
 src/components/Picker/BasePicker.tsx   | 6 +++---
 src/components/Picker/index.native.tsx | 1 +
 src/components/Picker/index.tsx        | 1 +
 3 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/components/Picker/BasePicker.tsx b/src/components/Picker/BasePicker.tsx
index 40ef265c999c..6ac6e11b254d 100644
--- a/src/components/Picker/BasePicker.tsx
+++ b/src/components/Picker/BasePicker.tsx
@@ -128,7 +128,7 @@ function BasePicker<TPickerValue>(
 
     if (isDisabled) {
         return (
-            <View key={inputID}>
+            <View>
                 {Boolean(label) && (
                     <Text
                         style={[styles.textLabelSupporting, styles.mb1]}
@@ -144,7 +144,7 @@ function BasePicker<TPickerValue>(
     }
 
     return (
-        <View key={inputID}>
+        <>
             <View
                 ref={root}
                 style={[styles.pickerContainer, isDisabled && styles.inputDisabled, containerStyles, isHighlighted && styles.borderColorFocus, hasError && styles.borderColorDanger]}
@@ -185,7 +185,7 @@ function BasePicker<TPickerValue>(
             </View>
             <FormHelpMessage message={errorText} />
             {Boolean(hintText) && <Text style={[styles.textLabel, styles.colorMuted, styles.mt2]}>{hintText}</Text>}
-        </View>
+        </>
     );
 }
 
diff --git a/src/components/Picker/index.native.tsx b/src/components/Picker/index.native.tsx
index 10ce8dcb2bba..7373f5a6f280 100644
--- a/src/components/Picker/index.native.tsx
+++ b/src/components/Picker/index.native.tsx
@@ -7,6 +7,7 @@ function Picker<TPickerValue>(props: BasePickerProps<TPickerValue>, ref: Forward
         <BasePicker<TPickerValue>
             // eslint-disable-next-line react/jsx-props-no-spreading
             {...props}
+            key={props.inputID}
             ref={ref}
         />
     );
diff --git a/src/components/Picker/index.tsx b/src/components/Picker/index.tsx
index 5a27b54380cc..18184b130bba 100644
--- a/src/components/Picker/index.tsx
+++ b/src/components/Picker/index.tsx
@@ -25,6 +25,7 @@ function Picker<TPickerValue>(props: BasePickerProps<TPickerValue>, ref: Forward
             // but doesn't open the picker (which we don't want), like it does on
             // Native.
             shouldFocusPicker
+            key={props.inputID}
             additionalPickerEvents={additionalPickerEvents}
         />
     );

From c1df87656a43a8e7a57389b987f631e25d322caf Mon Sep 17 00:00:00 2001
From: Viktoryia Kliushun <vikstash@gmail.com>
Date: Fri, 24 Nov 2023 16:53:17 +0100
Subject: [PATCH 16/19] Minor code improvements

---
 src/components/Picker/BasePicker.tsx | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/components/Picker/BasePicker.tsx b/src/components/Picker/BasePicker.tsx
index 6ac6e11b254d..7f6a88458cc3 100644
--- a/src/components/Picker/BasePicker.tsx
+++ b/src/components/Picker/BasePicker.tsx
@@ -129,7 +129,7 @@ function BasePicker<TPickerValue>(
     if (isDisabled) {
         return (
             <View>
-                {Boolean(label) && (
+                {!!label && (
                     <Text
                         style={[styles.textLabelSupporting, styles.mb1]}
                         numberOfLines={1}
@@ -138,7 +138,7 @@ function BasePicker<TPickerValue>(
                     </Text>
                 )}
                 <Text numberOfLines={1}>{value as ReactNode}</Text>
-                {Boolean(hintText) && <Text style={[styles.textLabel, styles.colorMuted, styles.mt2]}>{hintText}</Text>}
+                {!!hintText && <Text style={[styles.textLabel, styles.colorMuted, styles.mt2]}>{hintText}</Text>}
             </View>
         );
     }
@@ -184,7 +184,7 @@ function BasePicker<TPickerValue>(
                 />
             </View>
             <FormHelpMessage message={errorText} />
-            {Boolean(hintText) && <Text style={[styles.textLabel, styles.colorMuted, styles.mt2]}>{hintText}</Text>}
+            {!!hintText && <Text style={[styles.textLabel, styles.colorMuted, styles.mt2]}>{hintText}</Text>}
         </>
     );
 }

From 275a908e85e944bd408dc18467c2a5ed7c1951e6 Mon Sep 17 00:00:00 2001
From: Viktoryia Kliushun <vikstash@gmail.com>
Date: Fri, 1 Dec 2023 15:26:53 +0100
Subject: [PATCH 17/19] Updates after merging main

---
 package-lock.json                    | 14 ++++-----
 src/components/Picker/BasePicker.tsx | 44 ++++++++++++++++++----------
 2 files changed, 35 insertions(+), 23 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 65173ccec496..25735ab77bdd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -96,7 +96,7 @@
         "react-native-pdf": "^6.7.3",
         "react-native-performance": "^5.1.0",
         "react-native-permissions": "^3.9.3",
-        "react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#eae05855286dc699954415cc1d629bfd8e8e47e2",
+        "react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#0d15d4618f58e99c1261921111e68ee85bb3c2a8",
         "react-native-plaid-link-sdk": "10.8.0",
         "react-native-qrcode-svg": "^6.2.0",
         "react-native-quick-sqlite": "^8.0.0-beta.2",
@@ -44514,9 +44514,9 @@
       }
     },
     "node_modules/react-native-picker-select": {
-      "version": "8.0.4",
-      "resolved": "git+ssh://git@github.com/Expensify/react-native-picker-select.git#eae05855286dc699954415cc1d629bfd8e8e47e2",
-      "integrity": "sha512-3U/mtHN/pKC5yXtJnqj5rre8+4YPSqoXCn/3qKjb5u8BMIiuc5H3KJ0ZbKlZEg/8Uh4j0cvrtcNasdPgMqRgCQ==",
+      "version": "8.1.0",
+      "resolved": "git+ssh://git@github.com/Expensify/react-native-picker-select.git#0d15d4618f58e99c1261921111e68ee85bb3c2a8",
+      "integrity": "sha512-ly0ZCt3K4RX7t9lfSb2OSGAw0cv8UqdMoxNfh5j+KujYYq+N8VsI9O/lmqquNeX/AMp5hM3fjetEWue4nZw/hA==",
       "license": "MIT",
       "dependencies": {
         "lodash.isequal": "^4.5.0"
@@ -84854,9 +84854,9 @@
       "requires": {}
     },
     "react-native-picker-select": {
-      "version": "git+ssh://git@github.com/Expensify/react-native-picker-select.git#eae05855286dc699954415cc1d629bfd8e8e47e2",
-      "integrity": "sha512-3U/mtHN/pKC5yXtJnqj5rre8+4YPSqoXCn/3qKjb5u8BMIiuc5H3KJ0ZbKlZEg/8Uh4j0cvrtcNasdPgMqRgCQ==",
-      "from": "react-native-picker-select@git+https://github.com/Expensify/react-native-picker-select.git#eae05855286dc699954415cc1d629bfd8e8e47e2",
+      "version": "git+ssh://git@github.com/Expensify/react-native-picker-select.git#0d15d4618f58e99c1261921111e68ee85bb3c2a8",
+      "integrity": "sha512-ly0ZCt3K4RX7t9lfSb2OSGAw0cv8UqdMoxNfh5j+KujYYq+N8VsI9O/lmqquNeX/AMp5hM3fjetEWue4nZw/hA==",
+      "from": "react-native-picker-select@git+https://github.com/Expensify/react-native-picker-select.git#0d15d4618f58e99c1261921111e68ee85bb3c2a8",
       "requires": {
         "lodash.isequal": "^4.5.0"
       }
diff --git a/src/components/Picker/BasePicker.tsx b/src/components/Picker/BasePicker.tsx
index 7f6a88458cc3..30227a19ce4f 100644
--- a/src/components/Picker/BasePicker.tsx
+++ b/src/components/Picker/BasePicker.tsx
@@ -1,5 +1,5 @@
 import lodashDefer from 'lodash/defer';
-import React, {ForwardedRef, forwardRef, ReactElement, ReactNode, RefObject, useEffect, useImperativeHandle, useRef, useState} from 'react';
+import React, {ForwardedRef, forwardRef, ReactElement, ReactNode, RefObject, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react';
 import {ScrollView, View} from 'react-native';
 import RNPickerSelect from 'react-native-picker-select';
 import FormHelpMessage from '@components/FormHelpMessage';
@@ -7,17 +7,11 @@ import Icon from '@components/Icon';
 import * as Expensicons from '@components/Icon/Expensicons';
 import Text from '@components/Text';
 import useScrollContext from '@hooks/useScrollContext';
-import styles from '@styles/styles';
-import themeColors from '@styles/themes/default';
-import type {BasePickerHandle, BasePickerProps, PickerSize} from './types';
-
-const getDefaultPickerIcon = (iconSize: PickerSize): ReactElement => (
-    <Icon
-        src={Expensicons.DownArrow}
-        // eslint-disable-next-line react/jsx-props-no-spreading
-        {...(iconSize === 'small' ? {width: styles.pickerSmall().icon.width, height: styles.pickerSmall().icon.height} : {})}
-    />
-);
+import useTheme from '@styles/themes/useTheme';
+import useThemeStyles from '@styles/useThemeStyles';
+import type {BasePickerHandle, BasePickerProps} from './types';
+
+type IconToRender = () => ReactElement;
 
 function BasePicker<TPickerValue>(
     {
@@ -26,6 +20,7 @@ function BasePicker<TPickerValue>(
         inputID,
         value,
         onInputChange,
+        icon,
         label = '',
         isDisabled = false,
         errorText = '',
@@ -33,13 +28,15 @@ function BasePicker<TPickerValue>(
         containerStyles,
         placeholder = {},
         size = 'normal',
-        icon = getDefaultPickerIcon,
         shouldFocusPicker = false,
         onBlur = () => {},
         additionalPickerEvents = () => {},
     }: BasePickerProps<TPickerValue>,
     ref: ForwardedRef<BasePickerHandle>,
 ) {
+    const styles = useThemeStyles();
+    const theme = useTheme();
+
     const [isHighlighted, setIsHighlighted] = useState(false);
 
     // reference to the root View
@@ -50,7 +47,7 @@ function BasePicker<TPickerValue>(
 
     // Windows will reuse the text color of the select for each one of the options
     // so we might need to color accordingly so it doesn't blend with the background.
-    const pickerPlaceholder = Object.keys(placeholder).length > 0 ? {...placeholder, color: themeColors.pickerOptionsTextColor} : {};
+    const pickerPlaceholder = Object.keys(placeholder).length > 0 ? {...placeholder, color: theme.pickerOptionsTextColor} : {};
 
     useEffect(() => {
         if (!!value || !items || items.length !== 1 || !onInputChange) {
@@ -87,6 +84,21 @@ function BasePicker<TPickerValue>(
         setIsHighlighted(false);
     };
 
+    const iconToRender = useMemo((): IconToRender => {
+        if (icon) {
+            return () => icon(size);
+        }
+
+        // eslint-disable-next-line react/display-name
+        return () => (
+            <Icon
+                src={Expensicons.DownArrow}
+                // eslint-disable-next-line react/jsx-props-no-spreading
+                {...(size === 'small' ? {width: styles.pickerSmall().icon.width, height: styles.pickerSmall().icon.height} : {})}
+            />
+        );
+    }, [icon, size, styles]);
+
     useImperativeHandle(ref, () => ({
         /**
          * Focuses the picker (if configured to do so)
@@ -153,12 +165,12 @@ function BasePicker<TPickerValue>(
                 <RNPickerSelect
                     onValueChange={onValueChange}
                     // We add a text color to prevent white text on white background dropdown items on Windows
-                    items={items.map((item) => ({...item, color: themeColors.pickerOptionsTextColor}))}
+                    items={items.map((item) => ({...item, color: theme.pickerOptionsTextColor}))}
                     style={size === 'normal' ? styles.picker(isDisabled, backgroundColor) : styles.pickerSmall(backgroundColor)}
                     useNativeAndroidPickerStyle={false}
                     placeholder={pickerPlaceholder}
                     value={value}
-                    Icon={() => icon(size)}
+                    Icon={iconToRender}
                     disabled={isDisabled}
                     fixAndroidTouchableBug
                     onOpen={enableHighlight}

From eb536668d2c0b5a9b7b1094556892f6978124a22 Mon Sep 17 00:00:00 2001
From: Viktoryia Kliushun <vikstash@gmail.com>
Date: Fri, 1 Dec 2023 15:36:20 +0100
Subject: [PATCH 18/19] Remove old BasePicker js file

---
 src/components/Picker/BasePicker.js | 311 ----------------------------
 1 file changed, 311 deletions(-)
 delete mode 100644 src/components/Picker/BasePicker.js

diff --git a/src/components/Picker/BasePicker.js b/src/components/Picker/BasePicker.js
deleted file mode 100644
index 084cabc02234..000000000000
--- a/src/components/Picker/BasePicker.js
+++ /dev/null
@@ -1,311 +0,0 @@
-import PropTypes from 'prop-types';
-import React, {useContext, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react';
-import {View} from 'react-native';
-import RNPickerSelect from 'react-native-picker-select';
-import _ from 'underscore';
-import FormHelpMessage from '@components/FormHelpMessage';
-import Icon from '@components/Icon';
-import * as Expensicons from '@components/Icon/Expensicons';
-import refPropTypes from '@components/refPropTypes';
-import {ScrollContext} from '@components/ScrollViewWithContext';
-import Text from '@components/Text';
-import useTheme from '@styles/themes/useTheme';
-import useThemeStyles from '@styles/useThemeStyles';
-
-const propTypes = {
-    /** A forwarded ref */
-    forwardedRef: refPropTypes,
-
-    /** BasePicker label */
-    label: PropTypes.string,
-
-    /** Should the picker appear disabled? */
-    isDisabled: PropTypes.bool,
-
-    /** Input value */
-    value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
-
-    /** The items to display in the list of selections */
-    items: PropTypes.arrayOf(
-        PropTypes.shape({
-            /** The value of the item that is being selected */
-            value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
-
-            /** The text to display for the item */
-            label: PropTypes.string.isRequired,
-        }),
-    ).isRequired,
-
-    /** Something to show as the placeholder before something is selected */
-    placeholder: PropTypes.shape({
-        /** The value of the placeholder item, usually an empty string */
-        value: PropTypes.string,
-
-        /** The text to be displayed as the placeholder */
-        label: PropTypes.string,
-    }),
-
-    /** Error text to display */
-    errorText: PropTypes.string,
-
-    /** Customize the BasePicker container */
-    // eslint-disable-next-line react/forbid-prop-types
-    containerStyles: PropTypes.arrayOf(PropTypes.object),
-
-    /** Customize the BasePicker background color */
-    backgroundColor: PropTypes.string,
-
-    /** The ID used to uniquely identify the input in a Form */
-    inputID: PropTypes.string,
-
-    /** Saves a draft of the input value when used in a form */
-    // eslint-disable-next-line react/no-unused-prop-types
-    shouldSaveDraft: PropTypes.bool,
-
-    /** A callback method that is called when the value changes and it receives the selected value as an argument */
-    onInputChange: PropTypes.func.isRequired,
-
-    /** Size of a picker component */
-    size: PropTypes.oneOf(['normal', 'small']),
-
-    /** An icon to display with the picker */
-    icon: PropTypes.func,
-
-    /** Whether we should forward the focus/blur calls to the inner picker * */
-    shouldFocusPicker: PropTypes.bool,
-
-    /** Callback called when click or tap out of BasePicker */
-    onBlur: PropTypes.func,
-
-    /** Additional events passed to the core BasePicker for specific platforms such as web */
-    additionalPickerEvents: PropTypes.func,
-
-    /** Hint text that appears below the picker */
-    hintText: PropTypes.string,
-};
-
-const defaultProps = {
-    forwardedRef: undefined,
-    label: '',
-    isDisabled: false,
-    errorText: '',
-    hintText: '',
-    containerStyles: [],
-    backgroundColor: undefined,
-    inputID: undefined,
-    shouldSaveDraft: false,
-    value: undefined,
-    placeholder: {},
-    size: 'normal',
-    icon: undefined,
-    shouldFocusPicker: false,
-    onBlur: () => {},
-    additionalPickerEvents: () => {},
-};
-
-function BasePicker(props) {
-    const styles = useThemeStyles();
-    const theme = useTheme();
-
-    const [isHighlighted, setIsHighlighted] = useState(false);
-
-    // reference to the root View
-    const root = useRef(null);
-
-    // reference to @react-native-picker/picker
-    const picker = useRef(null);
-
-    // Windows will reuse the text color of the select for each one of the options
-    // so we might need to color accordingly so it doesn't blend with the background.
-    const placeholder = _.isEmpty(props.placeholder)
-        ? {}
-        : {
-              ...props.placeholder,
-              color: theme.pickerOptionsTextColor,
-          };
-
-    useEffect(() => {
-        if (props.value || !props.items || props.items.length !== 1 || !props.onInputChange) {
-            return;
-        }
-
-        // When there is only 1 element in the selector, we do the user a favor and automatically select it for them
-        // so they don't have to spend extra time selecting the only possible value.
-        props.onInputChange(props.items[0].value, 0);
-
-        // eslint-disable-next-line react-hooks/exhaustive-deps
-    }, [props.items]);
-
-    const context = useContext(ScrollContext);
-
-    /**
-     * Forms use inputID to set values. But BasePicker passes an index as the second parameter to onInputChange
-     * We are overriding this behavior to make BasePicker work with Form
-     * @param {String} value
-     * @param {Number} index
-     */
-    const onInputChange = (value, index) => {
-        if (props.inputID) {
-            props.onInputChange(value);
-            return;
-        }
-
-        props.onInputChange(value, index);
-    };
-
-    const enableHighlight = () => {
-        setIsHighlighted(true);
-    };
-
-    const disableHighlight = () => {
-        setIsHighlighted(false);
-    };
-
-    const {icon, size} = props;
-
-    const iconToRender = useMemo(() => {
-        if (icon) {
-            return () => icon(size);
-        }
-
-        // eslint-disable-next-line react/display-name
-        return () => (
-            <Icon
-                src={Expensicons.DownArrow}
-                // eslint-disable-next-line react/jsx-props-no-spreading
-                {...(size === 'small'
-                    ? {
-                          width: styles.pickerSmall().icon.width,
-                          height: styles.pickerSmall().icon.height,
-                      }
-                    : {})}
-            />
-        );
-    }, [icon, size, styles]);
-
-    useImperativeHandle(props.forwardedRef, () => ({
-        /**
-         * Focuses the picker (if configured to do so)
-         *
-         * This method is used by Form
-         */
-        focus() {
-            if (!props.shouldFocusPicker) {
-                return;
-            }
-
-            // Defer the focusing to work around a bug on Mobile Safari, where focusing the `select` element in the
-            // same task when we scrolled to it left that element in a glitched state, where the dropdown list can't
-            // be opened until the element gets re-focused
-            _.defer(() => {
-                picker.current.focus();
-            });
-        },
-
-        /**
-         * Like measure(), but measures the view relative to an ancestor
-         *
-         * This method is used by Form when scrolling to the input
-         *
-         * @param {Object} relativeToNativeComponentRef - reference to an ancestor
-         * @param {function(x: number, y: number, width: number, height: number): void} onSuccess - callback called on success
-         * @param {function(): void} onFail - callback called on failure
-         */
-        measureLayout(relativeToNativeComponentRef, onSuccess, onFail) {
-            if (!root.current) {
-                return;
-            }
-
-            root.current.measureLayout(relativeToNativeComponentRef, onSuccess, onFail);
-        },
-    }));
-
-    const hasError = !_.isEmpty(props.errorText);
-
-    if (props.isDisabled) {
-        return (
-            <View>
-                {Boolean(props.label) && (
-                    <Text
-                        style={[styles.textLabelSupporting, styles.mb1]}
-                        numberOfLines={1}
-                    >
-                        {props.label}
-                    </Text>
-                )}
-                <Text numberOfLines={1}>{props.value}</Text>
-                {Boolean(props.hintText) && <Text style={[styles.textLabel, styles.colorMuted, styles.mt2]}>{props.hintText}</Text>}
-            </View>
-        );
-    }
-
-    return (
-        <>
-            <View
-                ref={root}
-                style={[
-                    styles.pickerContainer,
-                    props.isDisabled && styles.inputDisabled,
-                    ...props.containerStyles,
-                    isHighlighted && styles.borderColorFocus,
-                    hasError && styles.borderColorDanger,
-                ]}
-            >
-                {props.label && <Text style={[styles.pickerLabel, styles.textLabelSupporting, styles.pointerEventsNone]}>{props.label}</Text>}
-                <RNPickerSelect
-                    onValueChange={onInputChange}
-                    // We add a text color to prevent white text on white background dropdown items on Windows
-                    items={_.map(props.items, (item) => ({...item, color: theme.pickerOptionsTextColor}))}
-                    style={size === 'normal' ? styles.picker(props.isDisabled, props.backgroundColor) : styles.pickerSmall(props.backgroundColor)}
-                    useNativeAndroidPickerStyle={false}
-                    placeholder={placeholder}
-                    value={props.value}
-                    Icon={iconToRender}
-                    disabled={props.isDisabled}
-                    fixAndroidTouchableBug
-                    onOpen={enableHighlight}
-                    onClose={disableHighlight}
-                    textInputProps={{
-                        allowFontScaling: false,
-                    }}
-                    pickerProps={{
-                        ref: picker,
-                        tabIndex: -1,
-                        onFocus: enableHighlight,
-                        onBlur: () => {
-                            disableHighlight();
-                            props.onBlur();
-                        },
-                        ...props.additionalPickerEvents(enableHighlight, (value, index) => {
-                            onInputChange(value, index);
-                            disableHighlight();
-                        }),
-                    }}
-                    scrollViewRef={context && context.scrollViewRef}
-                    scrollViewContentOffsetY={context && context.contentOffsetY}
-                />
-            </View>
-            <FormHelpMessage message={props.errorText} />
-            {Boolean(props.hintText) && <Text style={[styles.textLabel, styles.colorMuted, styles.mt2]}>{props.hintText}</Text>}
-        </>
-    );
-}
-
-BasePicker.propTypes = propTypes;
-BasePicker.defaultProps = defaultProps;
-BasePicker.displayName = 'BasePicker';
-
-const BasePickerWithRef = React.forwardRef((props, ref) => (
-    <BasePicker
-        // eslint-disable-next-line react/jsx-props-no-spreading
-        {...props}
-        // Forward the ref to BasePicker, as we implement imperative methods there
-        forwardedRef={ref}
-        // eslint-disable-next-line react/prop-types
-        key={props.inputID}
-    />
-));
-
-BasePickerWithRef.displayName = 'BasePickerWithRef';
-
-export default BasePickerWithRef;

From 237bb9490e6123cc7de4c6e1d1fd4a53fe10139e Mon Sep 17 00:00:00 2001
From: Viktoryia Kliushun <vikstash@gmail.com>
Date: Tue, 5 Dec 2023 21:49:36 +0100
Subject: [PATCH 19/19] Update hooks call order to follow main updates

---
 src/components/Picker/BasePicker.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/Picker/BasePicker.tsx b/src/components/Picker/BasePicker.tsx
index 30227a19ce4f..dfb2d6332da5 100644
--- a/src/components/Picker/BasePicker.tsx
+++ b/src/components/Picker/BasePicker.tsx
@@ -34,8 +34,8 @@ function BasePicker<TPickerValue>(
     }: BasePickerProps<TPickerValue>,
     ref: ForwardedRef<BasePickerHandle>,
 ) {
-    const styles = useThemeStyles();
     const theme = useTheme();
+    const styles = useThemeStyles();
 
     const [isHighlighted, setIsHighlighted] = useState(false);
 
