fix: Error UI improvements for nested lists/objects (#3726)
This commit is contained in:
@ -110,6 +110,7 @@ export default class ListControl extends React.Component {
|
||||
|
||||
static defaultProps = {
|
||||
value: List(),
|
||||
parentIds: [],
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
@ -410,6 +411,15 @@ export default class ListControl extends React.Component {
|
||||
this.validations = this.validations.filter(item => updatedKeys.includes(item.key));
|
||||
};
|
||||
|
||||
hasError = index => {
|
||||
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.state.keys[index])),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
renderItem = (item, index) => {
|
||||
const {
|
||||
@ -421,12 +431,15 @@ export default class ListControl extends React.Component {
|
||||
fieldsErrors,
|
||||
controlRef,
|
||||
resolveWidget,
|
||||
parentIds,
|
||||
forID,
|
||||
} = this.props;
|
||||
|
||||
const { itemsCollapsed, keys } = this.state;
|
||||
const collapsed = itemsCollapsed[index];
|
||||
const key = keys[index];
|
||||
let field = this.props.field;
|
||||
const hasError = this.hasError(index);
|
||||
|
||||
if (this.getValueType() === valueTypes.MIXED) {
|
||||
field = getTypedFieldForValue(field, item);
|
||||
@ -448,7 +461,9 @@ export default class ListControl extends React.Component {
|
||||
dragHandleHOC={SortableHandle}
|
||||
data-testid={`styled-list-item-top-bar-${key}`}
|
||||
/>
|
||||
<NestedObjectLabel collapsed={collapsed}>{this.objectLabel(item)}</NestedObjectLabel>
|
||||
<NestedObjectLabel collapsed={collapsed} error={hasError}>
|
||||
{this.objectLabel(item)}
|
||||
</NestedObjectLabel>
|
||||
<ClassNames>
|
||||
{({ css, cx }) => (
|
||||
<ObjectControl
|
||||
@ -472,6 +487,8 @@ export default class ListControl extends React.Component {
|
||||
validationKey={key}
|
||||
collapsed={collapsed}
|
||||
data-testid={`object-control-${key}`}
|
||||
hasError={hasError}
|
||||
parentIds={[...parentIds, forID, key]}
|
||||
/>
|
||||
)}
|
||||
</ClassNames>
|
||||
|
@ -52,6 +52,7 @@ describe('ListControl', () => {
|
||||
entry: fromJS({
|
||||
path: 'posts/index.md',
|
||||
}),
|
||||
forID: 'forID',
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -148,6 +148,7 @@ exports[`ListControl should add to list when add button is clicked 1`] = `
|
||||
|
||||
<div
|
||||
class="classNameWrapper emotion-18"
|
||||
id="forID"
|
||||
>
|
||||
<div
|
||||
class="emotion-12 emotion-13"
|
||||
@ -218,6 +219,7 @@ exports[`ListControl should add to list when add button is clicked 1`] = `
|
||||
field="Map { \\"name\\": \\"list\\", \\"label\\": \\"List\\", \\"fields\\": List [ Map { \\"label\\": \\"String\\", \\"name\\": \\"string\\", \\"widget\\": \\"string\\" } ] }"
|
||||
fieldserrors="Map {}"
|
||||
forlist="true"
|
||||
parentids="forID,0"
|
||||
validationkey="0"
|
||||
value="Map {}"
|
||||
/>
|
||||
@ -375,6 +377,7 @@ exports[`ListControl should remove from list when remove button is clicked 1`] =
|
||||
|
||||
<div
|
||||
class="classNameWrapper emotion-22"
|
||||
id="forID"
|
||||
>
|
||||
<div
|
||||
class="emotion-12 emotion-13"
|
||||
@ -445,6 +448,7 @@ exports[`ListControl should remove from list when remove button is clicked 1`] =
|
||||
field="Map { \\"name\\": \\"list\\", \\"label\\": \\"List\\", \\"collapsed\\": false, \\"minimize_collapsed\\": true, \\"fields\\": List [ Map { \\"label\\": \\"String\\", \\"name\\": \\"string\\", \\"widget\\": \\"string\\" } ] }"
|
||||
fieldserrors="Map {}"
|
||||
forlist="true"
|
||||
parentids="forID,0"
|
||||
validationkey="0"
|
||||
value="Map { \\"string\\": \\"item 1\\" }"
|
||||
/>
|
||||
@ -473,6 +477,7 @@ exports[`ListControl should remove from list when remove button is clicked 1`] =
|
||||
field="Map { \\"name\\": \\"list\\", \\"label\\": \\"List\\", \\"collapsed\\": false, \\"minimize_collapsed\\": true, \\"fields\\": List [ Map { \\"label\\": \\"String\\", \\"name\\": \\"string\\", \\"widget\\": \\"string\\" } ] }"
|
||||
fieldserrors="Map {}"
|
||||
forlist="true"
|
||||
parentids="forID,1"
|
||||
validationkey="1"
|
||||
value="Map { \\"string\\": \\"item 2\\" }"
|
||||
/>
|
||||
@ -631,6 +636,7 @@ exports[`ListControl should remove from list when remove button is clicked 2`] =
|
||||
|
||||
<div
|
||||
class="classNameWrapper emotion-18"
|
||||
id="forID"
|
||||
>
|
||||
<div
|
||||
class="emotion-12 emotion-13"
|
||||
@ -701,6 +707,7 @@ exports[`ListControl should remove from list when remove button is clicked 2`] =
|
||||
field="Map { \\"name\\": \\"list\\", \\"label\\": \\"List\\", \\"collapsed\\": false, \\"minimize_collapsed\\": true, \\"fields\\": List [ Map { \\"label\\": \\"String\\", \\"name\\": \\"string\\", \\"widget\\": \\"string\\" } ] }"
|
||||
fieldserrors="Map {}"
|
||||
forlist="true"
|
||||
parentids="forID,1"
|
||||
validationkey="1"
|
||||
value="Map { \\"string\\": \\"item 2\\" }"
|
||||
/>
|
||||
@ -714,6 +721,7 @@ exports[`ListControl should render empty list 1`] = `
|
||||
<DocumentFragment>
|
||||
<input
|
||||
class="classNameWrapper"
|
||||
id="forID"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
@ -868,6 +876,7 @@ exports[`ListControl should render list with fields with collapse = "false" and
|
||||
|
||||
<div
|
||||
class="classNameWrapper emotion-22"
|
||||
id="forID"
|
||||
>
|
||||
<div
|
||||
class="emotion-12 emotion-13"
|
||||
@ -938,6 +947,7 @@ exports[`ListControl should render list with fields with collapse = "false" and
|
||||
field="Map { \\"name\\": \\"list\\", \\"label\\": \\"List\\", \\"collapsed\\": false, \\"fields\\": List [ Map { \\"label\\": \\"String\\", \\"name\\": \\"string\\", \\"widget\\": \\"string\\" } ] }"
|
||||
fieldserrors="Map {}"
|
||||
forlist="true"
|
||||
parentids="forID,0"
|
||||
validationkey="0"
|
||||
value="Map { \\"string\\": \\"item 1\\" }"
|
||||
/>
|
||||
@ -966,6 +976,7 @@ exports[`ListControl should render list with fields with collapse = "false" and
|
||||
field="Map { \\"name\\": \\"list\\", \\"label\\": \\"List\\", \\"collapsed\\": false, \\"fields\\": List [ Map { \\"label\\": \\"String\\", \\"name\\": \\"string\\", \\"widget\\": \\"string\\" } ] }"
|
||||
fieldserrors="Map {}"
|
||||
forlist="true"
|
||||
parentids="forID,1"
|
||||
validationkey="1"
|
||||
value="Map { \\"string\\": \\"item 2\\" }"
|
||||
/>
|
||||
@ -1123,6 +1134,7 @@ exports[`ListControl should render list with fields with collapse = "false" and
|
||||
|
||||
<div
|
||||
class="classNameWrapper emotion-22"
|
||||
id="forID"
|
||||
>
|
||||
<div
|
||||
class="emotion-12 emotion-13"
|
||||
@ -1193,6 +1205,7 @@ exports[`ListControl should render list with fields with collapse = "false" and
|
||||
field="Map { \\"name\\": \\"list\\", \\"label\\": \\"List\\", \\"collapsed\\": false, \\"minimize_collapsed\\": true, \\"fields\\": List [ Map { \\"label\\": \\"String\\", \\"name\\": \\"string\\", \\"widget\\": \\"string\\" } ] }"
|
||||
fieldserrors="Map {}"
|
||||
forlist="true"
|
||||
parentids="forID,0"
|
||||
validationkey="0"
|
||||
value="Map { \\"string\\": \\"item 1\\" }"
|
||||
/>
|
||||
@ -1221,6 +1234,7 @@ exports[`ListControl should render list with fields with collapse = "false" and
|
||||
field="Map { \\"name\\": \\"list\\", \\"label\\": \\"List\\", \\"collapsed\\": false, \\"minimize_collapsed\\": true, \\"fields\\": List [ Map { \\"label\\": \\"String\\", \\"name\\": \\"string\\", \\"widget\\": \\"string\\" } ] }"
|
||||
fieldserrors="Map {}"
|
||||
forlist="true"
|
||||
parentids="forID,1"
|
||||
validationkey="1"
|
||||
value="Map { \\"string\\": \\"item 2\\" }"
|
||||
/>
|
||||
@ -1379,6 +1393,7 @@ exports[`ListControl should render list with fields with default collapse ("true
|
||||
|
||||
<div
|
||||
class="classNameWrapper emotion-22"
|
||||
id="forID"
|
||||
>
|
||||
<div
|
||||
class="emotion-12 emotion-13"
|
||||
@ -1449,6 +1464,7 @@ exports[`ListControl should render list with fields with default collapse ("true
|
||||
field="Map { \\"name\\": \\"list\\", \\"label\\": \\"List\\", \\"fields\\": List [ Map { \\"label\\": \\"String\\", \\"name\\": \\"string\\", \\"widget\\": \\"string\\" } ] }"
|
||||
fieldserrors="Map {}"
|
||||
forlist="true"
|
||||
parentids="forID,0"
|
||||
validationkey="0"
|
||||
value="Map { \\"string\\": \\"item 1\\" }"
|
||||
/>
|
||||
@ -1477,6 +1493,7 @@ exports[`ListControl should render list with fields with default collapse ("true
|
||||
field="Map { \\"name\\": \\"list\\", \\"label\\": \\"List\\", \\"fields\\": List [ Map { \\"label\\": \\"String\\", \\"name\\": \\"string\\", \\"widget\\": \\"string\\" } ] }"
|
||||
fieldserrors="Map {}"
|
||||
forlist="true"
|
||||
parentids="forID,1"
|
||||
validationkey="1"
|
||||
value="Map { \\"string\\": \\"item 2\\" }"
|
||||
/>
|
||||
@ -1617,6 +1634,7 @@ exports[`ListControl should render list with fields with default collapse ("true
|
||||
|
||||
<div
|
||||
class="classNameWrapper emotion-14"
|
||||
id="forID"
|
||||
>
|
||||
<div
|
||||
class="emotion-12 emotion-13"
|
||||
@ -1815,6 +1833,7 @@ exports[`ListControl should render list with nested object 1`] = `
|
||||
|
||||
<div
|
||||
class="classNameWrapper emotion-22"
|
||||
id="forID"
|
||||
>
|
||||
<div
|
||||
class="emotion-12 emotion-13"
|
||||
@ -1885,6 +1904,7 @@ exports[`ListControl should render list with nested object 1`] = `
|
||||
field="Map { \\"name\\": \\"list\\", \\"label\\": \\"List\\", \\"field\\": Map { \\"name\\": \\"object\\", \\"widget\\": \\"object\\", \\"label\\": \\"Object\\", \\"fields\\": List [ Map { \\"name\\": \\"title\\", \\"widget\\": \\"string\\", \\"label\\": \\"Title\\" } ] } }"
|
||||
fieldserrors="Map {}"
|
||||
forlist="true"
|
||||
parentids="forID,0"
|
||||
validationkey="0"
|
||||
value="Map { \\"object\\": Map { \\"title\\": \\"item 1\\" } }"
|
||||
/>
|
||||
@ -1913,6 +1933,7 @@ exports[`ListControl should render list with nested object 1`] = `
|
||||
field="Map { \\"name\\": \\"list\\", \\"label\\": \\"List\\", \\"field\\": Map { \\"name\\": \\"object\\", \\"widget\\": \\"object\\", \\"label\\": \\"Object\\", \\"fields\\": List [ Map { \\"name\\": \\"title\\", \\"widget\\": \\"string\\", \\"label\\": \\"Title\\" } ] } }"
|
||||
fieldserrors="Map {}"
|
||||
forlist="true"
|
||||
parentids="forID,1"
|
||||
validationkey="1"
|
||||
value="Map { \\"object\\": Map { \\"title\\": \\"item 2\\" } }"
|
||||
/>
|
||||
@ -2070,6 +2091,7 @@ exports[`ListControl should render list with nested object with collapse = false
|
||||
|
||||
<div
|
||||
class="classNameWrapper emotion-22"
|
||||
id="forID"
|
||||
>
|
||||
<div
|
||||
class="emotion-12 emotion-13"
|
||||
@ -2140,6 +2162,7 @@ exports[`ListControl should render list with nested object with collapse = false
|
||||
field="Map { \\"name\\": \\"list\\", \\"label\\": \\"List\\", \\"collapsed\\": false, \\"field\\": Map { \\"name\\": \\"object\\", \\"widget\\": \\"object\\", \\"label\\": \\"Object\\", \\"fields\\": List [ Map { \\"name\\": \\"title\\", \\"widget\\": \\"string\\", \\"label\\": \\"Title\\" } ] } }"
|
||||
fieldserrors="Map {}"
|
||||
forlist="true"
|
||||
parentids="forID,0"
|
||||
validationkey="0"
|
||||
value="Map { \\"object\\": Map { \\"title\\": \\"item 1\\" } }"
|
||||
/>
|
||||
@ -2168,6 +2191,7 @@ exports[`ListControl should render list with nested object with collapse = false
|
||||
field="Map { \\"name\\": \\"list\\", \\"label\\": \\"List\\", \\"collapsed\\": false, \\"field\\": Map { \\"name\\": \\"object\\", \\"widget\\": \\"object\\", \\"label\\": \\"Object\\", \\"fields\\": List [ Map { \\"name\\": \\"title\\", \\"widget\\": \\"string\\", \\"label\\": \\"Title\\" } ] } }"
|
||||
fieldserrors="Map {}"
|
||||
forlist="true"
|
||||
parentids="forID,1"
|
||||
validationkey="1"
|
||||
value="Map { \\"object\\": Map { \\"title\\": \\"item 2\\" } }"
|
||||
/>
|
||||
@ -2181,6 +2205,7 @@ exports[`ListControl should render list with string array 1`] = `
|
||||
<DocumentFragment>
|
||||
<input
|
||||
class="classNameWrapper"
|
||||
id="forID"
|
||||
type="text"
|
||||
value="item 1, item 2"
|
||||
/>
|
||||
|
Reference in New Issue
Block a user