Skip to content

Commit

Permalink
Revert RoomNameInput changes
Browse files Browse the repository at this point in the history
  • Loading branch information
kowczarz committed Dec 12, 2023
1 parent 02756ba commit c56d3af
Show file tree
Hide file tree
Showing 8 changed files with 212 additions and 135 deletions.
32 changes: 9 additions & 23 deletions src/components/Form/FormProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,12 +229,7 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC
inputValues[inputID] = propsToParse.defaultValue;
} else if (_.isUndefined(inputValues[inputID])) {
// We want to initialize the input value if it's undefined
const initialValue = _.isUndefined(propsToParse.defaultValue) ? getInitialValueByType(propsToParse.valueType) : propsToParse.defaultValue;

inputValues[inputID] = _.isFunction(propsToParse.valueParser) ? propsToParse.valueParser(initialValue) : initialValue;
if (_.isFunction(propsToParse.displayParser)) {
inputValues[`${inputID}ToDisplay`] = propsToParse.displayParser(initialValue);
}
inputValues[inputID] = _.isUndefined(propsToParse.defaultValue) ? getInitialValueByType(propsToParse.valueType) : propsToParse.defaultValue;
}

const errorFields = lodashGet(formState, 'errorFields', {});
Expand All @@ -247,8 +242,6 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC
.first()
.value() || '';

const value = !_.isUndefined(inputValues[`${inputID}ToDisplay`]) ? inputValues[`${inputID}ToDisplay`] : inputValues[inputID];

return {
...propsToParse,
ref:
Expand All @@ -261,8 +254,7 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC
inputID,
key: propsToParse.key || inputID,
errorText: errors[inputID] || fieldErrorMessage,
value,
// As the text input is controlled, we never set the defaultValue prop
value: inputValues[inputID], // As the text input is controlled, we never set the defaultValue prop
// as this is already happening by the value prop.
defaultValue: undefined,
onTouched: (event) => {
Expand Down Expand Up @@ -321,19 +313,13 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC
propsToParse.onBlur(event);
}
},
onInputChange: (inputValue, key) => {
onInputChange: (value, key) => {
const inputKey = key || inputID;
setInputValues((prevState) => {
const newState = _.isFunction(propsToParse.valueParser)
? {
...prevState,
[inputKey]: propsToParse.valueParser(inputValue),
[`${inputKey}ToDisplay`]: _.isFunction(propsToParse.displayParser) ? propsToParse.displayParser(inputValue) : inputValue,
}
: {
...prevState,
[inputKey]: inputValue,
};
const newState = {
...prevState,
[inputKey]: value,
};

if (shouldValidateOnChange) {
onValidate(newState);
Expand All @@ -342,11 +328,11 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC
});

if (propsToParse.shouldSaveDraft) {
FormActions.setDraftValues(formID, {[inputKey]: _.isFunction(propsToParse.valueParser) ? propsToParse.valueParser(inputValue) : inputValue});
FormActions.setDraftValues(formID, {[inputKey]: value});
}

if (_.isFunction(propsToParse.onValueChange)) {
propsToParse.onValueChange(inputValue, inputKey);
propsToParse.onValueChange(value, inputKey);
}
},
};
Expand Down
4 changes: 0 additions & 4 deletions src/components/Form/InputWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,11 @@ const propTypes = {
inputID: PropTypes.string.isRequired,
valueType: PropTypes.string,
forwardedRef: refPropTypes,
valueParser: PropTypes.func,
displayParser: PropTypes.func,
};

const defaultProps = {
forwardedRef: undefined,
valueType: 'string',
valueParser: undefined,
displayParser: undefined,
};

function InputWrapper(props) {
Expand Down
97 changes: 0 additions & 97 deletions src/components/RoomNameInput.js

This file was deleted.

86 changes: 86 additions & 0 deletions src/components/RoomNameInput/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React, {useState} from 'react';
import _ from 'underscore';
import TextInput from '@components/TextInput';
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';

function RoomNameInput({value, isFocused, autoFocus, disabled, forwardedRef, onBlur, shouldDelayFocus, onChangeText, onInputChange, ...restProps}) {
const {translate} = useLocalize();

const keyboardType = getOperatingSystem() === CONST.OS.IOS ? CONST.KEYBOARD_TYPE.ASCII_CAPABLE : CONST.KEYBOARD_TYPE.VISIBLE_PASSWORD;
const [selection, setSelection] = useState();

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

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

// Prevent cursor jump behaviour:
// Check if newRoomNameWithHash is the same as modifiedRoomName
// If it is then the room name is valid (does not contain unallowed characters); no action required
// If not then the room name contains unvalid characters and we must adjust the cursor position manually
// Read more: https://github.com/Expensify/App/issues/12741
const oldRoomNameWithHash = value || '';
const newRoomNameWithHash = `${CONST.POLICY.ROOM_PREFIX}${roomName}`;
if (modifiedRoomName !== newRoomNameWithHash) {
const offset = modifiedRoomName.length - oldRoomNameWithHash.length;
const newSelection = {
start: selection.start + offset,
end: selection.end + offset,
};
setSelection(newSelection);
}
};
return (
<TextInput
// eslint-disable-next-line react/jsx-props-no-spreading
{...restProps}
ref={forwardedRef}
disabled={disabled}
label={translate('newRoomPage.roomName')}
accessibilityLabel={translate('newRoomPage.roomName')}
role={CONST.ACCESSIBILITY_ROLE.TEXT}
placeholder={translate('newRoomPage.social')}
autoCapitalize="none"
value={value.substring(1)}
onChange={setModifiedRoomName}
selection={selection}
onBlur={(event) => isFocused && onBlur(event)}
shouldDelayFocus={shouldDelayFocus}
autoFocus={isFocused && autoFocus}
maxLength={CONST.REPORT.MAX_ROOM_NAME_LENGTH}
onSelectionChange={(event) => setSelection(event.nativeEvent.selection)}
spellCheck={false}
shouldInterceptSwipe
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;
68 changes: 68 additions & 0 deletions src/components/RoomNameInput/index.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react';
import _ from 'underscore';
import TextInput from '@components/TextInput';
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';

function RoomNameInput({isFocused, autoFocus, disabled, errorText, forwardedRef, value, onBlur, onChangeText, onInputChange, shouldDelayFocus, ...restProps}) {
const {translate} = useLocalize();

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

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

const keyboardType = getOperatingSystem() === CONST.OS.IOS ? CONST.KEYBOARD_TYPE.ASCII_CAPABLE : CONST.KEYBOARD_TYPE.VISIBLE_PASSWORD;

return (
<TextInput
// eslint-disable-next-line react/jsx-props-no-spreading
{...restProps}
ref={forwardedRef}
disabled={disabled}
label={translate('newRoomPage.roomName')}
accessibilityLabel={translate('newRoomPage.roomName')}
role={CONST.ACCESSIBILITY_ROLE.TEXT}
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}
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)}
autoFocus={isFocused && autoFocus}
autoCapitalize="none"
shouldDelayFocus={shouldDelayFocus}
/>
);
}

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;
48 changes: 48 additions & 0 deletions src/components/RoomNameInput/roomNameInputPropTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import PropTypes from 'prop-types';

const propTypes = {
/** Callback to execute when the text input is modified correctly */
onChangeText: PropTypes.func,

/** Room name to show in input field. This should include the '#' already prefixed to the name */
value: PropTypes.string,

/** Whether we should show the input as disabled */
disabled: PropTypes.bool,

/** Error text to show */
errorText: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object]))]),

/** A ref forwarded to the TextInput */
forwardedRef: PropTypes.func,

/** The ID used to uniquely identify the input in a Form */
inputID: PropTypes.string,

/** Callback that is called when the text input is blurred */
onBlur: PropTypes.func,

/** AutoFocus */
autoFocus: PropTypes.bool,

/** Whether we should wait before focusing the TextInput, useful when using transitions on Android */
shouldDelayFocus: PropTypes.bool,

/** Whether navigation is focused */
isFocused: PropTypes.bool.isRequired,
};

const defaultProps = {
onChangeText: () => {},
value: '',
disabled: false,
errorText: '',
forwardedRef: () => {},

inputID: undefined,
onBlur: () => {},
autoFocus: false,
shouldDelayFocus: false,
};

export {propTypes, defaultProps};
Loading

0 comments on commit c56d3af

Please sign in to comment.