-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Support displaying backend errors in Form #11465
Changes from all commits
6ca0473
6f147a1
69eca30
b64bd64
4cf6f29
7142d73
a3ad865
f495ce9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 */ | ||
|
@@ -90,6 +93,17 @@ class Form extends React.Component { | |
return this.props.formState.error || (typeof latestErrorMessage === 'string' ? latestErrorMessage : ''); | ||
} | ||
|
||
getFirstErroredInput() { | ||
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)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
submit() { | ||
// Return early if the form is already submitting to avoid duplicate submission | ||
if (this.props.formState.isLoading) { | ||
|
@@ -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)) { | ||
|
@@ -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); | ||
|
@@ -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} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
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]} | ||
|
There was a problem hiding this comment.
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