Skip to content

Commit

Permalink
[form-builder] Show warning on non-object array values (#1173)
Browse files Browse the repository at this point in the history
* [validation] Fix crash on non-object array values

* [form-builder] Show warning on non-object array values
  • Loading branch information
rexxars authored and bjoerge committed Aug 13, 2019
1 parent 5222546 commit bac21f6
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 2 deletions.
38 changes: 38 additions & 0 deletions packages/@sanity/form-builder/src/inputs/ArrayInput/ArrayInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import React from 'react'
import ArrayFunctions from 'part:@sanity/form-builder/input/array/functions'
import {map} from 'rxjs/operators'
import {isPlainObject} from 'lodash'
import type {Uploader} from '../../sanity/uploads/typedefs'
import type {Marker, Type} from '../../typedefs'
import type {Path} from '../../typedefs/path'
Expand Down Expand Up @@ -261,6 +262,15 @@ export default class ArrayInput extends React.Component<Props, State> {
onChange(PatchEvent.from(...patches))
}

handleRemoveNonObjectValues = () => {
const {onChange, value} = this.props
const nonObjects = value
.reduce((acc, val, i) => (isPlainObject(val) ? acc : acc.concat(i)), [])
.reverse()
const patches = nonObjects.map(index => unset([index]))
onChange(PatchEvent.from(...patches))
}

handleUpload = ({file, type, uploader}) => {
const {onChange} = this.props
const item = createProtoValue(type)
Expand All @@ -280,6 +290,34 @@ export default class ArrayInput extends React.Component<Props, State> {

render() {
const {type, level, markers, readOnly, onChange, value} = this.props
const hasNonObjectValues = (value || []).some(item => !isPlainObject(item))
if (hasNonObjectValues) {
return (
<Fieldset
legend={type.title}
description={type.description}
level={level}
tabIndex={0}
onFocus={this.handleFocus}
ref={this.setElement}
markers={markers}
>
<div className={styles.nonObjectsWarning}>
Some items in this list are not objects. We need to remove them before the list can be
edited.
<div className={styles.removeNonObjectsButtonWrapper}>
<Button onClick={this.handleRemoveNonObjectValues}>Remove non-object values</Button>
</div>
<Details title={<b>Why is this happening?</b>}>
This usually happens when items are created through an API client from outside the
Content Studio and sets invalid data, or a custom input component have inserted
incorrect values into the list.
</Details>
</div>
</Fieldset>
)
}

const hasMissingKeys = (value || []).some(item => !item._key)
if (hasMissingKeys) {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,12 @@
.fixMissingKeysButtonWrapper {
margin: 1em 0;
}

.nonObjectsWarning {
composes: warning;
padding: 1em;
}

.removeNonObjectsButtonWrapper {
composes: fixMissingKeysButtonWrapper;
}
15 changes: 13 additions & 2 deletions packages/@sanity/validation/src/validateDocument.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ function validateArray(items, type, path, options) {
}
// Validate items within array
const itemChecks = items.map((item, i) => {
const pathSegment = item._key ? {_key: item._key} : i
const pathSegment = item && item._key ? {_key: item._key} : i
const itemType = resolveTypeForArrayItem(item, type.of)
const itemPath = appendPath(path, [pathSegment])
return validateItem(item, itemType, itemPath, {
Expand All @@ -104,6 +104,17 @@ function validateArray(items, type, path, options) {
}

function validatePrimitive(item, type, path, options) {
if (!type) {
return [
{
type: 'validation',
level: 'error',
path,
item: new ValidationError('Unable to resolve type for item')
}
]
}

if (!type.validation) {
return []
}
Expand All @@ -118,7 +129,7 @@ function validatePrimitive(item, type, path, options) {
}

function resolveTypeForArrayItem(item, candidates) {
const primitive = !item._type && Type.string(item).toLowerCase()
const primitive = !item || (!item._type && Type.string(item).toLowerCase())
if (primitive) {
return candidates.find(candidate => candidate.jsonType === primitive)
}
Expand Down

0 comments on commit bac21f6

Please sign in to comment.