Skip to content

Commit

Permalink
Merge pull request #36676 from software-mansion-labs/ts/migrate-room-…
Browse files Browse the repository at this point in the history
…name-input

[TS Migration] Migrate 'RoomNameInput' to TypeScript
  • Loading branch information
rlinoz authored Feb 28, 2024
2 parents 9eb9b0a + 117c171 commit 3389b90
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 125 deletions.
6 changes: 5 additions & 1 deletion src/components/Form/InputWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ import type {ComponentPropsWithoutRef, ComponentType, ForwardedRef} from 'react'
import React, {forwardRef, useContext} from 'react';
import type {AnimatedTextInputRef} from '@components/RNTextInput';
import RoomNameInput from '@components/RoomNameInput';
import type RoomNameInputProps from '@components/RoomNameInput/types';
import TextInput from '@components/TextInput';
import type {BaseTextInputProps} from '@components/TextInput/BaseTextInput/types';
import {canUseTouchScreen} from '@libs/DeviceCapabilities';
import FormContext from './FormContext';
import type {InputComponentBaseProps, InputComponentValueProps, ValidInputs, ValueTypeKey} from './types';

const textInputBasedComponents: ComponentType[] = [TextInput, RoomNameInput];
type TextInputBasedComponents = [ComponentType<BaseTextInputProps>, ComponentType<RoomNameInputProps>];

const textInputBasedComponents: TextInputBasedComponents = [TextInput, RoomNameInput];

type ComputedComponentSpecificRegistrationParams = {
shouldSubmitForm: boolean;
Expand Down
4 changes: 3 additions & 1 deletion src/components/Form/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type CheckboxWithLabel from '@components/CheckboxWithLabel';
import type CountrySelector from '@components/CountrySelector';
import type Picker from '@components/Picker';
import type RadioButtons from '@components/RadioButtons';
import type RoomNameInput from '@components/RoomNameInput';
import type SingleChoiceQuestion from '@components/SingleChoiceQuestion';
import type StatePicker from '@components/StatePicker';
import type TextInput from '@components/TextInput';
Expand All @@ -22,7 +23,7 @@ import type {BaseForm} from '@src/types/form/Form';
* when adding new inputs or removing old ones.
*
* TODO: Add remaining inputs here once these components are migrated to Typescript:
* EmojiPickerButtonDropdown | RoomNameInput | ValuePicker
* EmojiPickerButtonDropdown
*/
type ValidInputs =
| typeof TextInput
Expand All @@ -35,6 +36,7 @@ type ValidInputs =
| typeof AmountForm
| typeof BusinessTypePicker
| typeof StatePicker
| typeof RoomNameInput
| typeof ValuePicker
| typeof RadioButtons;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
import type {ForwardedRef} from 'react';
import React from 'react';
import _ from 'underscore';
import type {NativeSyntheticEvent, TextInputChangeEventData} from 'react-native';
import TextInput from '@components/TextInput';
import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types';
import useLocalize from '@hooks/useLocalize';
import getOperatingSystem from '@libs/getOperatingSystem';
import * as RoomNameInputUtils from '@libs/RoomNameInputUtils';
import CONST from '@src/CONST';
import * as roomNameInputPropTypes from './roomNameInputPropTypes';
import type RoomNameInputProps from './types';

function RoomNameInput({isFocused, autoFocus, disabled, errorText, forwardedRef, value, onBlur, onChangeText, onInputChange, onSubmitEditing, returnKeyType, shouldDelayFocus}) {
function RoomNameInput(
{disabled = false, autoFocus = false, shouldDelayFocus = false, isFocused, value, onBlur, onChangeText, onInputChange, ...props}: RoomNameInputProps,
ref: ForwardedRef<BaseTextInputRef>,
) {
const {translate} = useLocalize();

/**
* Calls the onChangeText callback with a modified room name
* @param {Event} event
*/
const setModifiedRoomName = (event) => {
const setModifiedRoomName = (event: NativeSyntheticEvent<TextInputChangeEventData>) => {
const roomName = event.nativeEvent.text;
const modifiedRoomName = RoomNameInputUtils.modifyRoomName(roomName);
onChangeText(modifiedRoomName);
onChangeText?.(modifiedRoomName);

// if custom component has onInputChange, use it to trigger changes (Form input)
if (_.isFunction(onInputChange)) {
if (typeof onInputChange === 'function') {
onInputChange(modifiedRoomName);
}
};
Expand All @@ -29,40 +33,27 @@ function RoomNameInput({isFocused, autoFocus, disabled, errorText, forwardedRef,

return (
<TextInput
ref={forwardedRef}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
ref={ref}
disabled={disabled}
label={translate('newRoomPage.roomName')}
accessibilityLabel={translate('newRoomPage.roomName')}
role={CONST.ACCESSIBILITY_ROLE.TEXT}
role={CONST.ROLE.PRESENTATION}
prefixCharacter={CONST.POLICY.ROOM_PREFIX}
placeholder={translate('newRoomPage.social')}
onChange={setModifiedRoomName}
value={value.substring(1)} // Since the room name always starts with a prefix, we omit the first character to avoid displaying it twice.
errorText={errorText}
value={value?.substring(1)} // Since the room name always starts with a prefix, we omit the first character to avoid displaying it twice.
maxLength={CONST.REPORT.MAX_ROOM_NAME_LENGTH}
keyboardType={keyboardType} // this is a bit hacky solution to a RN issue https://github.com/facebook/react-native/issues/27449
onBlur={(event) => isFocused && onBlur(event)}
onSubmitEditing={onSubmitEditing}
returnKeyType={returnKeyType}
onBlur={(event) => isFocused && onBlur?.(event)}
autoFocus={isFocused && autoFocus}
autoCapitalize="none"
shouldDelayFocus={shouldDelayFocus}
autoCapitalize="none"
onChange={setModifiedRoomName}
keyboardType={keyboardType} // this is a bit hacky solution to a RN issue https://github.com/facebook/react-native/issues/27449
/>
);
}

RoomNameInput.propTypes = roomNameInputPropTypes.propTypes;
RoomNameInput.defaultProps = roomNameInputPropTypes.defaultProps;
RoomNameInput.displayName = 'RoomNameInput';

const RoomNameInputWithRef = React.forwardRef((props, ref) => (
<RoomNameInput
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));

RoomNameInputWithRef.displayName = 'RoomNameInputWithRef';

export default RoomNameInputWithRef;
export default React.forwardRef(RoomNameInput);
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
import React, {useState} from 'react';
import _ from 'underscore';
import type {ForwardedRef} from 'react';
import type {NativeSyntheticEvent, TextInputChangeEventData} from 'react-native';
import TextInput from '@components/TextInput';
import useLocalize from '@hooks/useLocalize';
import type {Selection} from '@libs/ComposerUtils';
import * as RoomNameInputUtils from '@libs/RoomNameInputUtils';
import type {BaseTextInputRef} from '@src/components/TextInput/BaseTextInput/types';
import CONST from '@src/CONST';
import * as roomNameInputPropTypes from './roomNameInputPropTypes';
import type RoomNameInputProps from './types';

function RoomNameInput({isFocused, autoFocus, disabled, errorText, forwardedRef, value, onBlur, onChangeText, onInputChange, onSubmitEditing, returnKeyType, shouldDelayFocus}) {
function RoomNameInput(
{disabled = false, autoFocus = false, shouldDelayFocus = false, isFocused, value = '', onBlur, onChangeText, onInputChange, ...props}: RoomNameInputProps,
ref: ForwardedRef<BaseTextInputRef>,
) {
const {translate} = useLocalize();

const [selection, setSelection] = useState();
const [selection, setSelection] = useState<Selection>({start: value.length - 1, end: value.length - 1});

/**
* Calls the onChangeText callback with a modified room name
* @param {Event} event
*/
const setModifiedRoomName = (event) => {
const setModifiedRoomName = (event: NativeSyntheticEvent<TextInputChangeEventData>) => {
const roomName = event.nativeEvent.text;
const modifiedRoomName = RoomNameInputUtils.modifyRoomName(roomName);
onChangeText(modifiedRoomName);
onChangeText?.(modifiedRoomName);

// if custom component has onInputChange, use it to trigger changes (Form input)
if (_.isFunction(onInputChange)) {
if (typeof onInputChange === 'function') {
onInputChange(modifiedRoomName);
}

Expand All @@ -30,7 +34,7 @@ function RoomNameInput({isFocused, autoFocus, disabled, errorText, forwardedRef,
// If it is, then the room name is valid (does not contain forbidden characters) – no action required
// If not, then the room name contains invalid characters, and we must adjust the cursor position manually
// Read more: https://github.com/Expensify/App/issues/12741
const oldRoomNameWithHash = value || '';
const oldRoomNameWithHash = value ?? '';
const newRoomNameWithHash = `${CONST.POLICY.ROOM_PREFIX}${roomName}`;
if (modifiedRoomName !== newRoomNameWithHash) {
const offset = modifiedRoomName.length - oldRoomNameWithHash.length;
Expand All @@ -41,43 +45,30 @@ function RoomNameInput({isFocused, autoFocus, disabled, errorText, forwardedRef,

return (
<TextInput
ref={forwardedRef}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
ref={ref}
disabled={disabled}
label={translate('newRoomPage.roomName')}
accessibilityLabel={translate('newRoomPage.roomName')}
role={CONST.ROLE.PRESENTATION}
prefixCharacter={CONST.POLICY.ROOM_PREFIX}
placeholder={translate('newRoomPage.social')}
value={value?.substring(1)} // Since the room name always starts with a prefix, we omit the first character to avoid displaying it twice.
maxLength={CONST.REPORT.MAX_ROOM_NAME_LENGTH}
onBlur={(event) => isFocused && onBlur?.(event)}
autoFocus={isFocused && autoFocus}
shouldDelayFocus={shouldDelayFocus}
autoCapitalize="none"
onChange={setModifiedRoomName}
value={value.substring(1)} // Since the room name always starts with a prefix, we omit the first character to avoid displaying it twice.
selection={selection}
onSelectionChange={(event) => setSelection(event.nativeEvent.selection)}
onSubmitEditing={onSubmitEditing}
returnKeyType={returnKeyType}
errorText={errorText}
autoCapitalize="none"
onBlur={(event) => isFocused && onBlur(event)}
shouldDelayFocus={shouldDelayFocus}
autoFocus={isFocused && autoFocus}
maxLength={CONST.REPORT.MAX_ROOM_NAME_LENGTH}
selection={selection}
spellCheck={false}
shouldInterceptSwipe
/>
);
}

RoomNameInput.propTypes = roomNameInputPropTypes.propTypes;
RoomNameInput.defaultProps = roomNameInputPropTypes.defaultProps;
RoomNameInput.displayName = 'RoomNameInput';

const RoomNameInputWithRef = React.forwardRef((props, ref) => (
<RoomNameInput
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));

RoomNameInputWithRef.displayName = 'RoomNameInputWithRef';

export default RoomNameInputWithRef;
export default React.forwardRef(RoomNameInput);
58 changes: 0 additions & 58 deletions src/components/RoomNameInput/roomNameInputPropTypes.js

This file was deleted.

19 changes: 19 additions & 0 deletions src/components/RoomNameInput/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type {NativeSyntheticEvent, ReturnKeyTypeOptions, TextInputFocusEventData, TextInputSubmitEditingEventData} from 'react-native';
import type {MaybePhraseKey} from '@libs/Localize';

type RoomNameInputProps = {
value?: string;
disabled?: boolean;
errorText?: MaybePhraseKey;
onChangeText?: (value: string) => void;
onSubmitEditing?: (event: NativeSyntheticEvent<TextInputSubmitEditingEventData>) => void;
onInputChange?: (value: string) => void;
returnKeyType?: ReturnKeyTypeOptions;
inputID?: string;
onBlur?: (event: NativeSyntheticEvent<TextInputFocusEventData>) => void;
autoFocus?: boolean;
shouldDelayFocus?: boolean;
isFocused: boolean;
};

export default RoomNameInputProps;
1 change: 1 addition & 0 deletions src/libs/ComposerUtils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@ function findCommonSuffixLength(str1: string, str2: string, cursorPosition: numb
}

export {getNumberOfLines, updateNumberOfLines, insertText, canSkipTriggerHotkeys, insertWhiteSpaceAtIndex, findCommonSuffixLength};
export type {Selection};
1 change: 0 additions & 1 deletion src/pages/workspace/WorkspaceNewRoomPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,6 @@ function WorkspaceNewRoomPage({policies, reports, formState, session, activePoli
ref={inputCallbackRef}
inputID={INPUT_IDS.ROOM_NAME}
isFocused={isFocused}
// @ts-expect-error TODO: Remove this once RoomNameInput (https://github.com/Expensify/App/issues/25090) is migrated to TypeScript.
shouldDelayFocus
autoFocus
/>
Expand Down

0 comments on commit 3389b90

Please sign in to comment.