Merge branch 'nirnatan-gh-pages' into gh-pages

This commit is contained in:
ido 2015-08-19 12:56:03 +03:00
commit 020d66126b
11 changed files with 252 additions and 6 deletions

View File

@ -399,6 +399,81 @@ define([
```
## properties template functions
In cases you'd like to use a property that accepts a function and return renderable React component.
You should use a **rt-template** tag that will let you do exactly that: `<rt-template prop="propName" arguments="arg1, arg2"/>`.
Templates can be used only as an immediate child of the component that it will be used in. All scope variable will be available in the template function.
###### Sample:
```html
<MyComp data="{[1,2,3]}">
<rt-template prop="renderItem" arguments="item">
<div>{item}</div>
</rt-template>
</MyComp>
```
###### Compiled (AMD):
```javascript
define([
'react/addons',
'lodash'
], function (React, _) {
'use strict';
function renderItem1(item) {
return React.createElement('div', {}, item);
}
return function () {
return React.createElement(MyComp, {
'data': [
1,
2,
3
],
'renderItem': renderItem1.bind(this)
});
};
});
```
###### Compiled (with CommonJS flag):
```javascript
'use strict';
var React = require('react/addons');
var _ = require('lodash');
function renderItem1(item) {
return React.createElement('div', {}, item);
}
module.exports = function () {
return React.createElement(MyComp, {
'data': [
1,
2,
3
],
'renderItem': renderItem1.bind(this)
});
};
```
###### Compiled (with ES6 flag):
```javascript
import React from 'react/addons';
import _ from 'lodash';
function renderItem1(item) {
return React.createElement('div', {}, item);
}
export default function () {
return React.createElement(MyComp, {
'data': [
1,
2,
3
],
'renderItem': renderItem1.bind(this)
});
};
```
## Contributing
See the [Contributing page](CONTRIBUTING.md).

View File

@ -53,6 +53,7 @@ var classSetAttr = 'rt-class';
var classAttr = 'class';
var scopeAttr = 'rt-scope';
var propsAttr = 'rt-props';
var templateNode = 'rt-template';
var defaultOptions = {modules: 'amd', version: false, force: false, format: 'stylish', targetVersion: '0.13.1', reactImportPath: 'react/addons', lodashImportPath: 'lodash'};
@ -179,6 +180,55 @@ function generateInjectedFunc(context, namePrefix, body, params) {
return generatedFuncName;
}
function generateTemplateProps(node, context) {
var propTemplateDefinition = context.options.templates && context.options.templates[node.name];
var propertiesTemplates = _(node.children)
.map(function (child, index) {
var templateProp = null;
if (child.name === templateNode) { // Generic explicit template tag
if (!_.has(child.attribs, 'prop')) {
throw RTCodeError.build('rt-template must have a prop attribute', context, child);
}
var childTemplate = _.find(context.options.templates, {prop: child.attribs.prop}) || {arguments: []};
templateProp = {
prop: child.attribs.prop,
arguments: (child.attribs.arguments ? child.attribs.arguments.split(',') : childTemplate.arguments) || []
};
} else if (propTemplateDefinition && propTemplateDefinition[child.name]) { // Implicit child template from configuration
templateProp = {
prop: propTemplateDefinition[child.name].prop,
arguments: child.attribs.arguments ? child.attribs.arguments.split(',') : propTemplateDefinition[child.name].arguments
};
}
if (templateProp) {
_.assign(templateProp, {childIndex: index, content: _.find(child.children, {type: 'tag'})});
}
return templateProp;
})
.compact()
.value();
return _.transform(propertiesTemplates, function (props, templateProp) {
var functionParams = _.values(context.boundParams).concat(templateProp.arguments);
var oldBoundParams = context.boundParams;
context.boundParams = context.boundParams.concat(templateProp.arguments);
var functionBody = 'return ' + convertHtmlToReact(templateProp.content, context);
context.boundParams = oldBoundParams;
var generatedFuncName = generateInjectedFunc(context, templateProp.prop, functionBody, functionParams);
var boundArguments = _.values(context.boundParams).join(',');
props[templateProp.prop] = generatedFuncName + '.bind(this' + (boundArguments.length ? ', ' + boundArguments : '') + ')';
// Remove the template child from the children definition.
node.children.splice(templateProp.childIndex, 1);
}, {});
}
/**
* @param node
* @param {Context} context
@ -237,6 +287,8 @@ function generateProps(node, context) {
}
});
_.assign(props, generateTemplateProps(node, context));
return '{' + _.map(props, function (val, key) {
return JSON.stringify(key) + ' : ' + val;
}).join(',') + '}';
@ -304,7 +356,7 @@ function convertHtmlToReact(node, context) {
_.each(context.boundParams, function (boundParam) {
data.outerScopeMapping[boundParam] = boundParam;
});
// these are variables declared in the rt-scope attribute
data.innerScopeMapping = {};
_.each(node.attribs[scopeAttr].split(';'), function (scopePart) {
@ -457,7 +509,7 @@ function convertTemplateToReact(html, options) {
function convertRT(html, reportContext, options) {
var rootNode = cheerio.load(html, {lowerCaseTags: false, lowerCaseAttributeNames: false, xmlMode: true, withStartIndices: true});
options = _.defaults({}, options, defaultOptions);
var defaultDefines = {};
defaultDefines[options.reactImportPath] = 'React';
defaultDefines[options.lodashImportPath] = '_';
@ -490,15 +542,23 @@ function convertRT(html, reportContext, options) {
throw RTCodeError.build('Document should have a single root element', context, rootNode.root()[0]);
}
var body = convertHtmlToReact(firstTag, context);
var requirePaths = _(defines).keys().map(function (reqName) { return '"' + reqName + '"'; }).value().join(',');
var requirePaths = _(defines).keys().map(function (reqName) {
return '"' + reqName + '"';
}).value().join(',');
var requireVars = _(defines).values().value().join(',');
var vars;
if (options.modules === 'typescript') {
vars = _(defines).map(function (reqVar, reqPath) { return 'import ' + reqVar + " = require('" + reqPath + "');"; }).join('\n');
vars = _(defines).map(function (reqVar, reqPath) {
return 'import ' + reqVar + " = require('" + reqPath + "');";
}).join('\n');
} else if (options.modules === 'es6') {
vars = _(defines).map(function (reqVar, reqPath) { return 'import ' + reqVar + " from '" + reqPath + "';"; }).join('\n');
vars = _(defines).map(function (reqVar, reqPath) {
return 'import ' + reqVar + " from '" + reqPath + "';";
}).join('\n');
} else {
vars = _(defines).map(function (reqVar, reqPath) { return 'var ' + reqVar + " = require('" + reqPath + "');"; }).join('\n');
vars = _(defines).map(function (reqVar, reqPath) {
return 'var ' + reqVar + " = require('" + reqPath + "');";
}).join('\n');
}
var data = {body: body, injectedFunctions: '', requireNames: requireVars, requirePaths: requirePaths, vars: vars, name: options.name};
data.injectedFunctions = context.injectedFunctions.join('\n');

View File

@ -0,0 +1,7 @@
<div>
<List data="{[1,2,3]}">
<Row>
<div>{rowData}</div>
</Row>
</List>
</div>

View File

@ -0,0 +1,19 @@
define([
'react/addons',
'lodash'
], function (React, _) {
'use strict';
function renderRow1(rowData) {
return React.createElement('div', {}, rowData);
}
return function () {
return React.createElement('div', {}, React.createElement(List, {
'data': [
1,
2,
3
],
'renderRow': renderRow1.bind(this)
}));
};
});

View File

@ -0,0 +1,5 @@
<div>
<rt-template prop="templateProp" arguments="arg1">
<div>{arg1}</div>
</rt-template>
</div>

View File

@ -0,0 +1,12 @@
define([
'react/addons',
'lodash'
], function (React, _) {
'use strict';
function templateProp1(arg1) {
return React.createElement('div', {}, arg1);
}
return function () {
return React.createElement('div', { 'templateProp': templateProp1.bind(this) });
};
});

View File

@ -0,0 +1,5 @@
<div rt-scope="'boten' as name">
<rt-template prop="templateProp" arguments="arg1">
<div>Name: {name} {arg1}</div>
</rt-template>
</div>

View File

@ -0,0 +1,16 @@
define([
'react/addons',
'lodash'
], function (React, _) {
'use strict';
function templateProp1(name, arg1) {
return React.createElement('div', {}, 'Name: ', name, ' ', arg1);
}
function scopeName2() {
var name = 'boten';
return React.createElement('div', { 'templateProp': templateProp1.bind(this, name) });
}
return function () {
return scopeName2.apply(this, []);
};
});

View File

@ -0,0 +1,10 @@
<div>
<rt-template prop="templateProp" arguments="arg1">
<div>
<rt-template prop="templateProp2" arguments="inner1, inner2">
<div>{arg1 + inner1 + inner2}</div>
</rt-template>
<div>{arg1}</div>
</div>
</rt-template>
</div>

View File

@ -0,0 +1,15 @@
define([
'react/addons',
'lodash'
], function (React, _) {
'use strict';
function templateProp21(arg1, inner1, inner2) {
return React.createElement('div', {}, arg1 + inner1 + inner2);
}
function templateProp2(arg1) {
return React.createElement('div', { 'templateProp2': templateProp21.bind(this, arg1) }, React.createElement('div', {}, arg1));
}
return function () {
return React.createElement('div', { 'templateProp': templateProp2.bind(this) });
};
});

View File

@ -114,6 +114,28 @@ test('conversion test', function (t) {
}
});
test('prop template conversion test', function (t) {
var options = {
templates: {
List: {
Row: {prop: 'renderRow', arguments: ['rowData']}
}
}
};
var files = ['propTemplates/simpleTemplate.rt', 'propTemplates/templateInScope.rt', 'propTemplates/implicitTemplate.rt', 'propTemplates/twoTemplates.rt'];
t.plan(files.length);
files.forEach(check);
function check(testFile) {
var filename = path.join(dataPath, testFile);
var html = readFileNormalized(filename);
var expected = readFileNormalized(filename + '.js');
var actual = reactTemplates.convertTemplateToReact(html, options).replace(/\r/g, '').trim();
compareAndWrite(t, actual, expected, filename);
}
});
/**
* @param {*} t
* @param {string} actual