Browse Source

Add `withProps` and `beautifyAttr` options to `Component.toHTML` method.

pull/4093/head
Artur Arseniev 5 years ago
parent
commit
19a7e48b01
  1. 41
      src/dom_components/model/Component.js
  2. 93
      test/specs/dom_components/model/Component.js

41
src/dom_components/model/Component.js

@ -1371,7 +1371,9 @@ export default class Component extends Model.extend(Styleable) {
* Return HTML string of the component
* @param {Object} [opts={}] Options
* @param {String} [opts.tag] Custom tagName
* @param {Object|Function} [opts.attributes=null] You can pass an object of custom attributes to replace
* @param {Object|Function} [opts.attributes=null] You can pass an object of custom attributes to replace.
* @param {Boolean} [opts.withProps] Include component properties as `data-gjs-*` attributes. This allows you to have re-importable HTML.
* @param {Boolean} [opts.beautifyAttr] In case the attribute value contains a `"` char, instead of escaping it (`attr="value ""`), the attribute will be quoted using single quotes (`attr='value "'`).
* with the current one or you can even pass a function to generate attributes dynamically
* @return {String} HTML string
* @example
@ -1415,15 +1417,33 @@ export default class Component extends Model.extend(Styleable) {
}
}
if (opts.withProps) {
const props = this.toJSON();
forEach(props, (value, key) => {
const skipProps = ['classes', 'attributes', 'components'];
if (key[0] !== '_' && skipProps.indexOf(key) < 0) {
attributes[`data-gjs-${key}`] = isArray(value) || isObject(value) ? JSON.stringify(value) : value;
}
});
}
for (let attr in attributes) {
const val = attributes[attr];
const value = isString(val) ? val.replace(/"/g, '&quot;') : val;
if (!isUndefined(value)) {
if (isBoolean(value)) {
value && attrs.push(attr);
if (!isUndefined(val) && val !== null) {
if (isBoolean(val)) {
val && attrs.push(attr);
} else {
attrs.push(`${attr}="${value}"`);
let valueRes = '';
if (opts.beautifyAttr && isString(val) && val.indexOf('"') >= 0) {
valueRes = `'${val.replace(/'/g, '&apos;')}'`;
} else {
const value = isString(val) ? val.replace(/"/g, '&quot;') : val;
valueRes = `"${value}"`;
}
attrs.push(`${attr}=${valueRes}`);
}
}
}
@ -1436,6 +1456,15 @@ export default class Component extends Model.extend(Styleable) {
return code;
}
/**
* Get inner HTML of the component
* @param {Object} [opts={}] Same options of `toHTML`
* @returns {String} HTML string
*/
getInnerHTML(opts) {
return this.__innerHTML(opts);
}
__innerHTML(opts = {}) {
const cmps = this.components();
return !cmps.length ? this.get('content') : cmps.map(c => c.toHTML(opts)).join('');

93
test/specs/dom_components/model/Component.js

@ -18,14 +18,14 @@ let em;
describe('Component', () => {
beforeEach(() => {
em = new Editor();
em = new Editor({ avoidDefaults: true });
dcomp = em.get('DomComponents');
em.get('PageManager').onLoad();
// dcomp = new DomComponents();
compOpts = {
em,
componentTypes: dcomp.componentTypes,
domc: dcomp
domc: dcomp,
};
obj = new Component({}, compOpts);
dcomp.init({ em });
@ -51,22 +51,11 @@ describe('Component', () => {
});
test('Clones correctly with traits', () => {
obj
.get('traits')
.at(0)
.set('value', 'testTitle');
obj.get('traits').at(0).set('value', 'testTitle');
var cloned = obj.clone();
cloned.set('stylable', 0);
cloned
.get('traits')
.at(0)
.set('value', 'testTitle2');
expect(
obj
.get('traits')
.at(0)
.get('value')
).toEqual('testTitle');
cloned.get('traits').at(0).set('value', 'testTitle2');
expect(obj.get('traits').at(0).get('value')).toEqual('testTitle');
expect(obj.get('stylable')).toEqual(true);
});
@ -75,12 +64,12 @@ describe('Component', () => {
{
label: 'Title',
name: 'title',
value: 'The title'
value: 'The title',
},
{
label: 'Context',
value: 'primary'
}
value: 'primary',
},
]);
expect(obj.get('attributes')).toEqual({ title: 'The title' });
});
@ -104,27 +93,25 @@ describe('Component', () => {
tagName: 'article',
attributes: {
'data-test1': 'value1',
'data-test2': 'value2'
}
'data-test2': 'value2',
},
});
expect(obj.toHTML()).toEqual(
'<article data-test1="value1" data-test2="value2"></article>'
);
expect(obj.toHTML()).toEqual('<article data-test1="value1" data-test2="value2"></article>');
});
test('Component toHTML with value-less attribute', () => {
obj = new Component({
tagName: 'div',
attributes: {
'data-is-a-test': ''
}
'data-is-a-test': '',
},
});
expect(obj.toHTML()).toEqual('<div data-is-a-test=""></div>');
});
test('Component toHTML with classes', () => {
obj = new Component({
tagName: 'article'
tagName: 'article',
});
['class1', 'class2'].forEach(item => {
obj.get('classes').add({ name: item });
@ -157,17 +144,41 @@ describe('Component', () => {
expect(obj.toHTML()).toEqual('<div data-test="&quot;value&quot;"></div>');
});
test('Component toHTML and withProps', () => {
obj = new Component({}, { em });
obj.set({
bool: true,
boolf: false,
string: `st'ri"ng`,
array: [1, 'string', true],
object: { a: 1, b: 'string', c: true },
null: null,
undef: undefined,
empty: '',
zero: 0,
_private: 'value',
});
let resStr = `st'ri&quot;ng`;
let resArr = '[1,&quot;string&quot;,true]';
let resObj = '{&quot;a&quot;:1,&quot;b&quot;:&quot;string&quot;,&quot;c&quot;:true}';
let res = `<div data-gjs-bool data-gjs-string="${resStr}" data-gjs-array="${resArr}" data-gjs-object="${resObj}" data-gjs-empty="" data-gjs-zero="0"></div>`;
expect(obj.toHTML({ withProps: true })).toEqual(res);
resStr = `st&apos;ri"ng`;
resArr = '[1,"string",true]';
resObj = '{"a":1,"b":"string","c":true}';
res = `<div data-gjs-bool data-gjs-string='${resStr}' data-gjs-array='${resArr}' data-gjs-object='${resObj}' data-gjs-empty="" data-gjs-zero="0"></div>`;
expect(obj.toHTML({ withProps: true, beautifyAttr: true })).toEqual(res);
});
test('Manage correctly boolean attributes', () => {
obj = new Component();
obj.set('attributes', {
'data-test': 'value',
checked: false,
required: true,
avoid: true
avoid: true,
});
expect(obj.toHTML()).toEqual(
'<div data-test="value" required avoid></div>'
);
expect(obj.toHTML()).toEqual('<div data-test="value" required avoid></div>');
});
test('Component parse empty div', () => {
@ -261,18 +272,18 @@ describe('Component', () => {
id: 'test',
'data-test': 'value',
class: 'class1 class2',
style: 'color: white; background: #fff'
style: 'color: white; background: #fff',
});
expect(obj.getAttributes()).toEqual({
id: 'test',
class: 'class1 class2',
style: 'color:white;background:#fff;',
'data-test': 'value'
'data-test': 'value',
});
expect(obj.get('classes').length).toEqual(2);
expect(obj.getStyle()).toEqual({
color: 'white',
background: '#fff'
background: '#fff',
});
});
@ -370,7 +381,7 @@ describe('Component', () => {
obj.append({
removable: false,
draggable: false,
propagate: ['removable', 'draggable']
propagate: ['removable', 'draggable'],
});
const result = obj.components();
const newObj = result.models[0];
@ -433,7 +444,7 @@ describe('Image Component', () => {
test('Component toHTML with attributes', () => {
obj = new ComponentImage({
attributes: { alt: 'AltTest' },
src: 'testPath'
src: 'testPath',
});
expect(obj.toHTML()).toEqual('<img alt="AltTest" src="testPath"/>');
});
@ -475,7 +486,7 @@ describe('Text Component', () => {
test('Component toHTML with attributes', () => {
obj = new ComponentText({
attributes: { 'data-test': 'value' },
content: 'test content'
content: 'test content',
});
expect(obj.toHTML()).toEqual('<div data-test="value">test content</div>');
});
@ -505,7 +516,7 @@ describe('Text Node Component', () => {
test('Component toHTML with attributes', () => {
obj = new ComponentTextNode({
attributes: { 'data-test': 'value' },
content: `test content &<>"'`
content: `test content &<>"'`,
});
expect(obj.toHTML()).toEqual('test content &amp;&lt;&gt;&quot;&#039;');
});
@ -555,9 +566,7 @@ describe('Map Component', () => {
});
test('Component parse not map iframe', () => {
var el = $(
'<iframe src="https://www.youtube.com/watch?v=jNQXAC9IVRw"></iframe>'
);
var el = $('<iframe src="https://www.youtube.com/watch?v=jNQXAC9IVRw"></iframe>');
obj = ComponentMap.isComponent(el.get(0));
expect(obj).toEqual('');
});
@ -593,7 +602,7 @@ describe('Components', () => {
em.get('PageManager').onLoad();
compOpts = {
em,
componentTypes: dcomp.componentTypes
componentTypes: dcomp.componentTypes,
};
});

Loading…
Cancel
Save