fix(netlify-cms-core): fix fields metadata for objects and lists (#2011)

This commit is contained in:
Bartholomew 2019-02-08 20:55:03 +01:00 committed by Shawn Erquhart
parent ff73175244
commit 2d1d1c13df
6 changed files with 48 additions and 30 deletions

View File

@ -137,13 +137,13 @@
const RelationKitchenSinkPostPreview = createClass({ const RelationKitchenSinkPostPreview = createClass({
render: function() { render: function() {
// When a post is selected from the relation field, all of it's data // When a post is selected from the relation field, all of it's data
// will be available in the metadata nested under the collection name, // will be available in the field's metadata nested under the collection
// and then further nested under the value specified in `valueField`. // name, and then further nested under the value specified in `valueField`.
// In this case, the post would be nested under "posts" and then under // In this case, the post would be nested under "posts" and then under
// the title of the selected post, since our `valueField` in the config // the title of the selected post, since our `valueField` in the config
// is "title". // is "title".
const postValue = this.props.entry.getIn(['data', 'post']); const { value, fieldsMetaData } = this.props;
const post = this.props.fieldsMetaData.getIn(['posts', postValue]); const post = fieldsMetaData && fieldsMetaData.getIn(['posts', value]);
const style = { border: '2px solid #ccc', borderRadius: '8px', padding: '20px' }; const style = { border: '2px solid #ccc', borderRadius: '8px', padding: '20px' };
return post ? h('div', { style: style }, return post ? h('div', { style: style },
h('h2', {}, 'Related Post'), h('h2', {}, 'Related Post'),

View File

@ -195,7 +195,7 @@ export default class Widget extends Component {
*/ */
onChangeObject = (fieldName, newValue, newMetadata) => { onChangeObject = (fieldName, newValue, newMetadata) => {
const newObjectValue = this.getObjectValue().set(fieldName, newValue); const newObjectValue = this.getObjectValue().set(fieldName, newValue);
return this.props.onChange(newObjectValue, newMetadata); return this.props.onChange(newObjectValue, { [this.props.field.get('name')]: newMetadata });
}; };
render() { render() {

View File

@ -22,8 +22,8 @@ const PreviewPaneFrame = styled(Frame)`
`; `;
export default class PreviewPane extends React.Component { export default class PreviewPane extends React.Component {
getWidget = (field, value, props, idx = null) => { getWidget = (field, value, metadata, props, idx = null) => {
const { fieldsMetaData, getAsset, entry } = props; const { getAsset, entry } = props;
const widget = resolveWidget(field.get('widget')); const widget = resolveWidget(field.get('widget'));
const key = idx ? field.get('name') + '_' + idx : field.get('name'); const key = idx ? field.get('name') + '_' + idx : field.get('name');
@ -37,9 +37,8 @@ export default class PreviewPane extends React.Component {
field={field} field={field}
getAsset={getAsset} getAsset={getAsset}
value={value && Map.isMap(value) ? value.get(field.get('name')) : value} value={value && Map.isMap(value) ? value.get(field.get('name')) : value}
metadata={fieldsMetaData && fieldsMetaData.get(field.get('name'))}
entry={entry} entry={entry}
fieldsMetaData={fieldsMetaData} fieldsMetaData={metadata}
/> />
); );
}; };
@ -63,20 +62,26 @@ export default class PreviewPane extends React.Component {
* object and list type fields. Used internally to retrieve widgets, and also * object and list type fields. Used internally to retrieve widgets, and also
* exposed for use in custom preview templates. * exposed for use in custom preview templates.
*/ */
widgetFor = (name, fields = this.props.fields, values = this.props.entry.get('data')) => { widgetFor = (
name,
fields = this.props.fields,
values = this.props.entry.get('data'),
fieldsMetaData = this.props.fieldsMetaData,
) => {
// We retrieve the field by name so that this function can also be used in // We retrieve the field by name so that this function can also be used in
// custom preview templates, where the field object can't be passed in. // custom preview templates, where the field object can't be passed in.
let field = fields && fields.find(f => f.get('name') === name); let field = fields && fields.find(f => f.get('name') === name);
let value = values && values.get(field.get('name')); let value = values && values.get(field.get('name'));
let nestedFields = field.get('fields'); let nestedFields = field.get('fields');
let singleField = field.get('field'); let singleField = field.get('field');
let metadata = fieldsMetaData && fieldsMetaData.get(field.get('name'), Map());
if (nestedFields) { if (nestedFields) {
field = field.set('fields', this.getNestedWidgets(nestedFields, value)); field = field.set('fields', this.getNestedWidgets(nestedFields, value, metadata));
} }
if (singleField) { if (singleField) {
field = field.set('field', this.getSingleNested(singleField, value)); field = field.set('field', this.getSingleNested(singleField, value, metadata));
} }
const labelledWidgets = ['string', 'text', 'number']; const labelledWidgets = ['string', 'text', 'number'];
@ -94,33 +99,35 @@ export default class PreviewPane extends React.Component {
); );
} }
return value ? this.getWidget(field, value, this.props) : null; return value ? this.getWidget(field, value, metadata, this.props) : null;
}; };
/** /**
* Retrieves widgets for nested fields (children of object/list fields) * Retrieves widgets for nested fields (children of object/list fields)
*/ */
getNestedWidgets = (fields, values) => { getNestedWidgets = (fields, values, fieldsMetaData) => {
// Fields nested within a list field will be paired with a List of value Maps. // Fields nested within a list field will be paired with a List of value Maps.
if (List.isList(values)) { if (List.isList(values)) {
return values.map(value => this.widgetsForNestedFields(fields, value)); return values.map(value => this.widgetsForNestedFields(fields, value, fieldsMetaData));
} }
// Fields nested within an object field will be paired with a single Map of values. // Fields nested within an object field will be paired with a single Map of values.
return this.widgetsForNestedFields(fields, values); return this.widgetsForNestedFields(fields, values, fieldsMetaData);
}; };
getSingleNested = (field, values) => { getSingleNested = (field, values, fieldsMetaData) => {
if (List.isList(values)) { if (List.isList(values)) {
return values.map((value, idx) => this.getWidget(field, value, this.props, idx)); return values.map((value, idx) =>
this.getWidget(field, value, fieldsMetaData.get(field.get('name')), this.props, idx),
);
} }
return this.getWidget(field, values, this.props); return this.getWidget(field, values, fieldsMetaData.get(field.get('name')), this.props);
}; };
/** /**
* Use widgetFor as a mapping function for recursive widget retrieval * Use widgetFor as a mapping function for recursive widget retrieval
*/ */
widgetsForNestedFields = (fields, values) => { widgetsForNestedFields = (fields, values, fieldsMetaData) => {
return fields.map(field => this.widgetFor(field.get('name'), fields, values)); return fields.map(field => this.widgetFor(field.get('name'), fields, values, fieldsMetaData));
}; };
/** /**
@ -130,10 +137,11 @@ export default class PreviewPane extends React.Component {
* TODO: see if widgetFor can now provide this functionality for preview templates * TODO: see if widgetFor can now provide this functionality for preview templates
*/ */
widgetsFor = name => { widgetsFor = name => {
const { fields, entry } = this.props; const { fields, entry, fieldsMetaData } = this.props;
const field = fields.find(f => f.get('name') === name); const field = fields.find(f => f.get('name') === name);
const nestedFields = field && field.get('fields'); const nestedFields = field && field.get('fields');
const value = entry.getIn(['data', field.get('name')]); const value = entry.getIn(['data', field.get('name')]);
const metadata = fieldsMetaData.get(field.get('name'), Map());
if (List.isList(value)) { if (List.isList(value)) {
return value.map(val => { return value.map(val => {
@ -142,7 +150,7 @@ export default class PreviewPane extends React.Component {
Map( Map(
nestedFields.map((f, i) => [ nestedFields.map((f, i) => [
f.get('name'), f.get('name'),
<div key={i}>{this.getWidget(f, val, this.props)}</div>, <div key={i}>{this.getWidget(f, val, metadata.get(f.get('name')), this.props)}</div>,
]), ]),
); );
return Map({ data: val, widgets }); return Map({ data: val, widgets });
@ -153,7 +161,12 @@ export default class PreviewPane extends React.Component {
data: value, data: value,
widgets: widgets:
nestedFields && nestedFields &&
Map(nestedFields.map(f => [f.get('name'), this.getWidget(f, value, this.props)])), Map(
nestedFields.map(f => [
f.get('name'),
this.getWidget(f, value, metadata.get(f.get('name')), this.props),
]),
),
}); });
}; };

View File

@ -201,10 +201,7 @@ export default class ListControl extends React.Component {
? this.getObjectValue(index).set(fieldName, newValue) ? this.getObjectValue(index).set(fieldName, newValue)
: newValue; : newValue;
const parsedMetadata = { const parsedMetadata = {
[collectionName]: Object.assign( [collectionName]: Object.assign(metadata ? metadata.toJS() : {}, newMetadata || {}),
metadata ? metadata.toJS() : {},
newMetadata ? newMetadata[collectionName] : {},
),
}; };
onChange(value.set(index, newObjectValue), parsedMetadata); onChange(value.set(index, newObjectValue), parsedMetadata);
}; };
@ -283,6 +280,7 @@ export default class ListControl extends React.Component {
classNameWrapper, classNameWrapper,
editorControl, editorControl,
onValidateObject, onValidateObject,
metadata,
clearFieldErrors, clearFieldErrors,
fieldsErrors, fieldsErrors,
controlRef, controlRef,
@ -320,6 +318,7 @@ export default class ListControl extends React.Component {
onChangeObject={this.handleChangeFor(index)} onChangeObject={this.handleChangeFor(index)}
editorControl={editorControl} editorControl={editorControl}
resolveWidget={resolveWidget} resolveWidget={resolveWidget}
metadata={metadata}
forList forList
onValidateObject={onValidateObject} onValidateObject={onValidateObject}
clearFieldErrors={clearFieldErrors} clearFieldErrors={clearFieldErrors}

View File

@ -69,6 +69,7 @@ export default class ObjectControl extends Component {
onChangeObject, onChangeObject,
onValidateObject, onValidateObject,
clearFieldErrors, clearFieldErrors,
metadata,
fieldsErrors, fieldsErrors,
editorControl: EditorControl, editorControl: EditorControl,
controlRef, controlRef,
@ -87,6 +88,7 @@ export default class ObjectControl extends Component {
value={fieldValue} value={fieldValue}
onChange={onChangeObject} onChange={onChangeObject}
clearFieldErrors={clearFieldErrors} clearFieldErrors={clearFieldErrors}
fieldsMetaData={metadata}
fieldsErrors={fieldsErrors} fieldsErrors={fieldsErrors}
onValidate={onValidateObject} onValidate={onValidateObject}
processControlRef={controlRef && controlRef.bind(this)} processControlRef={controlRef && controlRef.bind(this)}

View File

@ -95,7 +95,9 @@ export default class RelationControl extends React.Component {
if (suggestion && suggestion.length === 1) { if (suggestion && suggestion.length === 1) {
const val = this.getSuggestionValue(suggestion[0]); const val = this.getSuggestionValue(suggestion[0]);
this.props.onChange(val, { this.props.onChange(val, {
[this.props.field.get('collection')]: { [val]: suggestion[0].data }, [this.props.field.get('name')]: {
[this.props.field.get('collection')]: { [val]: suggestion[0].data },
},
}); });
} }
} }
@ -108,7 +110,9 @@ export default class RelationControl extends React.Component {
onSuggestionSelected = (event, { suggestion }) => { onSuggestionSelected = (event, { suggestion }) => {
const value = this.getSuggestionValue(suggestion); const value = this.getSuggestionValue(suggestion);
this.props.onChange(value, { this.props.onChange(value, {
[this.props.field.get('collection')]: { [value]: suggestion.data }, [this.props.field.get('name')]: {
[this.props.field.get('collection')]: { [value]: suggestion.data },
},
}); });
}; };