diff --git a/src/components/Form/FormProvider.js b/src/components/Form/FormProvider.js index add58dbef18c..ce402976d097 100644 --- a/src/components/Form/FormProvider.js +++ b/src/components/Form/FormProvider.js @@ -229,13 +229,15 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC .first() .value() || ''; + const value = !_.isUndefined(inputValues[`${inputID}ToDisplay`]) ? inputValues[`${inputID}ToDisplay`] : inputValues[inputID]; + return { ...propsToParse, ref: newRef, inputID, key: propsToParse.key || inputID, errorText: errors[inputID] || fieldErrorMessage, - value: inputValues[inputID], + value, // As the text input is controlled, we never set the defaultValue prop // as this is already happening by the value prop. defaultValue: undefined, @@ -275,13 +277,19 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC propsToParse.onBlur(event); } }, - onInputChange: (value, key) => { + onInputChange: (inputValue, key) => { const inputKey = key || inputID; setInputValues((prevState) => { - const newState = { - ...prevState, - [inputKey]: value, - }; + const newState = _.isFunction(propsToParse.valueParser) + ? { + ...prevState, + [inputKey]: propsToParse.valueParser(inputValue), + [`${inputKey}ToDisplay`]: inputValue, + } + : { + ...prevState, + [inputKey]: inputValue, + }; if (shouldValidateOnChange) { onValidate(newState); @@ -290,11 +298,11 @@ function FormProvider({validate, formID, shouldValidateOnBlur, shouldValidateOnC }); if (propsToParse.shouldSaveDraft) { - FormActions.setDraftValues(propsToParse.formID, {[inputKey]: value}); + FormActions.setDraftValues(propsToParse.formID, {[inputKey]: inputValue}); } if (_.isFunction(propsToParse.onValueChange)) { - propsToParse.onValueChange(value, inputKey); + propsToParse.onValueChange(inputValue, inputKey); } }, }; diff --git a/src/components/Form/InputWrapper.js b/src/components/Form/InputWrapper.js index 8a87bc2f5a5a..74a741239a3f 100644 --- a/src/components/Form/InputWrapper.js +++ b/src/components/Form/InputWrapper.js @@ -7,11 +7,13 @@ const propTypes = { inputID: PropTypes.string.isRequired, valueType: PropTypes.string, forwardedRef: PropTypes.oneOfType([PropTypes.func, PropTypes.shape({current: PropTypes.instanceOf(React.Component)})]), + valueParser: PropTypes.func, }; const defaultProps = { forwardedRef: undefined, valueType: 'string', + valueParser: undefined, }; function InputWrapper(props) { diff --git a/src/components/RoomNameInput/index.js b/src/components/RoomNameInput/index.js index ec9bf7a090ab..197b6b77acae 100644 --- a/src/components/RoomNameInput/index.js +++ b/src/components/RoomNameInput/index.js @@ -1,49 +1,23 @@ -import React, {useState} from 'react'; -import _ from 'underscore'; +import React from 'react'; import CONST from '../../CONST'; import TextInput from '../TextInput'; import useLocalize from '../../hooks/useLocalize'; import * as roomNameInputPropTypes from './roomNameInputPropTypes'; +import InputWrapper from '../Form/InputWrapper'; +import getOperatingSystem from '../../libs/getOperatingSystem'; import * as RoomNameInputUtils from '../../libs/RoomNameInputUtils'; -function RoomNameInput({isFocused, autoFocus, disabled, errorText, forwardedRef, value, onBlur, onChangeText, onInputChange, shouldDelayFocus}) { +function RoomNameInput({isFocused, autoFocus, disabled, errorText, forwardedRef, onBlur, shouldDelayFocus, inputID}) { const {translate} = useLocalize(); - const [selection, setSelection] = useState(); + const keyboardType = getOperatingSystem() === CONST.OS.IOS ? CONST.KEYBOARD_TYPE.ASCII_CAPABLE : CONST.KEYBOARD_TYPE.VISIBLE_PASSWORD; - /** - * 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); - } - }; + const valueParser = (roomName) => RoomNameInputUtils.modifyRoomName(roomName); return ( - setSelection(event.nativeEvent.selection)} errorText={errorText} + valueParser={valueParser} autoCapitalize="none" onBlur={() => isFocused && onBlur()} shouldDelayFocus={shouldDelayFocus} @@ -63,6 +34,7 @@ function RoomNameInput({isFocused, autoFocus, disabled, errorText, forwardedRef, maxLength={CONST.REPORT.MAX_ROOM_NAME_LENGTH} spellCheck={false} shouldInterceptSwipe + keyboardType={keyboardType} // this is a bit hacky solution to a RN issue https://github.com/facebook/react-native/issues/27449 /> ); } diff --git a/src/components/RoomNameInput/index.native.js b/src/components/RoomNameInput/index.native.js deleted file mode 100644 index 9e83a673982c..000000000000 --- a/src/components/RoomNameInput/index.native.js +++ /dev/null @@ -1,66 +0,0 @@ -import React from 'react'; -import _ from 'underscore'; -import CONST from '../../CONST'; -import useLocalize from '../../hooks/useLocalize'; -import TextInput from '../TextInput'; -import * as roomNameInputPropTypes from './roomNameInputPropTypes'; -import * as RoomNameInputUtils from '../../libs/RoomNameInputUtils'; -import getOperatingSystem from '../../libs/getOperatingSystem'; - -function RoomNameInput({isFocused, autoFocus, disabled, errorText, forwardedRef, value, onBlur, onChangeText, onInputChange, shouldDelayFocus}) { - 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 ( - isFocused && onBlur()} - autoFocus={isFocused && autoFocus} - autoCapitalize="none" - shouldDelayFocus={shouldDelayFocus} - /> - ); -} - -RoomNameInput.propTypes = roomNameInputPropTypes.propTypes; -RoomNameInput.defaultProps = roomNameInputPropTypes.defaultProps; -RoomNameInput.displayName = 'RoomNameInput'; - -const RoomNameInputWithRef = React.forwardRef((props, ref) => ( - -)); - -RoomNameInputWithRef.displayName = 'RoomNameInputWithRef'; - -export default RoomNameInputWithRef; diff --git a/src/components/RoomNameInput/roomNameInputPropTypes.js b/src/components/RoomNameInput/roomNameInputPropTypes.js index 7f8292f0123e..3d1ad18d27b3 100644 --- a/src/components/RoomNameInput/roomNameInputPropTypes.js +++ b/src/components/RoomNameInput/roomNameInputPropTypes.js @@ -1,4 +1,5 @@ import PropTypes from 'prop-types'; +import refPropTypes from '../refPropTypes'; const propTypes = { /** Callback to execute when the text input is modified correctly */ @@ -14,10 +15,10 @@ const propTypes = { errorText: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object]))]), /** A ref forwarded to the TextInput */ - forwardedRef: PropTypes.func, + forwardedRef: refPropTypes, /** The ID used to uniquely identify the input in a Form */ - inputID: PropTypes.string, + inputID: PropTypes.string.isRequired, /** Callback that is called when the text input is blurred */ onBlur: PropTypes.func, @@ -39,7 +40,6 @@ const defaultProps = { errorText: '', forwardedRef: () => {}, - inputID: undefined, onBlur: () => {}, autoFocus: false, shouldDelayFocus: false, diff --git a/src/pages/settings/Report/RoomNamePage.js b/src/pages/settings/Report/RoomNamePage.js index 4ce997533378..8163b09ca943 100644 --- a/src/pages/settings/Report/RoomNamePage.js +++ b/src/pages/settings/Report/RoomNamePage.js @@ -7,7 +7,6 @@ import CONST from '../../../CONST'; import ScreenWrapper from '../../../components/ScreenWrapper'; import HeaderWithBackButton from '../../../components/HeaderWithBackButton'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; -import Form from '../../../components/Form'; import ONYXKEYS from '../../../ONYXKEYS'; import styles from '../../../styles/styles'; import Navigation from '../../../libs/Navigation/Navigation'; @@ -21,6 +20,7 @@ import * as Report from '../../../libs/actions/Report'; import RoomNameInput from '../../../components/RoomNameInput'; import * as ReportUtils from '../../../libs/ReportUtils'; import FullPageNotFoundView from '../../../components/BlockingViews/FullPageNotFoundView'; +import FormProvider from '../../../components/Form/FormProvider'; const propTypes = { ...withLocalizePropTypes, @@ -90,7 +90,7 @@ function RoomNamePage(props) { title={translate('newRoomPage.roomName')} onBackButtonPress={() => Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(report.reportID))} /> -
Report.updatePolicyRoomNameAndNavigate(report, values.roomName)} @@ -100,13 +100,13 @@ function RoomNamePage(props) { > (roomNameInputRef.current = ref)} + ref={roomNameInputRef} inputID="roomName" defaultValue={report.reportName} isFocused={isFocused} /> -
+ ); diff --git a/src/pages/workspace/WorkspaceNewRoomPage.js b/src/pages/workspace/WorkspaceNewRoomPage.js index c059a54b7c21..548b00254721 100644 --- a/src/pages/workspace/WorkspaceNewRoomPage.js +++ b/src/pages/workspace/WorkspaceNewRoomPage.js @@ -20,13 +20,14 @@ import * as ErrorUtils from '../../libs/ErrorUtils'; import * as ValidationUtils from '../../libs/ValidationUtils'; import * as ReportUtils from '../../libs/ReportUtils'; import * as PolicyUtils from '../../libs/PolicyUtils'; -import Form from '../../components/Form'; import policyMemberPropType from '../policyMemberPropType'; import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; import compose from '../../libs/compose'; import variables from '../../styles/variables'; import useDelayedInputFocus from '../../hooks/useDelayedInputFocus'; import ValuePicker from '../../components/ValuePicker'; +import FormProvider from '../../components/Form/FormProvider'; +import InputWrapper from '../../components/Form/InputWrapper'; const propTypes = { /** All reports shared with the user */ @@ -183,7 +184,7 @@ function WorkspaceNewRoomPage(props) { // This is because when wrapping whole screen the screen was freezing when changing Tabs. keyboardVerticalOffset={variables.contentHeaderHeight + variables.tabSelectorButtonHeight + variables.tabSelectorButtonPadding + insets.top} > -
(roomNameInputRef.current = el)} + ref={roomNameInputRef} inputID="roomName" isFocused={props.isFocused} shouldDelayFocus @@ -201,7 +202,8 @@ function WorkspaceNewRoomPage(props) { /> - - {isPolicyAdmin && ( - )} - {visibilityDescription} - + )}