Skip to content

Commit 94bfae3

Browse files
authored
Merge pull request #8 from contentstack/feature/supercharged-rte
Feature Json rte
2 parents a4775ad + d492f49 commit 94bfae3

18 files changed

+555
-81
lines changed

README.md

Lines changed: 95 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,41 +22,52 @@ Use the following command to install Contentstack JavaScript Utils SDK:
2222
npm i @contentstack/utils
2323
```
2424
## Usage
25-
Let’s learn how you can use Utils SDK to render embedded items.
25+
Let’s learn how you can use Utils SDK to render RTE embedded items and Supercharged RTE Json to HTML.
2626

2727
### Create Render Option
28-
To render embedded items on the front-end, use the renderOptions function, and define the UI elements you want to show in the front-end of your website, as shown in the example below:
28+
To render embedded items on the front-end, use the renderOptions function, and define the UI elements you want to show in the front-end of your website, as shown in the example below:
2929
```js
3030
const renderOptions = {
31-
//to render block-type embedded items
31+
// to render Supercharged RTE NodeType content like paragraph, link, table, order list, un-order list and more.
32+
p: (node, next) => {
33+
`<p class='class-id'>${next(node.children)}</p>` // you will need to call next function with node children contents
34+
}
35+
h1: (node, next) => {
36+
`<h1 class='class-id'>${next(node.children)}</h1>` // you will need to call next function with node children contents
37+
}
38+
// to render Supercharged RTE MarkType content like bold, italic, underline, strikethrough, inlineCode, subscript, and superscript
39+
bold: (text) => {
40+
`<b>${next(node.children)}</b>`
41+
}
42+
// to render block-type embedded items
3243
block: {
33-
'product': (entry, metadata) => {
34-
`<div>
35-
<h2 >${entry.title}</h2>
36-
<img src=${entry.product_image.url} alt=${entry.product_image.title}/>
37-
<p>${entry.price}</p>
38-
</div>`
44+
'product': (item, metadata) => {
45+
`<div>
46+
<h2 >${item.title}</h2>
47+
<img src=${item.product_image.url} alt=${item.product_image.title}/>
48+
<p>${item.price}</p>
49+
</div>`
3950
},
40-
//to render the default
41-
'$default': (entry, metadata) => {
42-
`<div>
43-
<h2>${entry.title}</h2>
44-
<p>${ntry.description}</p>
45-
</div>`
51+
// to render the default
52+
'$default': (item, metadata) => {
53+
`<div>
54+
<h2>${item.title}</h2>
55+
<p>${item.description}</p>
56+
</div>`
4657
}
4758
},
48-
//to display inline embedded items
59+
// to display inline embedded items
4960
inline: {
50-
'$default': (entry) => {
51-
`<span><b>${entry.title}</b> - ${entry.description}</span>`
52-
}
61+
'$default': (item, metadata) => {
62+
`<span><b>${item.title}</b> - ${item.description}</span>`
63+
}
5364
},
54-
//to display embedded items inserted via link
55-
link: (entry, metadata) => {
65+
// to display embedded items inserted via link
66+
link: (item, metadata) => {
5667
`<a href="${metadata.attributes.href}">${metadata.text}</a>`
5768
},
5869
// to display assets
59-
display: (asset, metadata) => {
70+
display: (item, metadata) => {
6071
`<img src=${metadata.attributes.src} alt=${metadata.alt} />`
6172
}
6273
}
@@ -66,7 +77,8 @@ const renderOptions = {
6677
Contentstack Utils SDK lets you interact with the Content Delivery APIs and retrieve embedded items from the RTE field of an entry.
6778

6879
### Fetch Embedded Item(s) from a Single Entry
69-
To get an embedded item of a single entry, you need to provide the stack API key, environment name, delivery token, content type and entry UID. Then, use the includeEmbeddedItems and Contentstack.Utils.render functions as shown below:
80+
#### Render HTML RTE Embedded object
81+
To get an embedded item of a single entry, you need to provide the stack API key, environment name, delivery token, content type and entry UID. Then, use the `includeEmbeddedItems` and `Contentstack.Utils.render` functions as shown below:
7082
```js
7183
import * as Contentstack from 'contentstack'
7284
const stack = Contentstack.Stack({
@@ -88,9 +100,35 @@ If you have multiple RTE fields in an entry and want to fetch the embedded items
88100
Refer to the example code below:
89101
```js
90102
//code to render embedded item from an RTE field and from another RTE field nested within a group field
91-
Contentstack.Utils.render({ entry, path: ["rte_fieldUid", "group.rtefieldUID"], renderOption })
103+
Contentstack.Utils.render({ entry, path: ["rte_fieldUid", "group.rteFieldUID"], renderOption })
92104
```
105+
106+
#### Render Supercharged RTE contents
107+
To get a single entry, you need to provide the stack API key, environment name, delivery token, content type and entry UID. Then, use `Contentstack.Utils.jsonToHtml` function as shown below:
108+
```js
109+
import * as Contentstack from 'contentstack'
110+
const stack = Contentstack.Stack({
111+
api_key: '<API_KEY>',
112+
delivery_token: '<ENVIRONMENT_SPECIFIC_DELIVERY_TOKEN>',
113+
environment: '<ENVIRONMENT>'})
114+
115+
stack.ContentType('<CONTENT_TYPE_UID>')
116+
.Entry('<ENTRY_UID>')
117+
.toJSON()
118+
.fetch()
119+
.then(entry => {
120+
Contentstack.Utils.jsonToHtml({
121+
entry,
122+
path: ["rte_fieldUid", "group.rteFieldUID"],
123+
renderOption
124+
})
125+
})
126+
```
127+
> Node: Supercharged RTE also supports Embedded items to get all embedded items while fetching entry use `includeEmbeddedItems` function.
128+
93129
### Fetch Embedded Item(s) from Multiple Entries
130+
#### Render HTML RTE Embedded object
131+
94132
To get embedded items from multiple entries, you need to provide the content type UID. You can also use the path variable in case the entries have multiple RTE fields.
95133
```js
96134
import Contentstack from 'contentstack'
@@ -107,7 +145,38 @@ stack.ContentType('<CONTENT_TYPE_UID>')
107145
.find()
108146
.then(result => {
109147
result.forEach(entry => {
110-
Contentstack.Utils.render({ entry, path: ['rte', 'group.rtefieldUID'], renderOption })
148+
Contentstack.Utils.render({
149+
entry,
150+
path: ['rte', 'group.rteFieldUID'],
151+
renderOption
152+
})
111153
})
112154
})
113-
```
155+
```
156+
157+
#### Render Supercharged RTE contents
158+
To get a multiple entries, you need to provide the stack API key, environment name, delivery token, content type and entry UID. Then, use `Contentstack.Utils.jsonToHtml` function as shown below:
159+
```js
160+
import * as Contentstack from 'contentstack'
161+
const stack = Contentstack.Stack({
162+
api_key: '<API_KEY>',
163+
delivery_token: '<ENVIRONMENT_SPECIFIC_DELIVERY_TOKEN>',
164+
environment: '<ENVIRONMENT>'})
165+
166+
stack.ContentType('<CONTENT_TYPE_UID>')
167+
.Query()
168+
.toJSON()
169+
.where('title', '<entry_title_to_search>')
170+
.find()
171+
.then(result => {
172+
result.forEach(entry => {
173+
Contentstack.Utils.jsonToHtml({
174+
entry,
175+
path: ["rte_fieldUid", "group.rteFieldUID"],
176+
renderOption
177+
})
178+
})
179+
})
180+
```
181+
182+
> Node: Supercharged RTE also supports Embedded items to get all embedded items while fetching entry use `includeEmbeddedItems` function.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"url": "https://github.com/contentstack/contentstack-utils-javascript.git"
1919
},
2020
"scripts": {
21-
"test": "jest --config jestconfig.json ",
21+
"test": "jest --config jestconfig.json",
2222
"test:badges": "npm run test && jest-coverage-badges --output ./badges",
2323
"test:debug": "jest --watchAll --config jestconfig.json --runInBand",
2424
"prebuild": "rimraf dist",

src/Models/embedded-object.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export interface EmbeddedObject {
1+
export interface EmbeddedItem {
22
uid: string;
33
_content_type_uid: string;
44
[propName: string]: any;
@@ -10,6 +10,6 @@ export interface EmbedModel<T> {
1010

1111
export interface EntryEmbedable {
1212
uid: string;
13-
_embedded_items?: EmbedModel<EmbeddedObject>
13+
_embedded_items?: EmbedModel<EmbeddedItem>
1414
[propName: string]: any;
1515
}

src/Models/metadata-model.ts

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
import StyleType from '../embedded-types/style-type';
2+
import TextNode from '../nodes/text';
3+
import { EmbeddedItem } from './embedded-object';
24
export interface Metadata {
35
text: string
4-
itemUid: string
5-
itemType: 'entry' | 'asset'
6-
styleType: StyleType
76
attributes: Attributes
7+
8+
itemUid: string | undefined
9+
itemType: 'entry' | 'asset' | undefined
10+
styleType: StyleType | undefined
811
contentTypeUid: string | undefined
912
}
1013

1114
export interface Attributes {
12-
type: 'entry' | 'asset',
13-
class: string,
15+
type?: 'entry' | 'asset',
16+
class?: string,
1417
[key: string]: any,
15-
'sys-style-type': string
18+
'sys-style-type'?: string,
1619
}
1720

1821
export interface EntryAttributes extends Attributes {
@@ -28,13 +31,56 @@ export interface AssetAttributes extends Attributes {
2831
}
2932

3033
export function createMetadata(attribute: Attributes): Metadata {
31-
const metadata: Metadata = {
34+
return {
3235
text: attribute['#text'],
3336
itemUid: attribute["data-sys-entry-uid"] || attribute["data-sys-asset-uid"],
3437
itemType: attribute.type,
3538
styleType: attribute["sys-style-type"] as StyleType,
3639
attributes: attribute,
37-
contentTypeUid: attribute["data-sys-content-type-uid"]
40+
contentTypeUid: attribute["data-sys-content-type-uid"],
41+
}
42+
}
43+
44+
export function nodeToMetadata(attribute: Attributes, textNode: TextNode): Metadata {
45+
return {
46+
text: textNode.text,
47+
itemUid: attribute["entry-uid"] || attribute["asset-uid"],
48+
itemType: attribute.type,
49+
styleType: attribute["display-type"] as StyleType,
50+
attributes: attribute,
51+
contentTypeUid: attribute["content-type-uid"],
52+
}
53+
}
54+
55+
export function attributeToString( attributes: Attributes):string {
56+
let result = ''
57+
for (const key in attributes) {
58+
if (Object.prototype.hasOwnProperty.call(attributes, key)) {
59+
let element = attributes[key];
60+
if (element instanceof Array) {
61+
let elementString = ''
62+
let isFirst = true
63+
element.forEach((value) => {
64+
if (isFirst) {
65+
elementString += `${value}`
66+
isFirst = false
67+
}else {
68+
elementString += `, ${value}`
69+
}
70+
})
71+
element = elementString
72+
} else if (typeof element === 'object') {
73+
let elementString = ''
74+
for (const elementKey in element) {
75+
if (Object.prototype.hasOwnProperty.call(element, elementKey)) {
76+
const value = element[elementKey];
77+
elementString += `${elementKey}:${value}; `
78+
}
79+
}
80+
element = elementString
81+
}
82+
result += ` ${key}="${element}"`
83+
}
3884
}
39-
return metadata
85+
return result
4086
}

src/extensions/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ const frameflag = 'documentfragmentcontainer';
55

66
declare global {
77
interface String {
8-
forEachEmbeddedObject(callbackfn: (embededObjectTag: string, object: Metadata) => void): void;
8+
forEachEmbeddedItem(callbackfn: (embededObjectTag: string, object: Metadata) => void): void;
99
}
1010
}
1111

12-
String.prototype.forEachEmbeddedObject = function (
12+
String.prototype.forEachEmbeddedItem = function (
1313
callbackfn: (embededObjectTag: string, object: Metadata) => void,
1414
): void {
1515

src/helper/find-embeded-object.ts

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
1-
import { EntryEmbedable, EmbeddedObject } from '../Models/embedded-object';
2-
import { RenderOption, RenderObject, RenderContentType } from '../options/index';
1+
import { EntryEmbedable, EmbeddedItem } from '../Models/embedded-object';
2+
import { RenderOption, RenderNode, RenderContentType, RenderItem } from '../options/index';
33
import { EntryAttributes, Metadata } from '../Models/metadata-model';
44
import { defaultOptions } from '../options/default-options';
55

66
// This function will find Embedded object present in string
77
export function findEmbeddedEntry(
88
uid: string,
99
contentTypeUid: string,
10-
embeddeditems: EmbeddedObject[] = [],
11-
): EmbeddedObject[] {
10+
embeddeditems: EmbeddedItem[] = [],
11+
): EmbeddedItem[] {
1212
return embeddeditems.filter((entry) => {
1313
if (entry.uid === uid && entry._content_type_uid === contentTypeUid) {
1414
return entry;
1515
}
1616
});
1717
}
1818

19-
export function findEmbeddedAsset(uid: string, embeddedAssets: EmbeddedObject[] = []): EmbeddedObject[] {
19+
export function findEmbeddedAsset(uid: string, embeddedAssets: EmbeddedItem[] = []): EmbeddedItem[] {
2020
return embeddedAssets.filter((asset) => {
2121
if (asset.uid === uid) {
2222
return asset;
2323
}
2424
});
2525
}
2626

27-
export function findEmbeddedObjects(object: Metadata, entry: EntryEmbedable): (EmbeddedObject)[] {
27+
export function findEmbeddedItems(object: Metadata, entry: EntryEmbedable): (EmbeddedItem)[] {
2828
if (object && object !== undefined && entry && entry !== undefined) {
2929
if (entry._embedded_items !== undefined) {
3030
const entryEmbedable = entry
@@ -45,33 +45,45 @@ export function findEmbeddedObjects(object: Metadata, entry: EntryEmbedable): (E
4545
}
4646

4747
export function findRenderString(
48+
item: EmbeddedItem,
4849
metadata: Metadata,
49-
renderModel: EmbeddedObject,
5050
renderOptions?: RenderOption,
5151
): string {
52-
if ((!renderModel && renderModel === undefined) || (!metadata && metadata === undefined)) {
52+
if ((!item && item === undefined) || (!metadata && metadata === undefined)) {
5353
return '';
5454
}
5555

5656
if (renderOptions && renderOptions[metadata.styleType] !== undefined) {
57-
const renderFunction = renderOptions[metadata.styleType] as RenderObject;
57+
const renderFunction = renderOptions[metadata.styleType] as RenderItem;
5858

59-
if (
59+
if (
6060
(metadata.attributes as EntryAttributes)['data-sys-content-type-uid'] !== undefined &&
6161
typeof renderFunction !== 'function' &&
6262
renderFunction[(metadata.attributes as EntryAttributes)['data-sys-content-type-uid']] !== undefined
6363
) {
64-
return (renderFunction as RenderContentType)[(metadata.attributes as EntryAttributes)['data-sys-content-type-uid']](renderModel, metadata);
64+
return (renderFunction as RenderContentType)[(metadata.attributes as EntryAttributes)['data-sys-content-type-uid']](item, metadata);
6565
} else if (
6666
(metadata.attributes as EntryAttributes)['data-sys-content-type-uid'] !== undefined &&
6767
typeof renderFunction !== 'function' &&
6868
(renderFunction as RenderContentType).$default !== undefined
6969
) {
70-
return (renderFunction as RenderContentType).$default(renderModel, metadata);
71-
} else if (typeof renderFunction === 'function') {
72-
return renderFunction(renderModel, metadata);
70+
return (renderFunction as RenderContentType).$default(item, metadata);
71+
} else if (
72+
metadata.contentTypeUid !== undefined &&
73+
typeof renderFunction !== 'function' &&
74+
renderFunction[metadata.contentTypeUid] !== undefined
75+
) {
76+
return (renderFunction as RenderContentType)[metadata.contentTypeUid](item, metadata)
77+
} else if (
78+
metadata.contentTypeUid !== undefined &&
79+
typeof renderFunction !== 'function' &&
80+
(renderFunction as RenderContentType).$default !== undefined
81+
) {
82+
return (renderFunction as RenderContentType).$default(item, metadata);
83+
} else if (typeof renderFunction === 'function') {
84+
return renderFunction(item, metadata);
7385
}
7486
}
75-
const defaultRenderFunction = defaultOptions[metadata.styleType] as RenderObject;
76-
return defaultRenderFunction(renderModel, metadata);
87+
const defaultRenderFunction = defaultOptions[metadata.styleType] as RenderItem;
88+
return defaultRenderFunction(item, metadata);
7789
}

0 commit comments

Comments
 (0)