From 905cacf1cc8b0ee81b4ac307b252b944e9692814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Zen?= Date: Wed, 8 Feb 2017 16:28:57 -0200 Subject: [PATCH] Additional Docs --- docs/extending.md | 159 +++++++++++++++++++++++++++++++++++++++++++++ docs/validation.md | 54 +++++++++++++++ 2 files changed, 213 insertions(+) create mode 100644 docs/extending.md create mode 100644 docs/validation.md diff --git a/docs/extending.md b/docs/extending.md new file mode 100644 index 00000000..2fb44d23 --- /dev/null +++ b/docs/extending.md @@ -0,0 +1,159 @@ +# Extending Netlify CMS +The Netlify CMS exposes an `window.CMS` global object that you can use to register custom widgets, previews and editor plugins. The available methods are: + +* **registerPreviewStyle** Register a custom stylesheet to use on the preview pane. +* **registerPreviewTemplate** Registers a template for a collection. +* **registerWidget** lets you register a custom widget. +* **registerEditorComponent** lets you add a block component to the Markdown editor + +**Writing React Components inline** + +Both registerPreviewTemplate and registerWidget requires you to provide a React component. If you have a build process in place for your project, it is possible to integrate webpack and Babel for a complete React build flow. + +Although possible, it may be cumbersome or even impractical to add a React build phase. For this reason, Netlify CMS exposes two React constructs globally to allow you to create components inline: ‘createClass’ and ‘h’ (alias for React.createElement). + + +## `registerPreviewStyle` + +Register a custom stylesheet to use on the preview pane. + +`CMS.registerPreviewStyle(file);` + +**Params:** + +* file: css file path. + +**Example:** + +`CMS.registerPreviewStyle("/example.css");` + + +## `registerPreviewTemplate` + +Registers a template for a collection. + +`CMS.registerPreviewTemplate(collection, react_component);` + +**React Component Props:** + +* collection: The name of the collection which this preview component will be used for. +* react_component: A React component that renders the collection data. Three props will be passed to your component during render: + * entry: Immutable collection containing the entry data. + * widgetFor: Returns the appropriate widget preview component for a given field. + * getAsset: Returns the correct filePath or in-memory preview for uploaded images. + +**Example:** + +```html + +``` + +### Accessing Metadata +Preview Components also receive an additional prop: `fieldsMetaData`. It contains aditional information (besides the plain plain textual value of each field) that can be useful for preview purposes. + +For example, the Relation widget passes the whole selected relation data in fieldsMetaData. + +```js +export default class ArticlePreview extends React.Component { + render() { + const {entry, fieldsMetaData} = this.props; + const author = fieldsMetaData.getIn(['authors', data.author]); + + return
+

{ entry.getIn(['data', 'title']) }

+ {author && } +
+ } +} +``` + +## `registerWidget` + +lets you register a custom widget. + +`CMS.registerWidget(field, control, [preview])` + +**Params:** + +* 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. + * onChange: Callback function to update the field value. +* preview (optional): A React component that renders the preview of how the content will look. A `value` prop will be passed to this component. + + +**Example:** + +```html + +``` + +## `registerEditorComponent` + +lets your register a block level component for the Markdown editor + +`CMS.registerEditorComponent(definition)` + +**Params** + +* definition: The component definition, must specify: id, label, fields, patterns, fromBlock, toBlock, toPreview + +**Example:** + +```js +CMS.registerEditorComponent({ + // Internal id of the component + id: "youtube", + // Visible label + label: "Youtube", + // Fields the user need to fill out when adding an instance of the component + fields: [{name: 'id', label: 'Youtube Video ID', widget: 'string'}], + // Pattern to identify a block as being an instance of this component + pattern: /^{{<\s?youtube (\S+)\s?>}}/, + // Function to extract data elements from the regexp match + fromBlock: function(match) { + return { + id: match[1] + }; + }, + // Function to create a text block from an instance of this component + toBlock: function(obj) { + return '{{< youtube ' + obj.id + ' >}}'; + }, + // Preview output for this component. Can either be a string or a React component + // (Component gives better render performance) + toPreview: function(obj) { + return ( + 'Youtube Video' + ); + } +}); +``` diff --git a/docs/validation.md b/docs/validation.md new file mode 100644 index 00000000..d6b9df5a --- /dev/null +++ b/docs/validation.md @@ -0,0 +1,54 @@ +# Collection Field Validation + +## Available validations to use on config.yaml: + +- Presence: By default all widgets are required, unless specified in the config. Example: +`- {label: "Subtitle", name: "subtitle", widget: "string", required: false}` + +- Pattern: Field configuration can specify a regex pattern with the appropriate error message. Example: +`- {label: "Title", name: "title", widget: "string", pattern: [".{10,}", "Should have more than 10 characters"] }` + + +## Advanced Guide (For widget authors). + +The widget control can optionally implement an `isValid` method to perform custom validations, in addition to presence and pattern. The `isValid` method will be automatically called, and it can return either a boolean value, an object with an error message or a promise. Examples: + +**Boolean** +No errors: + +```javascript + isValid = () => { + // Do internal validation + return true; + }; +``` + +Existing error: + +```javascript + isValid = () => { + // Do internal validation + return false; + }; +``` + +**Object with `error` (Useful for returning custom error messages)** +Existing error: + +```javascript + isValid = () => { + // Do internal validation + return { error: 'Your error message.' }; + }; +``` + +**Promise** +You can also return a promise from isValid. While the promise is pending, the widget will be marked as in error. When the promise resolves, the error is automatically cleared. + +```javascript + isValid = () => { + return this.existingPromise; + }; +``` + +Note: Do not create a promise inside isValid - isValid is called right before trying to persist. This means that even if a previous promise was already resolved, when the user hits 'save', `isValid` will be called again - if it returns a new Promise it will be immediately marked as in error until the new promise resolves.