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 8 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
5 changes: 5 additions & 0 deletions src/components/AddPlaidBankAccount.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,14 @@ const propTypes = {
/** Are we adding a withdrawal account? */
allowDebit: PropTypes.bool,

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

...withLocalizePropTypes,
};

const defaultProps = {
inputID: null,
plaidData: {
bankName: '',
plaidAccessToken: '',
Expand Down Expand Up @@ -191,6 +195,7 @@ class AddPlaidBankAccount extends React.Component {
</View>
<View style={[styles.mb5]}>
<Picker
inputID={this.props.inputID}
label={this.props.translate('addPersonalBankAccountPage.chooseAccountLabel')}
onInputChange={this.selectAccount}
items={options}
Expand Down
28 changes: 17 additions & 11 deletions src/components/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ const propTypes = {
// eslint-disable-next-line react/forbid-prop-types
draftValues: PropTypes.object,

/** Should we show the form button */
isSubmitButtonVisible: PropTypes.bool,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this needs to be moved below submitButtonText, as now it sits under the /* Onyx Props */ comment but it's not an Onyx prop.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this change has been done


...withLocalizePropTypes,
};

Expand All @@ -49,6 +52,7 @@ const defaultProps = {
error: '',
},
draftValues: {},
isSubmitButtonVisible: true,
};

class Form extends React.Component {
Expand Down Expand Up @@ -184,17 +188,19 @@ class Form extends React.Component {
>
<View style={[this.props.style]}>
{this.childrenWrapperWithProps(this.props.children)}
<FormAlertWithSubmitButton
buttonText={this.props.submitButtonText}
isAlertVisible={_.size(this.state.errors) > 0 || Boolean(this.props.formState.error)}
isLoading={this.props.formState.isLoading}
message={this.props.formState.error}
onSubmit={this.submit}
onFixTheErrorsLinkPressed={() => {
this.inputRefs[_.first(_.keys(this.state.errors))].focus();
}}
containerStyles={[styles.mh0, styles.mt5]}
/>
{this.props.isSubmitButtonVisible && (
<FormAlertWithSubmitButton
buttonText={this.props.submitButtonText}
isAlertVisible={_.size(this.state.errors) > 0 || Boolean(this.props.formState.error)}
isLoading={this.props.formState.isLoading}
message={this.props.formState.error}
onSubmit={this.submit}
onFixTheErrorsLinkPressed={() => {
this.inputRefs[_.first(_.keys(this.state.errors))].focus();
}}
containerStyles={[styles.mh0, styles.mt5]}
/>
)}
</View>
</ScrollView>
</>
Expand Down
165 changes: 58 additions & 107 deletions src/pages/ReimbursementAccount/BankAccountStep.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,16 @@ import Text from '../../components/Text';
import * as BankAccounts from '../../libs/actions/BankAccounts';
import ONYXKEYS from '../../ONYXKEYS';
import compose from '../../libs/compose';
import * as ReimbursementAccountUtils from '../../libs/ReimbursementAccountUtils';
import ReimbursementAccountForm from './ReimbursementAccountForm';
import reimbursementAccountPropTypes from './reimbursementAccountPropTypes';
import Section from '../../components/Section';
import * as ValidationUtils from '../../libs/ValidationUtils';
import * as Illustrations from '../../components/Icon/Illustrations';
import getPlaidDesktopMessage from '../../libs/getPlaidDesktopMessage';
import CONFIG from '../../CONFIG';
import ROUTES from '../../ROUTES';
import Button from '../../components/Button';
import FormScrollView from '../../components/FormScrollView';
import FormAlertWithSubmitButton from '../../components/FormAlertWithSubmitButton';
import Form from '../../components/Form';

const propTypes = {
/** Bank account currently in setup */
// eslint-disable-next-line react/no-unused-prop-types
reimbursementAccount: reimbursementAccountPropTypes.isRequired,
luacmartins marked this conversation as resolved.
Show resolved Hide resolved

/** The OAuth URI + stateID needed to re-initialize the PlaidLink after the user logs into their bank */
receivedRedirectURI: PropTypes.string,

Expand All @@ -65,79 +57,50 @@ class BankAccountStep extends React.Component {
constructor(props) {
super(props);

this.toggleTerms = this.toggleTerms.bind(this);
this.addManualAccount = this.addManualAccount.bind(this);
this.addPlaidAccount = this.addPlaidAccount.bind(this);
this.validate = this.validate.bind(this);
this.validatePlaidAccount = this.validatePlaidAccount.bind(this);
this.state = {
selectedPlaidBankAccount: undefined,
hasAcceptedTerms: ReimbursementAccountUtils.getDefaultStateForField(props, 'acceptTerms', true),
luacmartins marked this conversation as resolved.
Show resolved Hide resolved
routingNumber: ReimbursementAccountUtils.getDefaultStateForField(props, 'routingNumber'),
accountNumber: ReimbursementAccountUtils.getDefaultStateForField(props, 'accountNumber'),
};

// Keys in this.errorTranslationKeys are associated to inputs, they are a subset of the keys found in this.state
this.errorTranslationKeys = {
routingNumber: 'bankAccount.error.routingNumber',
accountNumber: 'bankAccount.error.accountNumber',
hasAcceptedTerms: 'common.error.acceptedTerms',
};

this.getErrorText = inputKey => ReimbursementAccountUtils.getErrorText(this.props, this.errorTranslationKeys, inputKey);
this.clearError = inputKey => ReimbursementAccountUtils.clearError(this.props, inputKey);
this.getErrors = () => ReimbursementAccountUtils.getErrors(this.props);
}

toggleTerms() {
this.setState((prevState) => {
const hasAcceptedTerms = !prevState.hasAcceptedTerms;
BankAccounts.updateReimbursementAccountDraft({acceptTerms: hasAcceptedTerms});
return {hasAcceptedTerms};
});
this.clearError('hasAcceptedTerms');
}

/**
* @returns {Boolean}
* @param {Object} values - form input values passed by the Form component
* @returns {Object}
*/
validate() {
validate(values) {
const errors = {};

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

BankAccounts.setBankAccountFormValidationErrors(errors);
return _.size(errors) === 0;
}

/**
* Clear the error associated to inputKey if found and store the inputKey new value in the state.
*
* @param {String} inputKey
* @param {String} value
*/
clearErrorAndSetValue(inputKey, value) {
const newState = {[inputKey]: value};
this.setState(newState);
BankAccounts.updateReimbursementAccountDraft(newState);
this.clearError(inputKey);
return errors;
}

addManualAccount() {
if (!this.validate()) {
return;
validatePlaidAccount() {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can skip validating Plaid account, as we don't allow the user to continue unless they choose an account. I don't see a way of triggering the validation error if it exists. @luacmartins what do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes I think we can skip this, removed validatePlaidAccount for now

Copy link
Contributor

Choose a reason for hiding this comment

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

Makes sense since we can't display that error and the user can't continue without selecting an account.

const selectedPlaidBankAccount = this.state.selectedPlaidBankAccount;
const errors = {};
if (_.isUndefined(selectedPlaidBankAccount)) {
errors.selectedPlaidBankAccount = this.props.translate('bankAccount.error.noBankAccountSelected');
}

return errors;
}

addManualAccount(values) {
BankAccounts.setupWithdrawalAccount({
acceptTerms: this.state.hasAcceptedTerms,
accountNumber: this.state.accountNumber,
routingNumber: this.state.routingNumber,
acceptTerms: values.acceptedTerms,
accountNumber: values.accountNumber,
routingNumber: values.routingNumber,
setupType: CONST.BANK_ACCOUNT.SETUP_TYPE.MANUAL,

// Note: These are hardcoded as we're not supporting AU bank accounts for the free plan
Expand All @@ -149,6 +112,7 @@ class BankAccountStep extends React.Component {

/**
* Add the Bank account retrieved via Plaid in db
* @param {Object} values - form input values passed by the Form component
*/
addPlaidAccount() {
const selectedPlaidBankAccount = this.state.selectedPlaidBankAccount;
Expand Down Expand Up @@ -185,8 +149,6 @@ class BankAccountStep extends React.Component {
const subStep = shouldReinitializePlaidLink ? CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID : this.props.achData.subStep;
const plaidDesktopMessage = getPlaidDesktopMessage();
const bankAccountRoute = `${CONFIG.EXPENSIFY.NEW_EXPENSIFY_URL}${ROUTES.BANK_ACCOUNT}`;
const error = lodashGet(this.props, 'reimbursementAccount.error', '');
const loading = lodashGet(this.props, 'reimbursementAccount.loading', false);
const validated = lodashGet(this.props, 'user.validated', false);
return (
<View style={[styles.flex1, styles.justifyContentBetween]}>
Expand Down Expand Up @@ -274,36 +236,34 @@ class BankAccountStep extends React.Component {
</ScrollView>
)}
{subStep === CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID && (
<FormScrollView>
<View style={[styles.mh5, styles.mb5, styles.flex1]}>
<AddPlaidBankAccount
text={this.props.translate('bankAccount.plaidBodyCopy')}
onSelect={(params) => {
this.setState({
selectedPlaidBankAccount: params.selectedPlaidBankAccount,
});
}}
onExitPlaid={() => BankAccounts.setBankAccountSubStep(null)}
receivedRedirectURI={this.props.receivedRedirectURI}
plaidLinkOAuthToken={this.props.plaidLinkOAuthToken}
allowDebit
/>
</View>
{!_.isUndefined(this.state.selectedPlaidBankAccount) && (
luacmartins marked this conversation as resolved.
Show resolved Hide resolved
<FormAlertWithSubmitButton
isAlertVisible={Boolean(error)}
buttonText={this.props.translate('common.saveAndContinue')}
onSubmit={this.addPlaidAccount}
message={error}
isLoading={loading}
/>
)}
</FormScrollView>
<Form
formID={ONYXKEYS.REIMBURSEMENT_ACCOUNT}
validate={this.validatePlaidAccount}
onSubmit={this.addPlaidAccount}
submitButtonText={this.props.translate('common.saveAndContinue')}
style={[styles.mh5, styles.flexGrow1]}
isSubmitButtonVisible={!_.isUndefined(this.state.selectedPlaidBankAccount)}
>
<AddPlaidBankAccount
onSelect={(params) => {
this.setState({
selectedPlaidBankAccount: params.selectedPlaidBankAccount,
});
}}
onExitPlaid={() => BankAccounts.setBankAccountSubStep(null)}
receivedRedirectURI={this.props.receivedRedirectURI}
plaidLinkOAuthToken={this.props.plaidLinkOAuthToken}
allowDebit
/>
</Form>
)}
{subStep === CONST.BANK_ACCOUNT.SETUP_TYPE.MANUAL && (
<ReimbursementAccountForm
reimbursementAccount={this.props.reimbursementAccount}
<Form
formID={ONYXKEYS.REIMBURSEMENT_ACCOUNT}
luacmartins marked this conversation as resolved.
Show resolved Hide resolved
validate={this.validate}
onSubmit={this.addManualAccount}
submitButtonText={this.props.translate('common.saveAndContinue')}
style={[styles.mh5, styles.flexGrow1]}
>
<Text style={[styles.mb5]}>
{this.props.translate('bankAccount.checkHelpLine')}
Expand All @@ -314,26 +274,23 @@ class BankAccountStep extends React.Component {
source={exampleCheckImage(this.props.preferredLocale)}
/>
<TextInput
inputID="routingNumber"
label={this.props.translate('bankAccount.routingNumber')}
keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
value={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}
value={this.state.accountNumber}
onChangeText={value => this.clearErrorAndSetValue('accountNumber', value)}
disabled={shouldDisableInputs}
errorText={this.getErrorText('accountNumber')}
shouldSaveDraft
/>
<CheckboxWithLabel
style={styles.mt4}
isChecked={this.state.hasAcceptedTerms}
onInputChange={this.toggleTerms}
inputID="acceptedTerms"
LabelComponent={() => (
<View style={[styles.flexRow, styles.alignItemsCenter]}>
<Text>
Expand All @@ -344,9 +301,9 @@ class BankAccountStep extends React.Component {
</TextLink>
</View>
)}
errorText={this.getErrorText('hasAcceptedTerms')}
shouldSaveDraft
/>
</ReimbursementAccountForm>
</Form>
)}
</View>
);
Expand All @@ -359,12 +316,6 @@ BankAccountStep.defaultProps = defaultProps;
export default compose(
withLocalize,
withOnyx({
reimbursementAccount: {
key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
},
reimbursementAccountDraft: {
key: ONYXKEYS.REIMBURSEMENT_ACCOUNT_DRAFT,
},
user: {
key: ONYXKEYS.USER,
},
Expand Down