fix: Error UI improvements for nested lists/objects (#3726)
This commit is contained in:
@ -349,7 +349,7 @@ export function changeDraftField(
|
||||
|
||||
export function changeDraftFieldValidation(
|
||||
uniquefieldId: string,
|
||||
errors: { type: string; message: string }[],
|
||||
errors: { type: string; parentIds: string[]; message: string }[],
|
||||
) {
|
||||
return {
|
||||
type: DRAFT_VALIDATION_ERRORS,
|
||||
|
@ -115,6 +115,11 @@ class EditorControl extends React.Component {
|
||||
t: PropTypes.func.isRequired,
|
||||
isEditorComponent: PropTypes.bool,
|
||||
isNewEditorComponent: PropTypes.bool,
|
||||
parentIds: PropTypes.arrayOf(PropTypes.string),
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
parentIds: [],
|
||||
};
|
||||
|
||||
state = {
|
||||
@ -123,6 +128,17 @@ class EditorControl extends React.Component {
|
||||
|
||||
uniqueFieldId = uniqueId(`${this.props.field.get('name')}-field-`);
|
||||
|
||||
isAncestorOfFieldError = () => {
|
||||
const { fieldsErrors } = this.props;
|
||||
|
||||
if (fieldsErrors && fieldsErrors.size > 0) {
|
||||
return Object.values(fieldsErrors.toJS()).some(arr =>
|
||||
arr.some(err => err.parentIds && err.parentIds.includes(this.uniqueFieldId)),
|
||||
);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
value,
|
||||
@ -153,6 +169,7 @@ class EditorControl extends React.Component {
|
||||
isSelected,
|
||||
isEditorComponent,
|
||||
isNewEditorComponent,
|
||||
parentIds,
|
||||
t,
|
||||
} = this.props;
|
||||
|
||||
@ -164,6 +181,9 @@ class EditorControl extends React.Component {
|
||||
const onValidateObject = onValidate;
|
||||
const metadata = fieldsMetaData && fieldsMetaData.get(fieldName);
|
||||
const errors = fieldsErrors && fieldsErrors.get(this.uniqueFieldId);
|
||||
const childErrors = this.isAncestorOfFieldError();
|
||||
const hasErrors = !!errors || childErrors;
|
||||
|
||||
return (
|
||||
<ClassNames>
|
||||
{({ css, cx }) => (
|
||||
@ -184,7 +204,7 @@ class EditorControl extends React.Component {
|
||||
)}
|
||||
<FieldLabel
|
||||
isActive={isSelected || this.state.styleActive}
|
||||
hasErrors={!!errors}
|
||||
hasErrors={hasErrors}
|
||||
htmlFor={this.uniqueFieldId}
|
||||
>
|
||||
{`${field.get('label', field.get('name'))}${
|
||||
@ -204,7 +224,7 @@ class EditorControl extends React.Component {
|
||||
{
|
||||
[css`
|
||||
${styleStrings.widgetError};
|
||||
`]: !!errors,
|
||||
`]: hasErrors,
|
||||
},
|
||||
)}
|
||||
classNameWidget={css`
|
||||
@ -255,10 +275,11 @@ class EditorControl extends React.Component {
|
||||
onValidateObject={onValidateObject}
|
||||
isEditorComponent={isEditorComponent}
|
||||
isNewEditorComponent={isNewEditorComponent}
|
||||
parentIds={parentIds}
|
||||
t={t}
|
||||
/>
|
||||
{fieldHint && (
|
||||
<ControlHint active={isSelected || this.state.styleActive} error={!!errors}>
|
||||
<ControlHint active={isSelected || this.state.styleActive} error={hasErrors}>
|
||||
{fieldHint}
|
||||
</ControlHint>
|
||||
)}
|
||||
|
@ -118,11 +118,12 @@ export default class Widget extends Component {
|
||||
};
|
||||
|
||||
validatePresence = (field, value) => {
|
||||
const t = this.props.t;
|
||||
const { t, parentIds } = this.props;
|
||||
const isRequired = field.get('required', true);
|
||||
if (isRequired && isEmpty(value)) {
|
||||
const error = {
|
||||
type: ValidationErrorTypes.PRESENCE,
|
||||
parentIds,
|
||||
message: t('editor.editorControlPane.widget.required', {
|
||||
fieldLabel: field.get('label', field.get('name')),
|
||||
}),
|
||||
@ -134,7 +135,7 @@ export default class Widget extends Component {
|
||||
};
|
||||
|
||||
validatePattern = (field, value) => {
|
||||
const t = this.props.t;
|
||||
const { t, parentIds } = this.props;
|
||||
const pattern = field.get('pattern', false);
|
||||
|
||||
if (isEmpty(value)) {
|
||||
@ -144,6 +145,7 @@ export default class Widget extends Component {
|
||||
if (pattern && !RegExp(pattern.first()).test(value)) {
|
||||
const error = {
|
||||
type: ValidationErrorTypes.PATTERN,
|
||||
parentIds,
|
||||
message: t('editor.editorControlPane.widget.regexPattern', {
|
||||
fieldLabel: field.get('label', field.get('name')),
|
||||
pattern: pattern.last(),
|
||||
@ -157,7 +159,7 @@ export default class Widget extends Component {
|
||||
};
|
||||
|
||||
validateWrappedControl = field => {
|
||||
const t = this.props.t;
|
||||
const { t, parentIds } = this.props;
|
||||
if (typeof this.wrappedControlValid !== 'function') {
|
||||
throw new Error(oneLine`
|
||||
this.wrappedControlValid is not a function. Are you sure widget
|
||||
@ -188,6 +190,7 @@ export default class Widget extends Component {
|
||||
|
||||
const error = {
|
||||
type: ValidationErrorTypes.CUSTOM,
|
||||
parentIds,
|
||||
message: t('editor.editorControlPane.widget.processing', {
|
||||
fieldLabel: field.get('label', field.get('name')),
|
||||
}),
|
||||
@ -257,6 +260,7 @@ export default class Widget extends Component {
|
||||
controlRef,
|
||||
isEditorComponent,
|
||||
isNewEditorComponent,
|
||||
parentIds,
|
||||
t,
|
||||
} = this.props;
|
||||
return React.createElement(controlComponent, {
|
||||
@ -301,6 +305,7 @@ export default class Widget extends Component {
|
||||
isNewEditorComponent,
|
||||
fieldsErrors,
|
||||
controlRef,
|
||||
parentIds,
|
||||
t,
|
||||
});
|
||||
}
|
||||
|
Reference in New Issue
Block a user