Skip to content

Commit

Permalink
Merge pull request #5219 from Expensify/rocio-SaveContinue
Browse files Browse the repository at this point in the history
Always make the Save & Continue button in the VBA flow active
  • Loading branch information
Jag96 authored Sep 16, 2021
2 parents f0cc89b + 67360cf commit 57eed8b
Show file tree
Hide file tree
Showing 21 changed files with 2,583 additions and 5,271 deletions.
7,289 changes: 2,209 additions & 5,080 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@
"@octokit/plugin-throttling": "^3.4.1",
"@octokit/rest": "^18.3.5",
"@react-native-community/eslint-config": "^2.0.0",
"@storybook/addon-a11y": "^6.2.9",
"@storybook/addon-essentials": "^6.3.7",
"@storybook/react": "^6.3.7",
"@storybook/addon-a11y": "^6.3.8",
"@storybook/addon-essentials": "^6.3.8",
"@storybook/react": "^6.3.8",
"@svgr/webpack": "^5.5.0",
"@testing-library/jest-native": "^3.4.2",
"@testing-library/react-native": "^7.0.2",
Expand Down
29 changes: 28 additions & 1 deletion src/components/AddPlaidBankAccount.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
clearPlaidBankAccountsAndToken,
fetchPlaidLinkToken,
getPlaidBankAccounts,
setBankAccountFormValidationErrors,
showBankAccountErrorModal,
} from '../libs/actions/BankAccounts';
import ONYXKEYS from '../ONYXKEYS';
import styles from '../styles/styles';
Expand All @@ -21,6 +23,7 @@ import withLocalize, {withLocalizePropTypes} from './withLocalize';
import Button from './Button';
import ExpensiPicker from './ExpensiPicker';
import Text from './Text';
import * as ReimbursementAccountUtils from '../libs/ReimbursementAccountUtils';

const propTypes = {
...withLocalizePropTypes,
Expand Down Expand Up @@ -95,6 +98,9 @@ class AddPlaidBankAccount extends React.Component {
isCreatingAccount: false,
institution: {},
};

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

componentDidMount() {
Expand All @@ -111,7 +117,24 @@ class AddPlaidBankAccount extends React.Component {
return lodashGet(this.props.plaidBankAccounts, 'accounts', []);
}

/**
* @returns {Boolean}
*/
validate() {
const errors = {};
if (_.isUndefined(this.state.selectedIndex)) {
errors.selectedBank = true;
}
setBankAccountFormValidationErrors(errors);
return _.size(errors) === 0;
}

selectAccount() {
if (!this.validate()) {
showBankAccountErrorModal();
return;
}

const account = this.getAccounts()[this.state.selectedIndex];
this.props.onSubmit({
account, plaidLinkToken: this.props.plaidLinkToken,
Expand Down Expand Up @@ -163,13 +186,15 @@ class AddPlaidBankAccount extends React.Component {
label={this.props.translate('addPersonalBankAccountPage.chooseAccountLabel')}
onChange={(index) => {
this.setState({selectedIndex: Number(index)});
this.clearError('selectedBank');
}}
items={options}
placeholder={_.isUndefined(this.state.selectedIndex) ? {
value: '',
label: this.props.translate('bankAccount.chooseAnAccount'),
} : {}}
value={this.state.selectedIndex}
hasError={this.getErrors().selectedBank}
/>
</View>
</View>
Expand All @@ -179,7 +204,6 @@ class AddPlaidBankAccount extends React.Component {
text={this.props.translate('common.saveAndContinue')}
isLoading={this.state.isCreatingAccount}
onPress={this.selectAccount}
isDisabled={_.isUndefined(this.state.selectedIndex)}
/>
</View>
</>
Expand All @@ -206,5 +230,8 @@ export default compose(
plaidBankAccounts: {
key: ONYXKEYS.PLAID_BANK_ACCOUNTS,
},
reimbursementAccount: {
key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
},
}),
)(AddPlaidBankAccount);
17 changes: 16 additions & 1 deletion src/components/Checkbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,35 @@ const propTypes = {

/** A function that is called when the box/label is pressed */
onPress: PropTypes.func.isRequired,

/** Should the input be styled for errors */
hasError: PropTypes.bool,
};

const defaultProps = {
hasError: false,
};

const Checkbox = ({
isChecked,
onPress,
hasError,
}) => (
<Pressable onPress={() => onPress(!isChecked)}>
<View style={[styles.checkboxContainer, isChecked && styles.checkedContainer]}>
<View
style={[
styles.checkboxContainer,
isChecked && styles.checkedContainer,
hasError && styles.borderColorDanger,
]}
>
<Icon src={Checkmark} fill="white" height={14} width={14} />
</View>
</Pressable>
);

Checkbox.propTypes = propTypes;
Checkbox.defaultProps = defaultProps;
Checkbox.displayName = 'Checkbox';

export default Checkbox;
70 changes: 43 additions & 27 deletions src/components/CheckboxWithLabel.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import _ from 'underscore';
import styles from '../styles/styles';
import Checkbox from './Checkbox';
import Text from './Text';
import InlineErrorText from './InlineErrorText';

const propTypes = {
/** Whether the checkbox is checked */
Expand All @@ -21,50 +22,65 @@ const propTypes = {

/** Component to display for label */
LabelComponent: PropTypes.func,

/** Should the input be styled for errors */
hasError: PropTypes.bool,

/** Error text to display */
errorText: PropTypes.string,
};

const defaultProps = {
style: [],
label: undefined,
LabelComponent: undefined,
hasError: false,
errorText: '',
};

const CheckboxWithLabel = ({
LabelComponent, isChecked, onPress, style, label,
LabelComponent, isChecked, onPress, style, label, hasError, errorText,
}) => {
const defaultStyles = [styles.flexRow, styles.alignItemsCenter];
const wrapperStyles = _.isArray(style) ? [...defaultStyles, ...style] : [...defaultStyles, style];

if (!label && !LabelComponent) {
throw new Error('Must provide at least label or LabelComponent prop');
}

return (
<View style={wrapperStyles}>
<Checkbox
isChecked={isChecked}
onPress={() => onPress(!isChecked)}
label={label}
/>
<TouchableOpacity
onPress={() => onPress(!isChecked)}
style={[
styles.ml2,
styles.pr2,
styles.w100,
styles.flexRow,
styles.flexWrap,
styles.alignItemsCenter,
]}
>
{label && (
<Text style={[styles.ml2]}>
{label}
</Text>
)}
{LabelComponent && (<LabelComponent />)}
</TouchableOpacity>
</View>
<>
<View style={wrapperStyles}>
<Checkbox
isChecked={isChecked}
onPress={() => onPress(!isChecked)}
label={label}
hasError={hasError}
/>
<TouchableOpacity
onPress={() => onPress(!isChecked)}
style={[
styles.ml3,
styles.pr2,
styles.w100,
styles.flexRow,
styles.flexWrap,
styles.alignItemsCenter,
]}
>
{label && (
<Text style={[styles.ml2]}>
{label}
</Text>
)}
{LabelComponent && (<LabelComponent />)}
</TouchableOpacity>
</View>
{!_.isEmpty(errorText) && (
<InlineErrorText>
{errorText}
</InlineErrorText>
)}
</>
);
};

Expand Down
52 changes: 35 additions & 17 deletions src/components/ExpensiPicker.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
import _ from 'underscore';
import React, {PureComponent} from 'react';
import {Text, View} from 'react-native';
import PropTypes from 'prop-types';
import Picker from './Picker';
import styles from '../styles/styles';
import InlineErrorText from './InlineErrorText';

const propTypes = {
/** Picker label */
label: PropTypes.string,

/** Should the picker appear disabled? */
isDisabled: PropTypes.bool,

/** Should the input be styled for errors */
hasError: PropTypes.bool,

/** Error text to display */
errorText: PropTypes.string,
};

const defaultProps = {
label: '',
isDisabled: false,
hasError: false,
errorText: '',
};

class ExpensiPicker extends PureComponent {
Expand All @@ -30,24 +40,32 @@ class ExpensiPicker extends PureComponent {
label, isDisabled, ...pickerProps
} = this.props;
return (
<View
style={[
styles.expensiPickerContainer,
this.state.isOpen && styles.borderColorFocus,
isDisabled && styles.inputDisabled,
]}
>
{label && (
<Text style={[styles.expensiPickerLabel, styles.textLabelSupporting]}>{label}</Text>
<>
<View
style={[
styles.expensiPickerContainer,
this.state.isOpen && styles.borderColorFocus,
isDisabled && styles.inputDisabled,
this.props.hasError && styles.borderColorDanger,
]}
>
{label && (
<Text style={[styles.expensiPickerLabel, styles.textLabelSupporting]}>{label}</Text>
)}
<Picker
onOpen={() => this.setState({isOpen: true})}
onClose={() => this.setState({isOpen: false})}
disabled={isDisabled}
// eslint-disable-next-line react/jsx-props-no-spreading
{...pickerProps}
/>
</View>
{!_.isEmpty(this.props.errorText) && (
<InlineErrorText>
{this.props.errorText}
</InlineErrorText>
)}
<Picker
onOpen={() => this.setState({isOpen: true})}
onClose={() => this.setState({isOpen: false})}
disabled={isDisabled}
// eslint-disable-next-line react/jsx-props-no-spreading
{...pickerProps}
/>
</View>
</>
);
}
}
Expand Down
9 changes: 6 additions & 3 deletions src/components/ExpensiTextInput/BaseExpensiTextInput.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import _ from 'underscore';
import React, {Component} from 'react';
import {
Animated, TextInput, View, TouchableWithoutFeedback,
} from 'react-native';
import Str from 'expensify-common/lib/str';
import ExpensiTextInputLabel from './ExpensiTextInputLabel';
import Text from '../Text';
import {propTypes, defaultProps} from './baseExpensiTextInputPropTypes';
import themeColors from '../../styles/themes/default';
import styles from '../../styles/styles';
import InlineErrorText from '../InlineErrorText';

const ACTIVE_LABEL_TRANSLATE_Y = -12;
const ACTIVE_LABEL_TRANSLATE_X = (translateX = -22) => translateX;
Expand Down Expand Up @@ -174,8 +175,10 @@ class BaseExpensiTextInput extends Component {
</View>
</TouchableWithoutFeedback>
</View>
{Boolean(errorText) && (
<Text style={[styles.formError, styles.mt1]}>{errorText}</Text>
{!_.isEmpty(errorText) && (
<InlineErrorText>
{errorText}
</InlineErrorText>
)}
</View>
);
Expand Down
29 changes: 29 additions & 0 deletions src/components/InlineErrorText.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import _ from 'underscore';
import React from 'react';
import PropTypes from 'prop-types';
import styles from '../styles/styles';
import Text from './Text';

const propTypes = {
/** Text to display */
children: PropTypes.string,
};

const defaultProps = {
children: 'Error',
};

const InlineErrorText = (props) => {
if (_.isEmpty(props.children)) {
return null;
}

return (
<Text style={[styles.formError, styles.mt1]}>{props.children}</Text>
);
};

InlineErrorText.propTypes = propTypes;
InlineErrorText.defaultProps = defaultProps;
InlineErrorText.displayName = 'InlineErrorText';
export default InlineErrorText;
2 changes: 2 additions & 0 deletions src/components/StatePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ const StatePicker = props => (
onChange={props.onChange}
value={props.value}
label={props.translate('common.state')}
hasError={props.hasError}
errorText={props.errorText}
/>
);

Expand Down
2 changes: 2 additions & 0 deletions src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,8 @@ export default {
dob: 'Please enter a valid date of birth',
age: 'Requestors must be over 18 years old',
ssnLast4: 'Please enter valid last 4 digits of SSN',
firstName: 'Please enter valid first name',
lastName: 'Please enter valid last name',
noDefaultDepositAccountOrDebitCardAvailable: 'Please add a default deposit bank account or debit card',
existingOwners: {
alreadyInUse: 'This bank account is already in use by ',
Expand Down
Loading

0 comments on commit 57eed8b

Please sign in to comment.