Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 29 additions & 4 deletions src/components/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ const propTypes = {

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

/** Field-specific server side errors keyed by microtime */
errorFields: PropTypes.objectOf(PropTypes.objectOf(PropTypes.string)),
}),

/** Contains draft values for each input in the form */
Expand Down Expand Up @@ -90,6 +93,17 @@ class Form extends React.Component {
return this.props.formState.error || (typeof latestErrorMessage === 'string' ? latestErrorMessage : '');
}

getFirstErroredInput() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Just come across this randomly but this is missing a JSDoc and should have an @returns

const hasStateErrors = !_.isEmpty(this.state.errors);
const hasErrorFields = !_.isEmpty(this.props.formState.errorFields);

if (!hasStateErrors && !hasErrorFields) {
return;
}

return _.first(_.keys(hasStateErrors ? this.state.erorrs : this.props.formState.errorFields));
Copy link
Contributor

Choose a reason for hiding this comment

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

this.state.erorrs is a typo ?

}

submit() {
// Return early if the form is already submitting to avoid duplicate submission
if (this.props.formState.isLoading) {
Expand All @@ -116,6 +130,7 @@ class Form extends React.Component {
*/
validate(values) {
FormActions.setErrors(this.props.formID, null);
FormActions.setErrorFields(this.props.formID, null);
const validationErrors = this.props.validate(values);

if (!_.isObject(validationErrors)) {
Expand Down Expand Up @@ -181,10 +196,19 @@ class Form extends React.Component {
this.state.inputValues[inputID] = child.props.value;
}

const errorFields = lodashGet(this.props.formState, 'errorFields', {});
const fieldErrorMessage = _.chain(errorFields[inputID])
.keys()
.sortBy()
.reverse()
.map(key => errorFields[inputID][key])
.first()
.value() || '';

return React.cloneElement(child, {
ref: node => this.inputRefs[inputID] = node,
value: this.state.inputValues[inputID],
errorText: this.state.errors[inputID] || '',
errorText: this.state.errors[inputID] || fieldErrorMessage,
onBlur: () => {
this.setTouchedInput(inputID);
this.validate(this.state.inputValues);
Expand Down Expand Up @@ -223,12 +247,13 @@ class Form extends React.Component {
{this.props.isSubmitButtonVisible && (
<FormAlertWithSubmitButton
buttonText={this.props.submitButtonText}
isAlertVisible={_.size(this.state.errors) > 0 || Boolean(this.getErrorMessage())}
isAlertVisible={_.size(this.state.errors) > 0 || Boolean(this.getErrorMessage()) || !_.isEmpty(this.props.formState.errorFields)}
isLoading={this.props.formState.isLoading}
message={this.getErrorMessage()}
message={_.isEmpty(this.props.formState.errorFields) ? this.getErrorMessage() : null}
Copy link
Contributor Author

@youssef-lr youssef-lr Sep 30, 2022

Choose a reason for hiding this comment

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

I figured I should add this check here to avoid displaying a duplicate message on both the error field above the submit button and below the errored input. Instead, we will display the fixTheErrors message and the field error message below the input.

onSubmit={this.submit}
onFixTheErrorsLinkPressed={() => {
const focusKey = _.find(_.keys(this.inputRefs), key => _.keys(this.state.errors).includes(key));
const errors = !_.isEmpty(this.state.errors) ? this.state.errors : this.props.formState.errorFields;
const focusKey = _.find(_.keys(this.inputRefs), key => _.keys(errors).includes(key));
this.inputRefs[focusKey].focus();
}}
containerStyles={[styles.mh0, styles.mt5]}
Expand Down
9 changes: 9 additions & 0 deletions src/libs/actions/FormActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ function setErrors(formID, errors) {
Onyx.merge(formID, {errors});
}

/**
* @param {String} formID
* @param {Object} errorFields
*/
function setErrorFields(formID, errorFields) {
Onyx.merge(formID, {errorFields});
}

/**
* @param {String} formID
* @param {Object} draftValues
Expand All @@ -27,5 +35,6 @@ function setDraftValues(formID, draftValues) {
export {
setIsLoading,
setErrors,
setErrorFields,
setDraftValues,
};