Merge pull request #470 from netlify/relation-preview-improvements
Relation preview improvements
This commit is contained in:
commit
1d08f1a33b
@ -16,11 +16,16 @@ Although possible, it may be cumbersome or even impractical to add a React build
|
|||||||
Register a custom widget.
|
Register a custom widget.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
CMS.registerWidget(field, control, \[preview\])
|
CMS.registerWidget(name, control, \[preview\])
|
||||||
```
|
```
|
||||||
|
|
||||||
**Params:**
|
**Params:**
|
||||||
|
|
||||||
|
Param | Type | Description
|
||||||
|
--- | --- | ---
|
||||||
|
`name` | string | Widget name, allows this widget to be used via the field `widget` property in config
|
||||||
|
`control` | React.Component \| string | <ul><li>React component that renders the control, receives the following props: <ul><li>**value:** Current field value</li><li>**onChange**: Callback function to update the field value</li></ul></li><li>Name of a registered widget whose control should be used (includes built in widgets).</li></ul>
|
||||||
|
[`preview`] | React.Component, optional | Renders the widget preview, receives the following props: <ul><li>**value:** Current preview value</li><li>**field:** Immutable map of current field configuration</li><li>**metadata:** Immutable map of any available metadata for the current field</li><li>**getAsset:** Function for retrieving an asset url for image/file fields</li><li>**entry:** Immutable Map of all entry data</li><li>**fieldsMetaData:** Immutable map of metadata from all fields.</li></ul>
|
||||||
* **field:** The field type which this widget will be used for.
|
* **field:** The field type which this widget will be used for.
|
||||||
* **control:** A React component that renders the editing interface for this field. Two props will be passed:
|
* **control:** A React component that renders the editing interface for this field. Two props will be passed:
|
||||||
* **value:** The current value for this field.
|
* **value:** The current value for this field.
|
||||||
@ -43,7 +48,17 @@ var CategoriesControl = createClass({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
CMS.registerWidget('categories', CategoriesControl);
|
var CategoriesPreview = createClass({
|
||||||
|
render: function() {
|
||||||
|
return h('ul', {},
|
||||||
|
this.props.value.map(function(val, index) {
|
||||||
|
return h('li', {key: index}, val);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
CMS.registerWidget('categories', CategoriesControl, CategoriesPreview);
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -87,19 +87,7 @@ You point to where the files are stored, and specify the fields that define them
|
|||||||
|
|
||||||
### Widgets
|
### Widgets
|
||||||
|
|
||||||
Widgets define the data type and interface for entry fields. Netlify CMS comes with several built-in widgets, including:
|
Widgets define the data type and interface for entry fields. Netlify CMS comes with several built-in [widgets](/docs/widgets).
|
||||||
|
|
||||||
Widget | UI | Data Type
|
|
||||||
--- | --- | ---
|
|
||||||
`string` | text input | string
|
|
||||||
`text` | text area input | plain text, multiline input
|
|
||||||
`number` | text input with `+` and `-` buttons | number
|
|
||||||
`markdown` | rich text editor with raw option | markdown-formatted string
|
|
||||||
`datetime` | date picker widget | ISO date string
|
|
||||||
`image` | file picker widget with drag-and-drop | file path saved as string, image uploaded to media folder
|
|
||||||
`hidden` | No UI | Hidden element, typically only useful with a `default` attribute
|
|
||||||
|
|
||||||
We’re always adding new widgets, and you can also create your own.
|
|
||||||
|
|
||||||
## Customization
|
## Customization
|
||||||
|
|
||||||
|
@ -4,19 +4,60 @@
|
|||||||
|
|
||||||
Widgets define the data type and interface for entry fields. Netlify CMS comes with several built-in widgets, including:
|
Widgets define the data type and interface for entry fields. Netlify CMS comes with several built-in widgets, including:
|
||||||
|
|
||||||
| Name | UI | Data Type |
|
| Name | UI | Data Type |
|
||||||
| -------- | --------------------------------- | --------------------------------------------------|
|
| -------- | ---------------------------------- | ---------------------------------------------------|
|
||||||
| `string` | text input | string |
|
| `string` | text input | string |
|
||||||
| `boolean` | toggle switch | boolean |
|
| `boolean` | toggle switch | boolean |
|
||||||
| `text` | textarea input | string (multiline) |
|
| `text` | textarea input | string (multiline) |
|
||||||
| `number` | number input | number |
|
| `number` | number input | number |
|
||||||
| `markdown` | rich text editor | string (markdown) |
|
| `markdown` | rich text editor | string (markdown) |
|
||||||
| `datetime` | date picker | string (ISO date) |
|
| `datetime` | date picker | string (ISO date) |
|
||||||
| `select` | select input (dropdown) | string |
|
| `select` | select input (dropdown) | string |
|
||||||
| `image` | file picker w/ drag and drop | image file |
|
| `image` | file picker w/ drag and drop | image file |
|
||||||
| `file` | file picker w/ drag and drop | file |
|
| `file` | file picker w/ drag and drop | file |
|
||||||
| `hidden` | none | string |
|
| `hidden` | none | string |
|
||||||
| `object` | group of other widgets | Immutable Map containing field values |
|
| `object` | group of other widgets | Immutable Map containing field values |
|
||||||
| `list` | repeatable group of other widgets | Immutable List of objects containing field values |
|
| `list` | repeatable group of other widgets | Immutable List of objects containing field values |
|
||||||
|
| `relation` | text input w/ suggestions dropdown | value of `valueField` in related entry (see below) |
|
||||||
|
|
||||||
We’re always adding new widgets, and you can also [create your own](/docs/extending)!
|
We’re always adding new widgets, and you can also [create your own](/docs/extending)!
|
||||||
|
|
||||||
|
### Relation Widget
|
||||||
|
|
||||||
|
The relation widget allows you to reference an existing entry from within the entry you're editing. It provides a search input with a list of entries from the collection you're referencing, and the list automatically updates with matched entries based on what you've typed.
|
||||||
|
|
||||||
|
The following field configuration properties are specific to fields using the relation widget:
|
||||||
|
|
||||||
|
Property | Accepted Values | Description
|
||||||
|
--- | --- | ---
|
||||||
|
`collection` | string | name of the collection being referenced
|
||||||
|
`searchFields` | list | one or more names of fields in the referenced colleciton to search for the typed value
|
||||||
|
`valueField` | string | name a field from the referenced collection whose value will be stored for the relation
|
||||||
|
`name` | text input | string
|
||||||
|
|
||||||
|
Let's say we have a "posts" collection and an "authors" collection, and we want to select an author for each post - our config might look something like this:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
collections:
|
||||||
|
- name: authors
|
||||||
|
label: Authors
|
||||||
|
folder: "authors"
|
||||||
|
create: true
|
||||||
|
fields:
|
||||||
|
- {name: name, label: Name}
|
||||||
|
- {name: twitterHandle, label: "Twitter Handle"}
|
||||||
|
- {name: bio, label: Bio, widget: text}
|
||||||
|
- name: posts
|
||||||
|
label: Posts
|
||||||
|
folder: "posts"
|
||||||
|
create: true
|
||||||
|
fields:
|
||||||
|
- {name: title, label: Title}
|
||||||
|
- {name: body, label: Body, widget: markdown}
|
||||||
|
- name: author
|
||||||
|
label: Author
|
||||||
|
widget: relation
|
||||||
|
collection: authors
|
||||||
|
searchFields: [name, twitterHandle]
|
||||||
|
valueField: name
|
||||||
|
```
|
||||||
|
@ -62,6 +62,12 @@ collections: # A list of collections the CMS should be able to edit
|
|||||||
folder: "_sink"
|
folder: "_sink"
|
||||||
create: true
|
create: true
|
||||||
fields:
|
fields:
|
||||||
|
- label: "Related Post"
|
||||||
|
name: "post"
|
||||||
|
widget: "relationKitchenSinkPost"
|
||||||
|
collection: "posts"
|
||||||
|
searchFields: ["title", "body"]
|
||||||
|
valueField: "title"
|
||||||
- {label: "Title", name: "title", widget: "string"}
|
- {label: "Title", name: "title", widget: "string"}
|
||||||
- {label: "Boolean", name: "boolean", widget: "boolean", default: true}
|
- {label: "Boolean", name: "boolean", widget: "boolean", default: true}
|
||||||
- {label: "Text", name: "text", widget: "text"}
|
- {label: "Text", name: "text", widget: "text"}
|
||||||
@ -77,6 +83,12 @@ collections: # A list of collections the CMS should be able to edit
|
|||||||
name: "object"
|
name: "object"
|
||||||
widget: "object"
|
widget: "object"
|
||||||
fields:
|
fields:
|
||||||
|
- label: "Related Post"
|
||||||
|
name: "post"
|
||||||
|
widget: "relationKitchenSinkPost"
|
||||||
|
collection: "posts"
|
||||||
|
searchFields: ["title", "body"]
|
||||||
|
valueField: "title"
|
||||||
- {label: "String", name: "string", widget: "string"}
|
- {label: "String", name: "string", widget: "string"}
|
||||||
- {label: "Boolean", name: "boolean", widget: "boolean", default: false}
|
- {label: "Boolean", name: "boolean", widget: "boolean", default: false}
|
||||||
- {label: "Text", name: "text", widget: "text"}
|
- {label: "Text", name: "text", widget: "text"}
|
||||||
@ -119,6 +131,12 @@ collections: # A list of collections the CMS should be able to edit
|
|||||||
name: "list"
|
name: "list"
|
||||||
widget: "list"
|
widget: "list"
|
||||||
fields:
|
fields:
|
||||||
|
- label: "Related Post"
|
||||||
|
name: "post"
|
||||||
|
widget: "relationKitchenSinkPost"
|
||||||
|
collection: "posts"
|
||||||
|
searchFields: ["title", "body"]
|
||||||
|
valueField: "title"
|
||||||
- {label: "String", name: "string", widget: "string"}
|
- {label: "String", name: "string", widget: "string"}
|
||||||
- {label: "Boolean", name: "boolean", widget: "boolean"}
|
- {label: "Boolean", name: "boolean", widget: "boolean"}
|
||||||
- {label: "Text", name: "text", widget: "text"}
|
- {label: "Text", name: "text", widget: "text"}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -13,7 +13,7 @@ import styles from './PreviewPane.css';
|
|||||||
export default class PreviewPane extends React.Component {
|
export default class PreviewPane extends React.Component {
|
||||||
|
|
||||||
getWidget = (field, value, props) => {
|
getWidget = (field, value, props) => {
|
||||||
const { fieldsMetaData, getAsset } = props;
|
const { fieldsMetaData, getAsset, entry } = props;
|
||||||
const widget = resolveWidget(field.get('widget'));
|
const widget = resolveWidget(field.get('widget'));
|
||||||
|
|
||||||
return !widget.preview ? null : React.createElement(widget.preview, {
|
return !widget.preview ? null : React.createElement(widget.preview, {
|
||||||
@ -22,6 +22,8 @@ export default class PreviewPane extends React.Component {
|
|||||||
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')),
|
metadata: fieldsMetaData && fieldsMetaData.get(field.get('name')),
|
||||||
getAsset,
|
getAsset,
|
||||||
|
entry,
|
||||||
|
fieldsMetaData,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ class RelationControl extends Component {
|
|||||||
const collection = field.get('collection');
|
const collection = field.get('collection');
|
||||||
const searchFields = field.get('searchFields').toJS();
|
const searchFields = field.get('searchFields').toJS();
|
||||||
this.props.query(this.controlID, collection, searchFields, value);
|
this.props.query(this.controlID, collection, searchFields, value);
|
||||||
}, 100);
|
}, 500);
|
||||||
|
|
||||||
onSuggestionsClearRequested = () => {
|
onSuggestionsClearRequested = () => {
|
||||||
this.props.clearSearch();
|
this.props.clearSearch();
|
||||||
|
@ -22,7 +22,10 @@ export default {
|
|||||||
return _registry.previewStyles;
|
return _registry.previewStyles;
|
||||||
},
|
},
|
||||||
registerWidget(name, control, preview) {
|
registerWidget(name, control, preview) {
|
||||||
_registry.widgets[name] = { control, preview };
|
// A registered widget control can be reused by a new widget, allowing
|
||||||
|
// multiple copies with different previews.
|
||||||
|
const newControl = typeof control === 'string' ? _registry.widgets[control].control : control;
|
||||||
|
_registry.widgets[name] = { control: newControl, preview };
|
||||||
},
|
},
|
||||||
getWidget(name) {
|
getWidget(name) {
|
||||||
return _registry.widgets[name];
|
return _registry.widgets[name];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user