Skip to content

Commit

Permalink
wix-incubator#214, rt-template with in-place child component
Browse files Browse the repository at this point in the history
  • Loading branch information
yatra9 committed Mar 7, 2017
1 parent d47ef7e commit 05b1ab5
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 7 deletions.
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,35 @@ export default function () {
};
```

### defining in-place child properties
In cases you'd like to set a renderable React component itself (not a function) as a child property,
You should use a **rt-template** tag with **in-place** attribute: `<rt-template prop="propName" in-place/>`.
Note that **argument** attribute and **in-place** attribute cannot be set simulatenously.

###### Sample:
```html
<div>
<rt-template prop="templateProp" in-place>
<div>some</div>
</rt-template>
</div>
```
###### Compiled (AMD):
```javascript
define([
'react',
'lodash'
], function (React, _) {
'use strict';
return function () {
function templateProp1() {
return React.createElement('div', {}, 'some');
}
return React.createElement('div', { 'templateProp': templateProp1.call(this) });
};
});
```

## Contributing

See the [Contributing page](CONTRIBUTING.md).
Expand Down
24 changes: 18 additions & 6 deletions src/reactTemplates.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ const classAttr = 'class';
const scopeAttr = 'rt-scope';
const propsAttr = 'rt-props';
const templateNode = 'rt-template';
const templateNode_PropAttr = 'prop';
const templateNode_ArgumentsAttr = 'arguments';
const templateNode_InPlaceAttr = 'in-place';
const virtualNode = 'rt-virtual';
const includeNode = 'rt-include';
const includeSrcAttr = 'src';
Expand Down Expand Up @@ -123,17 +126,21 @@ function generateTemplateProps(node, context) {
.map((child, index) => {
let templateProp = null;
if (child.name === templateNode) { // Generic explicit template tag
if (!_.has(child.attribs, 'prop')) {
throw RTCodeError.build(context, child, 'rt-template must have a prop attribute');
if (!_.has(child.attribs, templateNode_PropAttr)) {
throw RTCodeError.build(context, child, "'rt-template' should have a " + templateNode_PropAttr + ' attribute');
}
if (_.filter(child.children, {type: 'tag'}).length !== 1) {
throw RTCodeError.build(context, child, "'rt-template' should have a single non-text element as direct child");
}
if (_.has(child.attribs, templateNode_ArgumentsAttr) && _.has(child.attribs, templateNode_InPlaceAttr)) {
throw RTCodeError.build(context, child, "'" + templateNode_ArgumentsAttr + "' and '" + templateNode_InPlaceAttr + "' cannot coexist together");
}

const childTemplate = _.find(context.options.propTemplates, {prop: child.attribs.prop}) || {arguments: []};
const childTemplate = _.find(context.options.propTemplates, {prop: child.attribs[templateNode_PropAttr]}) || {arguments: []};
templateProp = {
prop: child.attribs.prop,
arguments: (child.attribs.arguments ? child.attribs.arguments.split(',') : childTemplate.arguments) || []
prop: child.attribs[templateNode_PropAttr],
arguments: (child.attribs[templateNode_ArgumentsAttr] ? child.attribs[templateNode_ArgumentsAttr].split(',') : childTemplate.arguments) || [],
inplace: _.has(child.attribs, templateNode_InPlaceAttr)
};
} else if (propTemplateDefinition && propTemplateDefinition[child.name]) { // Implicit child template from configuration
templateProp = {
Expand Down Expand Up @@ -161,7 +168,7 @@ function generateTemplateProps(node, context) {
context.boundParams = oldBoundParams;

const generatedFuncName = generateInjectedFunc(context, templateProp.prop, functionBody, functionParams);
props[templateProp.prop] = genBind(generatedFuncName, _.values(context.boundParams));
props[templateProp.prop] = templateProp.inplace ? genCall(generatedFuncName, _.values(context.boundParams)) : genBind(generatedFuncName, _.values(context.boundParams));

// Remove the template child from the children definition.
node.children.splice(templateProp.childIndex, 1);
Expand Down Expand Up @@ -240,6 +247,11 @@ function genBind(func, args) {
return `${func}.bind(${bindArgs.join(',')})`;
}

function genCall(func, args) {
const bindArgs = ['this'].concat(args);
return `${func}.call(${bindArgs.join(',')})`;
}

function handleStyleProp(val, node, context) {
const styleStr = _(val)
.split(';')
Expand Down
5 changes: 5 additions & 0 deletions test/data/invalid/invalid-rt-template-3.rt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div>
<rt-template props="some">
<div>1</div>
</rt-template>
</div>
5 changes: 5 additions & 0 deletions test/data/invalid/invalid-rt-template-4.rt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div>
<rt-template prop="some" arguments="" in-place>
<div>1</div>
</rt-template>
</div>
5 changes: 5 additions & 0 deletions test/data/propTemplates/inplaceTemplate.rt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div>
<rt-template prop="templateProp" in-place>
<div>some</div>
</rt-template>
</div>
12 changes: 12 additions & 0 deletions test/data/propTemplates/inplaceTemplate.rt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
define([
'react',
'lodash'
], function (React, _) {
'use strict';
return function () {
function templateProp1() {
return React.createElement('div', {}, 'some');
}
return React.createElement('div', { 'templateProp': templateProp1.call(this) });
};
});
5 changes: 5 additions & 0 deletions test/data/propTemplates/inplaceTemplateInScope.rt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div rt-scope="'boten' as name">
<rt-template prop="templateProp" in-place>
<div>Name: {name} some</div>
</rt-template>
</div>
16 changes: 16 additions & 0 deletions test/data/propTemplates/inplaceTemplateInScope.rt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
define([
'react',
'lodash'
], function (React, _) {
'use strict';
return function () {
function templateProp1(name) {
return React.createElement('div', {}, 'Name: ', name, ' some');
}
function scopeName2() {
var name = 'boten';
return React.createElement('div', { 'templateProp': templateProp1.call(this, name) });
}
return scopeName2.apply(this, []);
};
});
2 changes: 2 additions & 0 deletions test/src/rt.invalid.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ module.exports = {
{file: 'invalid-rt-import-4.rt', issue: new RTCodeError("'rt-import' must be a toplevel node", 9, 54, 2, 4)},
{file: 'invalid-rt-template-1.rt', issue: new RTCodeError("'rt-template' should have a single non-text element as direct child", 9, 88, 2, 4)},
{file: 'invalid-rt-template-2.rt', issue: new RTCodeError("'rt-template' should have a single non-text element as direct child", 9, 90, 2, 4)},
{file: 'invalid-rt-template-3.rt', issue: new RTCodeError("'rt-template' should have a prop attribute", 9, 72, 2, 4)},
{file: 'invalid-rt-template-4.rt', issue: new RTCodeError("'arguments' and 'in-place' cannot coexist together", 9, 93, 2, 4)},
{file: 'invalid-brace.rt', issue: new RTCodeError('Unexpected end of input', 128, 163, 5, 11)},
{file: 'invalid-style-1.rt', issue: new RTCodeError('Unexpected token ILLEGAL', 10, 39, 2, 5)},
{file: 'invalid-style-2.rt', issue: new RTCodeError('style attribute keys cannot contain { } expressions', 35, 68, 2, 5)},
Expand Down
5 changes: 4 additions & 1 deletion test/src/rt.valid.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ module.exports = {

test('autobinding conversion test', t => {
const options = {
modules: 'amd',
autobind: true
};
const files = ['autobind.rt'];
Expand All @@ -62,7 +63,9 @@ module.exports = {
'templateInScope.rt',
'implicitTemplate.rt',
'twoTemplates.rt',
'siblingTemplates.rt'
'siblingTemplates.rt',
'inplaceTemplate.rt',
'inplaceTemplateInScope.rt'
].map(file => path.join('propTemplates', file));
testFiles(t, files, options);
});
Expand Down

0 comments on commit 05b1ab5

Please sign in to comment.