Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relation preview improvements #470

Merged
merged 7 commits into from
Jul 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions docs/extending.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,16 @@ Although possible, it may be cumbersome or even impractical to add a React build
Register a custom widget.

```js
CMS.registerWidget(field, control, \[preview\])
CMS.registerWidget(name, control, \[preview\])
```

**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>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, all of this nested list markup is horrible. I am very ashamed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better than nothing! And it does render fairly well. Tables are the worst thing about markdown. 😛

[`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.
* **control:** A React component that renders the editing interface for this field. Two props will be passed:
* **value:** The current value for this field.
Expand All @@ -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>
```

Expand Down
14 changes: 1 addition & 13 deletions docs/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,19 +87,7 @@ You point to where the files are stored, and specify the fields that define them

### Widgets

Widgets define the data type and interface for entry fields. Netlify CMS comes with several built-in widgets, including:

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.
Widgets define the data type and interface for entry fields. Netlify CMS comes with several built-in [widgets](/docs/widgets).

## Customization

Expand Down
69 changes: 55 additions & 14 deletions docs/widgets.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,60 @@

Widgets define the data type and interface for entry fields. Netlify CMS comes with several built-in widgets, including:

| Name | UI | Data Type |
| -------- | --------------------------------- | --------------------------------------------------|
| `string` | text input | string |
| `boolean` | toggle switch | boolean |
| `text` | textarea input | string (multiline) |
| `number` | number input | number |
| `markdown` | rich text editor | string (markdown) |
| `datetime` | date picker | string (ISO date) |
| `select` | select input (dropdown) | string |
| `image` | file picker w/ drag and drop | image file |
| `file` | file picker w/ drag and drop | file |
| `hidden` | none | string |
| `object` | group of other widgets | Immutable Map containing field values |
| `list` | repeatable group of other widgets | Immutable List of objects containing field values |
| Name | UI | Data Type |
| -------- | ---------------------------------- | ---------------------------------------------------|
| `string` | text input | string |
| `boolean` | toggle switch | boolean |
| `text` | textarea input | string (multiline) |
| `number` | number input | number |
| `markdown` | rich text editor | string (markdown) |
| `datetime` | date picker | string (ISO date) |
| `select` | select input (dropdown) | string |
| `image` | file picker w/ drag and drop | image file |
| `file` | file picker w/ drag and drop | file |
| `hidden` | none | string |
| `object` | group of other widgets | Immutable Map 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)!

### 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
```
18 changes: 18 additions & 0 deletions example/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ collections: # A list of collections the CMS should be able to edit
folder: "_sink"
create: true
fields:
- label: "Related Post"
name: "post"
widget: "relationKitchenSinkPost"
collection: "posts"
searchFields: ["title", "body"]
valueField: "title"
- {label: "Title", name: "title", widget: "string"}
- {label: "Boolean", name: "boolean", widget: "boolean", default: true}
- {label: "Text", name: "text", widget: "text"}
Expand All @@ -77,6 +83,12 @@ collections: # A list of collections the CMS should be able to edit
name: "object"
widget: "object"
fields:
- label: "Related Post"
name: "post"
widget: "relationKitchenSinkPost"
collection: "posts"
searchFields: ["title", "body"]
valueField: "title"
- {label: "String", name: "string", widget: "string"}
- {label: "Boolean", name: "boolean", widget: "boolean", default: false}
- {label: "Text", name: "text", widget: "text"}
Expand Down Expand Up @@ -119,6 +131,12 @@ collections: # A list of collections the CMS should be able to edit
name: "list"
widget: "list"
fields:
- label: "Related Post"
name: "post"
widget: "relationKitchenSinkPost"
collection: "posts"
searchFields: ["title", "body"]
valueField: "title"
- {label: "String", name: "string", widget: "string"}
- {label: "Boolean", name: "boolean", widget: "boolean"}
- {label: "Text", name: "text", widget: "text"}
Expand Down
24 changes: 23 additions & 1 deletion example/index.html

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion src/components/PreviewPane/PreviewPane.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import styles from './PreviewPane.css';
export default class PreviewPane extends React.Component {

getWidget = (field, value, props) => {
const { fieldsMetaData, getAsset } = props;
const { fieldsMetaData, getAsset, entry } = props;
const widget = resolveWidget(field.get('widget'));

return !widget.preview ? null : React.createElement(widget.preview, {
Expand All @@ -22,6 +22,8 @@ export default class PreviewPane extends React.Component {
value: value && Map.isMap(value) ? value.get(field.get('name')) : value,
metadata: fieldsMetaData && fieldsMetaData.get(field.get('name')),
getAsset,
entry,
fieldsMetaData,
});
};

Expand Down
2 changes: 1 addition & 1 deletion src/components/Widgets/RelationControl.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class RelationControl extends Component {
const collection = field.get('collection');
const searchFields = field.get('searchFields').toJS();
this.props.query(this.controlID, collection, searchFields, value);
}, 100);
}, 500);

onSuggestionsClearRequested = () => {
this.props.clearSearch();
Expand Down
5 changes: 4 additions & 1 deletion src/lib/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ export default {
return _registry.previewStyles;
},
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) {
return _registry.widgets[name];
Expand Down