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

Form refactor bankaccountstep #10900

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c09bf34
bank account step form refactored
jayeshmangwani Sep 5, 2022
3f8c116
Merge branch 'main' into form_refactor_bankaccountstep
jayeshmangwani Sep 6, 2022
8808c9c
fixed console warnings & changed inputID defaultValue from undefined …
jayeshmangwani Sep 6, 2022
9bf7312
added shouldSaveDraft to inputs and checkbox
jayeshmangwani Sep 8, 2022
7ecc6c2
Merge branch 'main' into form_refactor_bankaccountstep
jayeshmangwani Sep 8, 2022
590d0df
resolved conflicts & replaced form key with REIMBURSEMENT_ACCOUNT key
jayeshmangwani Sep 15, 2022
f4134db
Merge branch 'main' into form_refactor_bankaccountstep
jayeshmangwani Sep 20, 2022
191739a
added isSubmitButtonVisible form prop
jayeshmangwani Sep 21, 2022
90057a2
Merge branch 'main' into form_refactor_bankaccountstep
jayeshmangwani Sep 21, 2022
2d99e38
skipped validation for plaid
jayeshmangwani Sep 23, 2022
cdb883a
removed inputID from AddPlaidBankAccount
jayeshmangwani Sep 23, 2022
2ef74ca
Merge branch 'main' into form_refactor_bankaccountstep
jayeshmangwani Sep 23, 2022
0e2b26f
added validate function to PLAID Form
jayeshmangwani Sep 24, 2022
4bdc2d7
Merge branch 'main' into form_refactor_bankaccountstep
jayeshmangwani Sep 24, 2022
5f08c30
updated onyx key name for bankaccount form
jayeshmangwani Sep 26, 2022
df9b2c0
merged & resolved conflicts, accepted incoming changes
jayeshmangwani Sep 26, 2022
84011f1
added form component to BankAccountManualStep & BankAccountPlaidStep
jayeshmangwani Sep 26, 2022
e19f42f
updated formID for plaid
jayeshmangwani Sep 26, 2022
e8c0496
Merge branch 'main' into form_refactor_bankaccountstep
jayeshmangwani Sep 26, 2022
58198e2
merged and resolved conflicts
jayeshmangwani Sep 27, 2022
27dddb3
updated ONYX key name from BANK_FORM to REIMBURSEMENT_ACCOUNT_FORM
jayeshmangwani Sep 28, 2022
ebcc540
Merge branch 'main' into form_refactor_bankaccountstep
jayeshmangwani Sep 28, 2022
44cc59d
resolved conflicts
jayeshmangwani Sep 29, 2022
fbd3ca4
Merge branch 'main' into form_refactor_bankaccountstep
jayeshmangwani Sep 30, 2022
bd2e691
merged code from Form and FormActions
jayeshmangwani Sep 30, 2022
08753f3
fixed newline lint issue
jayeshmangwani Sep 30, 2022
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
1 change: 1 addition & 0 deletions src/ONYXKEYS.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,5 +180,6 @@ export default {
FORMS: {
ADD_DEBIT_CARD_FORM: 'addDebitCardForm',
REQUEST_CALL_FORM: 'requestCallForm',
REIMBURSEMENT_ACCOUNT_FORM: 'reimbursementAccount',
},
};
24 changes: 18 additions & 6 deletions src/components/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {withOnyx} from 'react-native-onyx';
import compose from '../libs/compose';
import withLocalize, {withLocalizePropTypes} from './withLocalize';
import * as FormActions from '../libs/actions/FormActions';
import * as ErrorUtils from '../libs/ErrorUtils';
import styles from '../styles/styles';
import FormAlertWithSubmitButton from './FormAlertWithSubmitButton';

Expand All @@ -16,6 +17,9 @@ const propTypes = {
/** Text to be displayed in the submit button */
submitButtonText: PropTypes.string.isRequired,

/** Controls the submit button's visibility */
isSubmitButtonVisible: PropTypes.bool,

/** Callback to validate the form */
validate: PropTypes.func.isRequired,

Expand All @@ -32,8 +36,8 @@ const propTypes = {
/** Controls the loading state of the form */
isLoading: PropTypes.bool,

/** Server side error message */
error: PropTypes.string,
/** Server side errors keyed by microtime */
errors: PropTypes.objectOf(PropTypes.string),
}),

/** Contains draft values for each input in the form */
Expand All @@ -44,9 +48,10 @@ const propTypes = {
};

const defaultProps = {
isSubmitButtonVisible: true,
formState: {
isLoading: false,
error: '',
errors: null,
},
draftValues: {},
};
Expand Down Expand Up @@ -75,6 +80,11 @@ class Form extends React.Component {
this.touchedInputs[inputID] = true;
}

getErrorMessage() {
const latestErrorMessage = ErrorUtils.getLatestErrorMessage(this.props.formState);
return this.props.formState.error || (typeof latestErrorMessage === 'string' ? latestErrorMessage : '');
Copy link
Contributor

Choose a reason for hiding this comment

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

Doing a refactor in #20228 , I got an eslint error related to this line :

ESLint: 'formState.error' is missing in props validation (react/prop-types)

I guess this should be updated to this.props.formState.errors . But as I don't have much context, I would appreciate further clarification on this issue.

cc @jayeshmangwani @luacmartins

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jayeshmangwani feel free to merge contents of Form and FormActions into this PR then we should be all set to merge!

I am not sure about this change, I copied these changes from this Form.js file and @youssef-lr may have the full context of these changes

Copy link
Contributor

Choose a reason for hiding this comment

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

We're removing this.props.formState.error from that line in https://github.com/Expensify/App/pull/20091/files. See this comment.

Copy link
Contributor

Choose a reason for hiding this comment

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

That’s perfect.

}

submit() {
// Return early if the form is already submitting to avoid duplicate submission
if (this.props.formState.isLoading) {
Expand All @@ -100,7 +110,7 @@ class Form extends React.Component {
* @returns {Object} - An object containing the errors for each inputID, e.g. {inputID1: error1, inputID2: error2}
*/
validate(values) {
FormActions.setErrorMessage(this.props.formID, '');
FormActions.setErrors(this.props.formID, null);
const validationErrors = this.props.validate(values);

if (!_.isObject(validationErrors)) {
Expand Down Expand Up @@ -184,17 +194,19 @@ class Form extends React.Component {
>
<View style={[this.props.style]}>
{this.childrenWrapperWithProps(this.props.children)}
{this.props.isSubmitButtonVisible && (
<FormAlertWithSubmitButton
buttonText={this.props.submitButtonText}
isAlertVisible={_.size(this.state.errors) > 0 || Boolean(this.props.formState.error)}
isAlertVisible={_.size(this.state.errors) > 0 || Boolean(this.getErrorMessage())}
isLoading={this.props.formState.isLoading}
message={this.props.formState.error}
message={this.getErrorMessage()}
onSubmit={this.submit}
onFixTheErrorsLinkPressed={() => {
this.inputRefs[_.first(_.keys(this.state.errors))].focus();
}}
containerStyles={[styles.mh0, styles.mt5]}
/>
)}
</View>
</ScrollView>
</>
Expand Down
8 changes: 4 additions & 4 deletions src/libs/actions/FormActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ function setIsLoading(formID, isLoading) {

/**
* @param {String} formID
* @param {String} error
* @param {String} errors
*/
function setErrorMessage(formID, error) {
Onyx.merge(formID, {error});
function setErrors(formID, errors) {
Onyx.merge(formID, {errors});
}

/**
Expand All @@ -26,6 +26,6 @@ function setDraftValues(formID, draftValues) {

export {
setIsLoading,
setErrorMessage,
setErrors,
setDraftValues,
};
88 changes: 30 additions & 58 deletions src/pages/ReimbursementAccount/BankAccountManualStep.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import _ from 'underscore';
import React from 'react';
import {Image, View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
Expand All @@ -15,9 +14,8 @@ import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize
import * as ValidationUtils from '../../libs/ValidationUtils';
import compose from '../../libs/compose';
import ONYXKEYS from '../../ONYXKEYS';
import * as ReimbursementAccount from '../../libs/actions/ReimbursementAccount';
import exampleCheckImage from './exampleCheckImage';
import ReimbursementAccountForm from './ReimbursementAccountForm';
import Form from '../../components/Form';
import * as ReimbursementAccountUtils from '../../libs/ReimbursementAccountUtils';

const propTypes = {
Expand All @@ -27,69 +25,40 @@ const propTypes = {
class BankAccountManualStep extends React.Component {
constructor(props) {
super(props);

this.submit = this.submit.bind(this);
this.clearErrorAndSetValue = this.clearErrorAndSetValue.bind(this);
this.getErrorText = inputKey => ReimbursementAccountUtils.getErrorText(this.props, this.errorTranslationKeys, inputKey);
this.state = {
acceptTerms: ReimbursementAccountUtils.getDefaultStateForField(props, 'acceptTerms', true),
routingNumber: ReimbursementAccountUtils.getDefaultStateForField(props, 'routingNumber'),
accountNumber: ReimbursementAccountUtils.getDefaultStateForField(props, 'accountNumber'),
};

// Map a field to the key of the error's translation
this.errorTranslationKeys = {
routingNumber: 'bankAccount.error.routingNumber',
accountNumber: 'bankAccount.error.accountNumber',
acceptTerms: 'common.error.acceptedTerms',
};
this.validate = this.validate.bind(this);
}

/**
* @returns {Boolean}
* @param {Object} values - form input values passed by the Form component
* @returns {Object}
*/
validate() {
validate(values) {
const errorFields = {};
const routingNumber = this.state.routingNumber.trim();
const routingNumber = values.routingNumber && values.routingNumber.trim();

if (!CONST.BANK_ACCOUNT.REGEX.US_ACCOUNT_NUMBER.test(this.state.accountNumber.trim())) {
errorFields.accountNumber = true;
if (!values.accountNumber || !CONST.BANK_ACCOUNT.REGEX.US_ACCOUNT_NUMBER.test(values.accountNumber.trim())) {
errorFields.accountNumber = this.props.translate('bankAccount.error.accountNumber');
}
if (!CONST.BANK_ACCOUNT.REGEX.SWIFT_BIC.test(routingNumber) || !ValidationUtils.isValidRoutingNumber(routingNumber)) {
errorFields.routingNumber = true;
if (!routingNumber || !CONST.BANK_ACCOUNT.REGEX.SWIFT_BIC.test(routingNumber) || !ValidationUtils.isValidRoutingNumber(routingNumber)) {
errorFields.routingNumber = this.props.translate('bankAccount.error.routingNumber');
}
if (!this.state.acceptTerms) {
errorFields.acceptTerms = true;
if (!values.acceptedTerms) {
errorFields.acceptedTerms = this.props.translate('common.error.acceptedTerms');
}

ReimbursementAccount.setBankAccountFormValidationErrors(errorFields);

return _.size(errorFields) === 0;
return errorFields;
}

submit() {
if (!this.validate()) {
return;
}
submit(values) {
BankAccounts.connectBankAccountManually(
ReimbursementAccountUtils.getDefaultStateForField(this.props, 'bankAccountID', 0),
this.state.accountNumber,
this.state.routingNumber,
values.accountNumber,
values.routingNumber,
ReimbursementAccountUtils.getDefaultStateForField(this.props, 'plaidMask'),
);
}

/**
* @param {String} inputKey
* @param {String} value
*/
clearErrorAndSetValue(inputKey, value) {
const newState = {[inputKey]: value};
this.setState(newState);
ReimbursementAccount.updateReimbursementAccountDraft(newState);
ReimbursementAccountUtils.clearError(this.props, inputKey);
}

render() {
const shouldDisableInputs = Boolean(ReimbursementAccountUtils.getDefaultStateForField(this.props, 'bankAccountID'));

Expand All @@ -104,7 +73,13 @@ class BankAccountManualStep extends React.Component {
onBackButtonPress={() => BankAccounts.setBankAccountSubStep(null)}
onCloseButtonPress={Navigation.dismissModal}
/>
<ReimbursementAccountForm onSubmit={this.submit}>
<Form
formID={ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM}
onSubmit={this.submit}
validate={this.validate}
submitButtonText={this.props.translate('common.saveAndContinue')}
style={[styles.mh5, styles.flexGrow1]}
>
<Text style={[styles.mb5]}>
{this.props.translate('bankAccount.checkHelpLine')}
</Text>
Expand All @@ -114,26 +89,23 @@ class BankAccountManualStep extends React.Component {
source={exampleCheckImage(this.props.preferredLocale)}
/>
<TextInput
inputID="routingNumber"
label={this.props.translate('bankAccount.routingNumber')}
keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
defaultValue={this.state.routingNumber}
onChangeText={value => this.clearErrorAndSetValue('routingNumber', value)}
disabled={shouldDisableInputs}
errorText={this.getErrorText('routingNumber')}
shouldSaveDraft
/>
<TextInput
inputID="accountNumber"
containerStyles={[styles.mt4]}
label={this.props.translate('bankAccount.accountNumber')}
keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
defaultValue={this.state.accountNumber}
onChangeText={value => this.clearErrorAndSetValue('accountNumber', value)}
disabled={shouldDisableInputs}
errorText={this.getErrorText('accountNumber')}
shouldSaveDraft
/>
<CheckboxWithLabel
style={styles.mt4}
isChecked={this.state.acceptTerms}
onInputChange={value => this.clearErrorAndSetValue('acceptTerms', value)}
inputID="acceptedTerms"
LabelComponent={() => (
<View style={[styles.flexRow, styles.alignItemsCenter]}>
<Text>
Expand All @@ -144,9 +116,9 @@ class BankAccountManualStep extends React.Component {
</TextLink>
</View>
)}
errorText={this.getErrorText('acceptTerms')}
shouldSaveDraft
/>
</ReimbursementAccountForm>
</Form>
</>
);
}
Expand Down
14 changes: 11 additions & 3 deletions src/pages/ReimbursementAccount/BankAccountPlaidStep.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import compose from '../../libs/compose';
import ONYXKEYS from '../../ONYXKEYS';
import AddPlaidBankAccount from '../../components/AddPlaidBankAccount';
import * as ReimbursementAccount from '../../libs/actions/ReimbursementAccount';
import ReimbursementAccountForm from './ReimbursementAccountForm';
import * as ReimbursementAccountUtils from '../../libs/ReimbursementAccountUtils';
import Form from '../../components/Form';
import styles from '../../styles/styles';

const propTypes = {
/** The OAuth URI + stateID needed to re-initialize the PlaidLink after the user logs into their bank */
Expand Down Expand Up @@ -69,7 +70,14 @@ class BankAccountPlaidStep extends React.Component {
onBackButtonPress={() => BankAccounts.setBankAccountSubStep(null)}
onCloseButtonPress={Navigation.dismissModal}
/>
<ReimbursementAccountForm onSubmit={this.submit} hideSubmitButton={_.isUndefined(this.props.plaidData.selectedPlaidBankAccount)}>
<Form
formID={ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM}
validate={() => ({})}
luacmartins marked this conversation as resolved.
Show resolved Hide resolved
onSubmit={this.submit}
submitButtonText={this.props.translate('common.saveAndContinue')}
style={[styles.mh5, styles.flexGrow1]}
isSubmitButtonVisible={!_.isUndefined(this.props.plaidData.selectedPlaidBankAccount)}
>
<AddPlaidBankAccount
text={this.props.translate('bankAccount.plaidBodyCopy')}
onSelect={(params) => {
Expand All @@ -81,7 +89,7 @@ class BankAccountPlaidStep extends React.Component {
allowDebit
bankAccountID={bankAccountID}
/>
</ReimbursementAccountForm>
</Form>
</>
);
}
Expand Down