WORKING DRAFT
Canvas/composer JSON structure
Each composer item in a composer/canvas is made up of a type
a value
array which contains fragments
that can have their own properties
and decorators
.
- _paragraph
- _heading
- _lists
- _divider
- _location
- _image - Needs more discussion
- _imageArray - Needs more discussion
- _code
- _quote
- _markup
- _callout
- _embed
- _component - Needs more discussion
_table
(coming)
Example: This is a paragraph
{
"type": "_paragraph",
"value": [
{
"type": "_fragment",
"value": "This is a paragraph"
}
]
}
Example: This is some **bold** _text_
{
"type": "_paragraph",
"value": [
{
"type": "_fragment",
"value": "This is some "
},
{
"type": "_fragment",
"properties": {
"decorators": ["strong"]
},
"value": "bold"
},
{
"type": "_fragment",
"properties": {
"decorators": ["emphasis"]
},
"value": " text"
}
]
}
Example: Check out our latest [Boston Fern](contensis:///0123-0123-01547-7894) plant
{
"type": "_paragraph",
"value": [
{
"type": "_fragment",
"value": "Check out our latest "
},
{
"type": "_fragment",
"properties": {
"decorators": ["entry"],
"entry": {
"entryId": "0123-0123-01547-7894",
"contentType": "plantProduct",
"nodeId": "0123-0123-01547-7894"
},
},
"value": "{{entryTitle}}"
},
{
"type": "_fragment",
"value": " plant"
}
]
}
Example: This is some **bold** **text**
Note: This feels odd to include the blank space between two decorators as blank fragment
{
"type": "_paragraph",
"value": [
{
"type": "_fragment",
"value": "This is some "
},
{
"type": "_fragment",
"properties": {
"decorators": ["strong"]
},
"value": "bold "
},
{
"type": "_fragment",
"value": " "
},
{
"type": "_fragment",
"properties": {
"decorators": ["strong"]
},
"value": "text"
}
]
}
Example: `This is a sentence that has a link that is bold
{
"type": "_paragraph",
"value": [
{
"type": "_fragment",
"value": "This is a sentence that "
},
{
"type": "_fragment",
"properties": {
"decorators": ["strong"]
},
"value": [
{
"type": "_fragment",
"value": "has a ",
},
{
"type": "_link",
"value": {
"text": "link",
"url": "http://www.bbc.co.uk",
"target": "blank"
}
}
,{
"type": "_fragment",
"value": "that is bold"
}
}
]
}
]
}
Represents a lede introductory paragraph.
Example: This is a lede paragraph
Option 1: Lede is considered a visual styling of the paragraph for the introductory opening of a story.
{
"type": "_paragraph",
"properties": {
"type": "lede"
},
"value": [
{
"type": "_fragment",
"value": "This is a paragraph"
}
]
}
Option 2: Lede is considered a core part of the content and is decorated with a first class decorator.
{
"type": "_paragraph",
"value": [
{
"type": "_fragment",
"properties": {
"decorators": ["lede"]
},
"value": "This is a paragraph"
}
]
}
Option 3: Lede is considered a first class block?
{
"type": "_lede",
"value": [
{
"type": "_fragment",
"value": "This is a paragraph"
}
]
}
Example: # This is my heading
{
"type": "_heading",
"properties": {
"level": 1
},
"value": {
"type": "_fragment",
"value": "This is my heading "
}
}
This is my heading
Example: # This **is** my heading
{
"type": "_heading",
"properties": {
"level": 1
},
"value": [
{
"type": "_fragment",
"value": "This "
},
{
"type": "_fragment",
"value": "is",
"properties": {
"decorators": ["strong"]
}
},
{
"type": "_fragment",
"value": " my heading"
}
]
}
- **_List item one_**
- List item two
- **List item three**
{
"type": "_list",
"properties": {
"listType": "unordered"
},
"value": [
{
"type": "_listItem",
"value": [
{
"type": "_fragment",
"properties": {
"decorators": ["strong, italic"]
},
"value": "List item one"
}
]
},
{
"type": "_listItem",
"value": [
{
"type": "_fragment",
"value": "List item two"
}
]
},
{
"type": "_listItem",
"value": [
{
"type": "_fragment",
"properties": {
"decorators": ["strong"]
},
"value": "List item three"
}
]
}
]
}
1. **_List item one_**
2. List item two
3. **List item three**
{
"type": "_list",
"properties": {
"_list": {
"listType": "ordered",
"start": 1
}
},
"value": [
{
"type": "_listItem",
"value": [
{
"type": "_fragment",
"properties": {
"decorators": ["strong, italic"]
},
"value": "List item one"
}
]
},
{
"type": "_listItem",
"value": [
{
"type": "_fragment",
"value": "List item two"
}
]
},
{
"type": "_listItem",
"value": [
{
"type": "_fragment",
"properties": {
"decorators": ["strong"]
},
"value": "List item three"
}
]
}
]
}
---
{
"type": "_divider",
"properties": {
"style": "dotted",
}
}
Selecting a latitude and longitude on a map.
{
"type": "_location",
"value": [
{
"lon": -2.997664,
"lat": 51.584151
}
]
}
- Images feel as though they go against the grain of the new proposed _type format.
- We could duplicate certain properties of an image such as caption, alt-text and transforms.
{
"type": "_image",
"value": "https://unsplash.com/photos/TYQ6fyF3Amc"
}
{
"type": "_image",
"properties": {
"caption": "",
"altText": "A photo of Richard Saunders.",
"transformations": null
},
"value": {
"altText": "A photo of Richard Saunders.",
"sys": {
"projectId": "contensis",
"metadata": {
"includeInSearch": true,
"includeInAToZ": false,
"includeInMenu": false,
"includeInSiteMap": false,
"nodeId": "bc6435eb-c2e3-4cef-801f-b6061f9cdad6"
},
"properties": {
"filename": "richard-saunders-blog-image.png",
"fileSize": 611073,
"fileId": "bc6435eb-c2e3-4cef-801f-b6061f9cdad6",
"width": 600,
"height": 600
},
"version": {
"createdBy": "ServicesUser",
"created": "2019-11-04T16:57:43.7464974Z",
"modifiedBy": "ServicesUser",
"modified": "2019-11-04T16:57:43.7464974Z",
"publishedBy": "ServicesUser",
"published": "2019-11-04T16:57:54.6463551Z",
"versionNo": "1.0"
},
"owner": "r.bromley",
"isPublished": false,
"availableLanguages": ["en-GB"],
"unavailableLanguages": ["en-GB"],
"translationState": "none",
"id": "bc6435eb-c2e3-4cef-801f-b6061f9cdad6",
"dataFormat": "asset",
"language": "en-GB",
"uri": "/image-library/people-images/richard-saunders-blog-image.png",
"contentTypeId": "image"
},
"entryTitle": "richard-saunders-blog-image",
"entryDescription": null
}
}
Below based on composer item
{
"type": "_image",
"value": {
"asset": {
"altText": "A photo of Richard Saunders.",
"sys": {
"projectId": "contensis",
"metadata": {
"includeInSearch": true,
"includeInAToZ": false,
"includeInMenu": false,
"includeInSiteMap": false,
"nodeId": "bc6435eb-c2e3-4cef-801f-b6061f9cdad6"
},
"properties": {
"filename": "richard-saunders-blog-image.png",
"fileSize": 611073,
"fileId": "bc6435eb-c2e3-4cef-801f-b6061f9cdad6",
"width": 600,
"height": 600
},
"version": {
"createdBy": "ServicesUser",
"created": "2019-11-04T16:57:43.7464974Z",
"modifiedBy": "ServicesUser",
"modified": "2019-11-04T16:57:43.7464974Z",
"publishedBy": "ServicesUser",
"published": "2019-11-04T16:57:54.6463551Z",
"versionNo": "1.0"
},
"owner": "r.bromley",
"isPublished": false,
"availableLanguages": ["en-GB"],
"unavailableLanguages": ["en-GB"],
"translationState": "none",
"id": "bc6435eb-c2e3-4cef-801f-b6061f9cdad6",
"dataFormat": "asset",
"language": "en-GB",
"uri": "/image-library/people-images/richard-saunders-blog-image.png",
"contentTypeId": "image"
},
"entryTitle": "richard-saunders-blog-image",
"entryDescription": null
},
"caption": "",
"altText": "A photo of Richard Saunders.",
"transformations": null
}
}
{
"type": "_imageArray",
"properties": {
"layout": "gallery, carousel"
},
"value": [
{
"type": "_image",
"properties": {
"caption": "",
"altText": "A photo of Jon Maskrey.",
"transformations": null
},
"value": {
"altText": "A photo of Jon Maskrey.",
"sys": {},
"entryTitle": "jon-maskrey-blog-image",
"entryDescription": null
}
},
{
"type": "_image",
"properties": {
"caption": "",
"altText": "A photo of Ryan Bromley.",
"transformations": null
},
"value": {
"altText": "A photo of Ryan Bromley.",
"sys": {},
"entryTitle": "ryan-bromley-blog-image",
"entryDescription": null
}
},
{
"type": "_image",
"properties": {
"caption": "",
"altText": "A photo of Richard Saunders.",
"transformations": null
},
"value": {
"altText": "A photo of Richard Saunders.",
"sys": {},
"entryTitle": "richard-saunders-blog-image",
"entryDescription": null
}
}
]
}
{
"type": "_code",
"properties": {
"language": "javascript"
},
"value": "console.log(\"hello world\");"
}
{
"type": "_code",
"properties": {
"display": "split" // This feels like an editor property
},
"language": "javascript",
"value": "console.log(\"hello world\");"
}
{
"type": "_number",
"value": 123456
}
{
"type": "_decimal",
"value": 10.534353453453453453425345345345
}
Example: Stay hungry, stay foolish – Steve Jobs
{
"type": "_quote",
"value": {
"text": "Stay hungry stay foolish",
"source": "Steve Jobs"
}
]
}
Possible extension is to add citation?
{
"type": "_quote",
"value": {
"text": "Stay hungry stay foolish",
"source": "Steve Jobs",
"citation": "https://www.apple.com/stevejobs/"
}
]
}
#### Support for fragments?
```json
{
"type": "_quote",
"properties": {
"source": "Steve Jobs",
"cite": "https://www.apple.com/stevejobs/"
},
"value": [
{
"type": "_fragment",
"value": "Stay",
"properties": {
"decorators": ["strong"]
},
},
{
"type": "_fragment",
"value": " hungry"
},
{
"type": "_fragment",
"value": "Stay",
"properties": {
"decorators": ["strong"]
},
},
{
"type": "_fragment",
"value": " foolish"
}
]
}
To support existing TinyMCE / Markdown data
{
"type": "_markup",
"properties": {
"format": "html"
},
"value": "<p>This is rich <em>text</em> with some <strong>styling</strong></p>"
}
The format is part of the content as it describes it.
{
"type": "_markup",
"value": {
"format": "html",
"value": "<p>This is rich <em>text</em> with some <strong>styling</strong></p>"
},
}
Strongly define the type, no need for extra properties.
{
"type": "_html",
"value": "<p>This is rich <em>text</em> with some <strong>styling</strong></p>"
}
{
"type": "_markup",
"properties": {
"format": "markdown"
},
"value": "This is rich _text_ with some **styling**"
}
The format is part of the content as it describes it.
{
"type": "_markup",
"value": {
"format": "markdown",
"value": "This is rich _text_ with some **styling**"
},
}
Strongly define the type, no need for extra properties.
{
"type": "_markdown",
"value": "This is rich _text_ with some **styling**"
}
{
"type": "_callout",
"properties": {
"type": "note",
"icon": "notepad",
},
"value": {
"title": "Heading",
"message": "This is a call out message"
}
}
or
{
"type": "_callout",
"value": {
"title": "Heading", // optional
"message": "This is a call out message",
"type": "note",
"icon": "notepad"
}
}
{
"type": "_embed",
"properties": {
"provider_name": "youtube",
"provider_url": "https://www.youtube.com"
},
"value": "https://www.youtube.com/watch?v=ArOMXELHiLw"
}
{
"type": "_link",
"value": {
"url": "https://www.bbc.co.uk",
"newWindow": true,
"rel": ["noopener", "external"]
}
}
{
"type": "_component",
"properties": {
"componentID": "iconWithText",
},
"value": {
"icon": {
"sys": {
"projectId": "contensis",
"workflow": {
"id": "contensisEntryBasic",
"state": "versionComplete",
"allowedEvents": [
"versionComplete.sysUpdate",
"versionComplete.sysDelete",
"sysUnpublish"
],
"transition": {
"invoked": "2020-04-28T15:45:21.1250724Z",
"invokedBy": "m.white",
"from": "draft",
"event": "publish",
"data": null
}
},
"metadata": {},
"version": {},
"owner": "r.bromley",
"isPublished": true,
"slug": "icon-caching-images",
"availableLanguages": ["en-GB"],
"unavailableLanguages": [],
"translationState": "none",
"id": "51639de0-a1e4-4352-b166-17f86e3558bf",
"dataFormat": "entry",
"language": "en-GB",
"contentTypeId": "icon"
},
"entryTitle": "ICON: Caching images",
"entryDescription": null
},
"text": "This is my icon text"
}
}
Decorators are applied to _fragments
.
- Strong
- Emphasis
- Underline
- Strikethrough
- Subscript
- Superscript
- Mark
- Code
- Keyboard
- Variable
- Code
- Insert
- Delete
- _links
- _comment
Do we want to support inline images to a paragraph? e.g. small icons / emoji
Example: This is a [link](https://www.bbc.co.uk) to the BBC.
{
"type": "_paragraph",
"value": [
{
"type": "_fragment",
"value": "This is a "
},
{
"type": "_fragment",
"properties": {
"decorators": ["link"],
"link": {
"url": "https://www.bbc.co.uk",
"target": "blank"
}
},
"value": "link"
},
{
"type": "_fragment",
"value": " to the BBC."
}
]
}
The ability to link to CMS content using a search to insert links to CMS content, without the need for carrying out extra lookups on the front end for the most important information.
{
"type": "_paragraph",
"value": [
{
"type": "_fragment",
"value": "This is a "
},
{
"type": "_fragment",
"properties": {
"decorators": ["inlineEntry"],
"inlineEntry": {
"entryId": "0e552c3f-5f1d-4eb4-b9dc-c5d0f5ed5c9d",
"entryUrl": "http://www.contensis.com/help-and-docs/account-settings/change-password",
"nodeId": "fa606bf2-c9fa-42f3-94cf-4d0a879c70b3",
"contentTypeId": "article"
}
},
"value": "link"
},
{
"type": "_fragment",
"value": " to the BBC."
}
]
}
Example: This text has a @@comment@@
{
"type": "_paragraph",
"value": [
{
"type": "_fragment",
"value": "This text has a "
},
{
"type": "_fragment",
"properties": {
"decorators": ["comment"],
"comment": {
"commentId": "0000-1234-5678-9101-1121",
"authorID": "BC12-1234-5678-9101-1121"
}
},
"value": "comment"
}
]
}