App: Refactor Form: Support `errors` and `errorFields` returned from the backend, and add possibility to hide the submit the button
Problem
We recently started returning an errors object in onyxData from the API that contain the latest error related to a form submission. The current Form component does not take into account the new key and no server error message gets displayed when we submit incorrect data.
There are also some cases where we need to hide the submit button but this is currently not possible as it is abstracted away from outside components. Example: when adding a bank account from Plaid, we need to hide the button while Plaid is loading and when the user hasn’t selected a Plaid account yet from the picker.
Since there are a few ongoing PRs that depend on the Form component, I figured it might be a good idea to create a separate PR for this and have the PRs merge with main.
Solution
- Add a
getErrorMessagefunction toFormthat extracts the latest error message from theerrorsobject. For API calls that still return anerrorkey, this should still work as the function below checks for both keys.
getErrorMessage() {
const latestErrorMessage = ErrorUtils.getLatestErrorMessage(this.props.formState);
return this.props.formState.error || (typeof latestErrorMessage === 'string' ? latestErrorMessage : '');
}
-
Rename
setErrorMessageinactions/FormActions.jstosetErrors. -
Add an optional prop
isSubmitButtonVisibletoFormthat controls the visibility of the submit button. This prop should have a default value oftrueto not break any existing components that useForm. -
Handle field-specific errors coming from the backend and display them on the inputs that need attention.
-
If there are any
errorFields, display the errors on the input and display thefixTheErrors.
childrenWrapperWithProps(children) {
return React.Children.map(children, (child) => {
...
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,
defaultValue,
errorText: this.state.errors[inputID] || fieldErrorMessage,
...
}
}
}
/** Controls the submit button's visibility */
isSubmitButtonVisible: PropTypes.bool,
...
const defaultProps = {
isSubmitButtonVisible: true,
...
};
...
{this.props.isSubmitButtonVisible && (
<FormAlertWithSubmitButton
buttonText={this.props.submitButtonText}
isAlertVisible={_.size(this.state.errors) > 0 || Boolean(this.getErrorMessage())}
isLoading={this.props.formState.isLoading}
message={_.isEmpty(this.props.formState.errorFields) ? this.getErrorMessage() : null}
onSubmit={this.submit}
onFixTheErrorsLinkPressed={() => {
this.inputRefs[_.first(_.keys(this.state.errors))].focus();
}}
containerStyles={[styles.mh0, styles.mt5]}
/>
)}
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Comments: 20 (15 by maintainers)
Totally agree with doing this. A great example of us running into this is here.
IIRC this is where we discussed it, not sure if there were other discussions around it though.
That may be the case now, but our OfflineWithFeedback component does allow multiple errors to be passed and displayed here. So we should have the same behavior for Forms.
👍 IIRC
errorFieldswas explicitly added for Form errors, so we should support that in Form.@youssef-lr I’m assuming you are also actively working on this - if so, we can add the
InternallabelShould we be showing all possible errors messages from the errors object?
👍
This is probably good for the Form case. I think sometimes an alternative can be to figure out how to pass an element you would otherwise toggle with a boolean prop in as a child or prop and then have the parent control it’s visibility by either passing in the button or not. That is sometimes more flexible than a boolean prop - but probably not be worth worrying about in this case (and would be a lot more changes).