Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TS migration] Migrate 'TextInput' component to TypeScript #31356

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
326e443
ref: migrating TextInput to TS
kubabutkiewicz Nov 15, 2023
61fd846
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Nov 15, 2023
255058f
fix: fixing types problems in BaseTextInput for Web
kubabutkiewicz Nov 15, 2023
0e5fe98
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Nov 16, 2023
b573de9
fix: move inputProps const into native ones
kubabutkiewicz Nov 16, 2023
b568d32
fix: destructure props
kubabutkiewicz Nov 16, 2023
e90c503
fix: fixing types
kubabutkiewicz Nov 17, 2023
6a3a270
fix: resolving types issues
kubabutkiewicz Nov 17, 2023
aaaeac0
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Dec 1, 2023
5b562b7
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Dec 4, 2023
bddaeb2
fix: types
kubabutkiewicz Dec 4, 2023
2bcc1c7
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Dec 5, 2023
4bc71cd
fix: removed commented code
kubabutkiewicz Dec 5, 2023
bbe0c5d
fix: resolve comments
kubabutkiewicz Dec 5, 2023
1f5cac7
fix: address comments
kubabutkiewicz Dec 6, 2023
fc794e3
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Dec 7, 2023
052610c
fix: resolve comments
kubabutkiewicz Dec 7, 2023
0b22b3b
fix: lint error
kubabutkiewicz Dec 7, 2023
cd39877
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Dec 7, 2023
c8fa81e
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Dec 11, 2023
12f6197
fix: types
kubabutkiewicz Dec 11, 2023
b48cee2
fix: bring back proptypes as they are used in different file
kubabutkiewicz Dec 11, 2023
f327d28
fix: tests
kubabutkiewicz Dec 11, 2023
b4597cb
fix: import types
kubabutkiewicz Dec 11, 2023
3abace7
fix: import types
kubabutkiewicz Dec 11, 2023
11bf2ef
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Dec 12, 2023
cba69d0
fix: resolve comments
kubabutkiewicz Dec 12, 2023
886aa60
fix: resolved comments
kubabutkiewicz Dec 12, 2023
ac1564e
fix: fixed default values for props
kubabutkiewicz Dec 13, 2023
e596427
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Dec 13, 2023
d69e087
fix: adress comments
kubabutkiewicz Dec 13, 2023
aad9c1d
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Dec 14, 2023
fa251df
fix: typecheck
kubabutkiewicz Dec 14, 2023
648100c
fix: address comments
kubabutkiewicz Dec 15, 2023
ccd8f60
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Dec 18, 2023
31ad5b6
fix: issue with not expanding multiline textinput
kubabutkiewicz Dec 18, 2023
ee63669
fix: type issue
kubabutkiewicz Dec 18, 2023
c02470c
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Dec 19, 2023
b6ba4e1
fix: error message color
kubabutkiewicz Dec 19, 2023
2632af4
Merge branch 'main' of github.com:kubabutkiewicz/expensify-app into t…
kubabutkiewicz Dec 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/components/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {ForwardedRef, forwardRef, KeyboardEvent as ReactKeyboardEvent} from 'react';
import React, {type ForwardedRef, forwardRef, type MouseEventHandler, type KeyboardEvent as ReactKeyboardEvent} from 'react';
import {GestureResponderEvent, StyleProp, View, ViewStyle} from 'react-native';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
Expand Down Expand Up @@ -29,7 +29,7 @@ type CheckboxProps = Partial<ChildrenProps> & {
containerStyle?: StyleProp<ViewStyle>;

/** Callback that is called when mousedown is triggered. */
onMouseDown?: () => void;
onMouseDown?: MouseEventHandler;

/** The size of the checkbox container */
containerSize?: number;
Expand Down
6 changes: 4 additions & 2 deletions src/components/RNTextInput.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React, {ForwardedRef} from 'react';
import React, {Component, ForwardedRef} from 'react';
// eslint-disable-next-line no-restricted-imports
import {TextInput, TextInputProps} from 'react-native';
import Animated, {AnimatedProps} from 'react-native-reanimated';
import useTheme from '@hooks/useTheme';

type AnimatedTextInputRef = Component<AnimatedProps<TextInputProps>>;
// Convert the underlying TextInput into an Animated component so that we can take an animated ref and pass it to a worklet
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput);

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function RNTextInputWithRef(props: TextInputProps, ref: ForwardedRef<React.Component<AnimatedProps<TextInputProps>>>) {
const theme = useTheme();

Expand All @@ -31,3 +31,5 @@ function RNTextInputWithRef(props: TextInputProps, ref: ForwardedRef<React.Compo
RNTextInputWithRef.displayName = 'RNTextInputWithRef';

export default React.forwardRef(RNTextInputWithRef);

export type {AnimatedTextInputRef};

Large diffs are not rendered by default.

Large diffs are not rendered by default.

116 changes: 116 additions & 0 deletions src/components/TextInput/BaseTextInput/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import React from 'react';
import type {Component, ForwardedRef} from 'react';
import type {GestureResponderEvent, StyleProp, TextInputProps, TextStyle, ViewStyle} from 'react-native';
import type {AnimatedProps} from 'react-native-reanimated';
import type {SrcProps} from '@components/Icon';
import type {MaybePhraseKey} from '@libs/Localize';

type CustomBaseTextInputProps = {
/** Input label */
label?: string;

/** Name attribute for the input */
name?: string;

/** Input value */
value?: string;

/** Default value - used for non controlled inputs */
defaultValue?: string;

/** Input value placeholder */
placeholder?: string;

/** Error text to display */
errorText?: MaybePhraseKey;

/** Icon to display in right side of text input */
icon: ((props: SrcProps) => React.ReactNode) | null;

/** Customize the TextInput container */
textInputContainerStyles?: StyleProp<ViewStyle>;

/** Customize the main container */
containerStyles?: StyleProp<ViewStyle>;

/** input style */
inputStyle?: StyleProp<TextStyle>;

/** If present, this prop forces the label to remain in a position where it will not collide with input text */
forceActiveLabel?: boolean;

/** Should the input auto focus? */
autoFocus?: boolean;

/** Disable the virtual keyboard */
disableKeyboard?: boolean;

/**
* Autogrow input container length based on the entered text.
* Note: If you use this prop, the text input has to be controlled
* by a value prop.
*/
autoGrow?: boolean;

/**
* Autogrow input container height based on the entered text
* Note: If you use this prop, the text input has to be controlled
* by a value prop.
*/
autoGrowHeight?: boolean;

/** Hide the focus styles on TextInput */
hideFocusedState?: boolean;

/** Hint text to display below the TextInput */
hint?: string;

/** Prefix character */
prefixCharacter?: string;

/** Whether autoCorrect functionality should enable */
autoCorrect?: boolean;

/** Form props */
/** 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;

/** Callback to update the value on Form when input is used in the Form component. */
onInputChange?: (value: string) => void;

/** Whether we should wait before focusing the TextInput, useful when using transitions */
shouldDelayFocus?: boolean;

/** Indicate whether pressing Enter on multiline input is allowed to submit the form. */
submitOnEnter?: boolean;

/** Indicate whether input is multiline */
multiline?: boolean;

/** Set the default value to the input if there is a valid saved value */
shouldUseDefaultValue?: boolean;

/** Indicate whether or not the input should prevent swipe actions in tabs */
shouldInterceptSwipe?: boolean;

/** Should there be an error displayed */
hasError?: boolean;

/** On Press handler */
onPress?: (event: GestureResponderEvent | KeyboardEvent) => void;

/** Should loading state should be displayed */
isLoading?: boolean;

/** Type of autocomplete */
autoCompleteType?: string;
};

type BaseTextInputRef = ForwardedRef<HTMLFormElement | Component<AnimatedProps<TextInputProps>>>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super NAB - can we use AnimatedTextInputRef here like this?

Suggested change
type BaseTextInputRef = ForwardedRef<HTMLFormElement | Component<AnimatedProps<TextInputProps>>>;
type BaseTextInputRef = ForwardedRef<HTMLFormElement | AnimatedTextInputRef>;


type BaseTextInputProps = CustomBaseTextInputProps & TextInputProps;

export type {CustomBaseTextInputProps, BaseTextInputRef, BaseTextInputProps};

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import React, {useState} from 'react';
import {Animated} from 'react-native';
import * as styleConst from '@components/TextInput/styleConst';
import useThemeStyles from '@hooks/useThemeStyles';
import * as TextInputLabelPropTypes from './TextInputLabelPropTypes';
import type TextInputLabelProps from './types';

function TextInputLabel(props) {
function TextInputLabel({isLabelActive, label, labelScale, labelTranslateY}: TextInputLabelProps) {
const styles = useThemeStyles();
const [width, setWidth] = useState(0);

Expand All @@ -17,29 +17,27 @@ function TextInputLabel(props) {
style={[
styles.textInputLabel,
styles.textInputLabelTransformation(
props.labelTranslateY,
props.labelScale.interpolate({
labelTranslateY,
labelScale.interpolate({
inputRange: [styleConst.ACTIVE_LABEL_SCALE, styleConst.INACTIVE_LABEL_SCALE],
outputRange: [-(width - width * styleConst.ACTIVE_LABEL_SCALE) / 2, 0],
}),
props.labelScale,
labelScale,
),
// If the label is active but the width is not ready yet, the above translateX value will be 0,
// making the label sits at the top center instead of the top left of the input. To solve it
// move the label by a percentage value with left style as translateX doesn't support percentage value.
width === 0 &&
props.isLabelActive && {
isLabelActive && {
left: `${-((1 - styleConst.ACTIVE_LABEL_SCALE) * 100) / 2}%`,
},
]}
>
{props.label}
{label}
</Animated.Text>
);
}

TextInputLabel.propTypes = TextInputLabelPropTypes.propTypes;
TextInputLabel.defaultProps = TextInputLabelPropTypes.defaultProps;
TextInputLabel.displayName = 'TextInputLabel';

export default TextInputLabel;
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React, {useEffect, useRef} from 'react';
import {Animated} from 'react-native';
import {Animated, Text} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import {defaultProps, propTypes} from './TextInputLabelPropTypes';
import type TextInputLabelProps from './types';

function TextInputLabel({for: inputId, label, labelTranslateY, labelScale}) {
function TextInputLabel({for: inputId = '', label, labelTranslateY, labelScale}: TextInputLabelProps) {
const styles = useThemeStyles();
const labelRef = useRef(null);
const labelRef = useRef<Text & HTMLFormElement>(null);

useEffect(() => {
if (!inputId || !labelRef.current) {
Expand All @@ -28,7 +28,5 @@ function TextInputLabel({for: inputId, label, labelTranslateY, labelScale}) {
}

TextInputLabel.displayName = 'TextInputLabel';
TextInputLabel.propTypes = propTypes;
TextInputLabel.defaultProps = defaultProps;

export default React.memo(TextInputLabel);
20 changes: 20 additions & 0 deletions src/components/TextInput/TextInputLabel/types.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NAB - most prop descriptions in this file are pretty weak / not descriptive. Maybe they came from somewhere else in the codebase (a.k.a. weren't added new here?) but it would be best to try to make them as clear as possible... Probably in a follow-up PR

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {Animated} from 'react-native';

type TextInputLabelProps = {
/** Label */
label: string;

/** Label vertical translate */
labelTranslateY: Animated.Value;

/** Label scale */
labelScale: Animated.Value;

/** Whether the label is currently active or not */
isLabelActive: boolean;

/** For attribute for label */
for?: string;
};

export default TextInputLabelProps;
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import React, {forwardRef, useEffect} from 'react';
import {AppState, Keyboard} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';
import BaseTextInput from './BaseTextInput';
import * as baseTextInputPropTypes from './BaseTextInput/baseTextInputPropTypes';
import type {BaseTextInputProps, BaseTextInputRef} from './BaseTextInput/types';

const TextInput = forwardRef((props, ref) => {
function TextInput(props: BaseTextInputProps, ref: BaseTextInputRef) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NAB - Question: Why aren't we destructuring props here, but we do in other files like TextInputLabel?

const styles = useThemeStyles();

useEffect(() => {
if (!props.disableKeyboard) {
return;
Expand All @@ -30,15 +31,13 @@ const TextInput = forwardRef((props, ref) => {
{...props}
// Setting autoCompleteType to new-password throws an error on Android/iOS, so fall back to password in that case
// eslint-disable-next-line react/jsx-props-no-multi-spaces
ref={ref}
autoCompleteType={props.autoCompleteType === 'new-password' ? 'password' : props.autoCompleteType}
innerRef={ref}
inputStyle={[styles.baseTextInput, ...props.inputStyle]}
inputStyle={[styles.baseTextInput, props.inputStyle]}
/>
);
});
}

TextInput.propTypes = baseTextInputPropTypes.propTypes;
TextInput.defaultProps = baseTextInputPropTypes.defaultProps;
TextInput.displayName = 'TextInput';

export default TextInput;
export default forwardRef(TextInput);
Loading
Loading