From 71b81bfec753876f17fc2eca976255248eae553c Mon Sep 17 00:00:00 2001 From: Thomas Shafer Date: Wed, 12 Aug 2015 19:34:14 -0700 Subject: [PATCH 1/5] Added failing test for rendering sub components --- build/tests/reactable_test.js | 68 +++++++++++++++++++++++++++++++++++ tests/reactable_test.jsx | 57 +++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) diff --git a/build/tests/reactable_test.js b/build/tests/reactable_test.js index 6bf0bfb9..41a7fead 100644 --- a/build/tests/reactable_test.js +++ b/build/tests/reactable_test.js @@ -610,6 +610,74 @@ }); }); + describe('adding s to the ', function () { + before(function () { + var CustomComponent = React.createClass({ + displayName: "CustomComponent", + propTypes: { + name: React.PropTypes.string, + age: React.PropTypes.number, + position: React.PropTypes.string + }, + render: function render() { + return React.createElement( + Reactable.Tr, + null, + React.createElement( + Reactable.Td, + { column: 'Name' }, + this.props.name + ), + React.createElement( + Reactable.Td, + { column: 'Age' }, + this.props.age + ), + React.createElement( + Reactable.Td, + { column: 'Position' }, + this.props.position + ) + ); + } + }); + React.render(React.createElement( + Reactable.Table, + { className: 'table', id: 'table' }, + React.createElement(CustomComponent, { name: 'Griffin Smith', age: 18 }), + React.createElement(CustomComponent, { name: 'Lee Salminen', age: 23 }), + React.createElement(CustomComponent, { age: 28, position: 'Developer' }) + ), ReactableTestUtils.testNode()); + }); + + after(ReactableTestUtils.resetTestEnvironment); + + it('renders the table', function () { + expect($('table#table.table')).to.exist; + }); + + it('renders the column headers in the table', function () { + var headers = []; + $('thead th').each(function () { + headers.push($(this).text()); + }); + + expect(headers).to.eql(['Name', 'Age', 'Position']); + }); + + it('renders the first row with the correct data', function () { + ReactableTestUtils.expectRowText(0, ['Griffin Smith', '18', '']); + }); + + it('renders the second row with the correct data', function () { + ReactableTestUtils.expectRowText(1, ['Lee Salminen', '23', '']); + }); + + it('renders the third row with the correct data', function () { + ReactableTestUtils.expectRowText(2, ['', '28', 'Developer']); + }); + }); + describe('passing through HTML props', function () { describe('adding s with className to the
', function () { before(function () { diff --git a/tests/reactable_test.jsx b/tests/reactable_test.jsx index b2232dc6..d9b5fe19 100644 --- a/tests/reactable_test.jsx +++ b/tests/reactable_test.jsx @@ -417,6 +417,63 @@ describe('Reactable', function() { }); }); + describe('adding s to the
', function() { + before(function() { + var CustomComponent = React.createClass({ + displayName: "CustomComponent", + propTypes:{ + name: React.PropTypes.string, + age: React.PropTypes.number, + position: React.PropTypes.string + }, + render: function(){ + return ( + + {this.props.name} + {this.props.age} + {this.props.position} + + ); + } + }); + React.render( + + + + + , + ReactableTestUtils.testNode() + ); + }); + + after(ReactableTestUtils.resetTestEnvironment); + + it('renders the table', function() { + expect($('table#table.table')).to.exist; + }); + + it('renders the column headers in the table', function() { + var headers = []; + $('thead th').each(function() { + headers.push($(this).text()); + }); + + expect(headers).to.eql([ 'Name', 'Age', 'Position' ]); + }); + + it('renders the first row with the correct data', function() { + ReactableTestUtils.expectRowText(0, ['Griffin Smith', '18', '']); + }); + + it('renders the second row with the correct data', function() { + ReactableTestUtils.expectRowText(1, ['Lee Salminen', '23', '']); + }); + + it('renders the third row with the correct data', function() { + ReactableTestUtils.expectRowText(2, ['', '28', 'Developer']); + }); + }); + describe('passing through HTML props', function() { describe('adding s with className to the
', function() { before(function() { From 570cfd1da215228c6a814d1283e533981cbc76a2 Mon Sep 17 00:00:00 2001 From: Thomas Shafer Date: Thu, 13 Aug 2015 11:39:59 -0700 Subject: [PATCH 2/5] prototype solution to custom components Since react will not render child components until the parent component is rendered (exploratory hypotheis) We need to render the custom component first and then access the component. This uses a Component function of getData(). The getData function could/should be fast because after the render function all the data could be available via props or state I have not tested this with updating data from the custom component. Or if the custom component internally changes the data and they table needs to resort. --- build/reactable.js | 54 +++++++++++++++++++++++++++++++++-- build/tests/reactable_test.js | 7 +++++ lib/reactable/table.js | 54 +++++++++++++++++++++++++++++++++-- src/reactable/table.jsx | 48 ++++++++++++++++++++++++++++--- tests/reactable_test.jsx | 7 +++++ 5 files changed, 162 insertions(+), 8 deletions(-) diff --git a/build/reactable.js b/build/reactable.js index 463dc9cc..e49713f4 100644 --- a/build/reactable.js +++ b/build/reactable.js @@ -991,6 +991,7 @@ window.React["default"] = window.React; _get(Object.getPrototypeOf(Table.prototype), 'constructor', this).call(this, props); this.state = { + parsedCustomComponents: false, currentPage: 0, currentSort: { column: null, @@ -1037,7 +1038,8 @@ window.React["default"] = window.React; key: 'parseChildData', value: function parseChildData(props) { var data = [], - tfoot = undefined; + tfoot = undefined, + customComponentsCount = 0; // Transform any children back to a data array if (typeof props.children !== 'undefined') { @@ -1089,11 +1091,17 @@ window.React["default"] = window.React; __reactableMeta: true }); break; + default: + // Don't know if there are other acceptable types + // that should be dismissed + // console.log("Table, got custom component", child.type) + customComponentsCount++; + break; } }).bind(this)); } - return { data: data, tfoot: tfoot }; + return { data: data, tfoot: tfoot, customComponentsCount: customComponentsCount }; } }, { key: 'initialize', @@ -1104,9 +1112,11 @@ window.React["default"] = window.React; var data = _parseChildData.data; var tfoot = _parseChildData.tfoot; + var customComponentsCount = _parseChildData.customComponentsCount; this.data = this.data.concat(data); this.tfoot = tfoot; + this.customComponentsCount = customComponentsCount; this.initializeSorts(props); } @@ -1191,6 +1201,27 @@ window.React["default"] = window.React; this.initialize(this.props); this.sortByCurrentSort(); } + }, { + key: 'componentDidMount', + value: function componentDidMount() { + for (var i = 0; i < this.customComponentsCount; i++) { + var child = this.refs['child-' + i], + childData = child.getData(), + childDataToPush = {}; + for (var key in childData) { + childDataToPush[key] = { + value: childData[key], + __reactableMeta: true + }; + } + this.data.push({ + data: childDataToPush, + props: (0, _libFilter_props_from.filterPropsFrom)(child.props), + __reactableMeta: true + }); + }; + this.setState({ parsedCustomComponents: true }); + } }, { key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { @@ -1281,11 +1312,30 @@ window.React["default"] = window.React; this.setState({ currentSort: currentSort }); this.sortByCurrentSort(); } + }, { + key: 'renderUnparsedDataTable', + value: function renderUnparsedDataTable() { + // http://www.mattzabriskie.com/blog/react-referencing-dynamic-children + var index = 0; + var children = _react['default'].Children.map(this.props.children, function (child) { + return _react['default'].addons.cloneWithProps(child, { ref: 'child-' + index++ }); + }); + + return _react['default'].createElement( + 'div', + null, + children + ); + } }, { key: 'render', value: function render() { var _this = this; + if (!this.state.parsedCustomComponents && this.customComponentsCount > 0) { + return this.renderUnparsedDataTable(); + } + var children = []; var columns = undefined; var userColumnsSpecified = false; diff --git a/build/tests/reactable_test.js b/build/tests/reactable_test.js index 41a7fead..dcf41817 100644 --- a/build/tests/reactable_test.js +++ b/build/tests/reactable_test.js @@ -619,6 +619,13 @@ age: React.PropTypes.number, position: React.PropTypes.string }, + getData: function getData() { + return { + Name: this.props.name, + Age: this.props.age, + Position: this.props.position + }; + }, render: function render() { return React.createElement( Reactable.Tr, diff --git a/lib/reactable/table.js b/lib/reactable/table.js index 24f036c5..1a8e2cd2 100644 --- a/lib/reactable/table.js +++ b/lib/reactable/table.js @@ -45,6 +45,7 @@ var Table = (function (_React$Component) { _get(Object.getPrototypeOf(Table.prototype), 'constructor', this).call(this, props); this.state = { + parsedCustomComponents: false, currentPage: 0, currentSort: { column: null, @@ -91,7 +92,8 @@ var Table = (function (_React$Component) { key: 'parseChildData', value: function parseChildData(props) { var data = [], - tfoot = undefined; + tfoot = undefined, + customComponentsCount = 0; // Transform any children back to a data array if (typeof props.children !== 'undefined') { @@ -143,11 +145,17 @@ var Table = (function (_React$Component) { __reactableMeta: true }); break; + default: + // Don't know if there are other acceptable types + // that should be dismissed + // console.log("Table, got custom component", child.type) + customComponentsCount++; + break; } }).bind(this)); } - return { data: data, tfoot: tfoot }; + return { data: data, tfoot: tfoot, customComponentsCount: customComponentsCount }; } }, { key: 'initialize', @@ -158,9 +166,11 @@ var Table = (function (_React$Component) { var data = _parseChildData.data; var tfoot = _parseChildData.tfoot; + var customComponentsCount = _parseChildData.customComponentsCount; this.data = this.data.concat(data); this.tfoot = tfoot; + this.customComponentsCount = customComponentsCount; this.initializeSorts(props); } @@ -245,6 +255,27 @@ var Table = (function (_React$Component) { this.initialize(this.props); this.sortByCurrentSort(); } + }, { + key: 'componentDidMount', + value: function componentDidMount() { + for (var i = 0; i < this.customComponentsCount; i++) { + var child = this.refs['child-' + i], + childData = child.getData(), + childDataToPush = {}; + for (var key in childData) { + childDataToPush[key] = { + value: childData[key], + __reactableMeta: true + }; + } + this.data.push({ + data: childDataToPush, + props: (0, _libFilter_props_from.filterPropsFrom)(child.props), + __reactableMeta: true + }); + }; + this.setState({ parsedCustomComponents: true }); + } }, { key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { @@ -335,11 +366,30 @@ var Table = (function (_React$Component) { this.setState({ currentSort: currentSort }); this.sortByCurrentSort(); } + }, { + key: 'renderUnparsedDataTable', + value: function renderUnparsedDataTable() { + // http://www.mattzabriskie.com/blog/react-referencing-dynamic-children + var index = 0; + var children = _react2['default'].Children.map(this.props.children, function (child) { + return _react2['default'].addons.cloneWithProps(child, { ref: 'child-' + index++ }); + }); + + return _react2['default'].createElement( + 'div', + null, + children + ); + } }, { key: 'render', value: function render() { var _this = this; + if (!this.state.parsedCustomComponents && this.customComponentsCount > 0) { + return this.renderUnparsedDataTable(); + } + var children = []; var columns = undefined; var userColumnsSpecified = false; diff --git a/src/reactable/table.jsx b/src/reactable/table.jsx index e69f1bd3..74e12ea0 100644 --- a/src/reactable/table.jsx +++ b/src/reactable/table.jsx @@ -13,6 +13,7 @@ export class Table extends React.Component { super(props); this.state = { + parsedCustomComponents: false, currentPage: 0, currentSort: { column: null, @@ -53,7 +54,7 @@ export class Table extends React.Component { } parseChildData(props) { - let data = [], tfoot; + let data = [], tfoot, customComponentsCount = 0; // Transform any children back to a data array if (typeof(props.children) !== 'undefined') { @@ -112,19 +113,26 @@ export class Table extends React.Component { __reactableMeta: true }); break; + default: + // Don't know if there are other acceptable types + // that should be dismissed + // console.log("Table, got custom component", child.type) + customComponentsCount++; + break; } }.bind(this)); } - return { data, tfoot }; + return { data, tfoot, customComponentsCount }; } initialize(props) { this.data = props.data || []; - let { data, tfoot } = this.parseChildData(props); + let { data, tfoot, customComponentsCount } = this.parseChildData(props); this.data = this.data.concat(data); this.tfoot = tfoot; + this.customComponentsCount = customComponentsCount; this.initializeSorts(props); } @@ -206,6 +214,26 @@ export class Table extends React.Component { this.sortByCurrentSort(); } + componentDidMount() { + for (var i = 0; i < this.customComponentsCount; i++) { + let child = this.refs['child-'+i], + childData = child.getData(), + childDataToPush = {}; + for (var key in childData){ + childDataToPush[key] = { + value: childData[key], + __reactableMeta: true + }; + } + this.data.push({ + data: childDataToPush, + props: filterPropsFrom(child.props), + __reactableMeta: true + }); + }; + this.setState({parsedCustomComponents: true}); + } + componentWillReceiveProps(nextProps) { this.initialize(nextProps); this.updateCurrentSort(nextProps.sortBy); @@ -297,8 +325,20 @@ export class Table extends React.Component { this.setState({ currentSort: currentSort }); this.sortByCurrentSort(); } - + renderUnparsedDataTable() { + // http://www.mattzabriskie.com/blog/react-referencing-dynamic-children + let index = 0; + let children = React.Children.map(this.props.children, function (child) { + return React.addons.cloneWithProps(child, {ref: 'child-' + (index++) }); + }); + + return
{children}
; + } render() { + if (!this.state.parsedCustomComponents && this.customComponentsCount > 0){ + return this.renderUnparsedDataTable(); + } + let children = []; let columns; let userColumnsSpecified = false; diff --git a/tests/reactable_test.jsx b/tests/reactable_test.jsx index d9b5fe19..f22da064 100644 --- a/tests/reactable_test.jsx +++ b/tests/reactable_test.jsx @@ -426,6 +426,13 @@ describe('Reactable', function() { age: React.PropTypes.number, position: React.PropTypes.string }, + getData: function(){ + return { + Name: this.props.name, + Age: this.props.age, + Position: this.props.position, + } + }, render: function(){ return ( From 3bd8e5aa602c8a5bbd4e61c29444cccd566c7527 Mon Sep 17 00:00:00 2001 From: Griffin Smith Date: Wed, 30 Sep 2015 00:45:19 -0400 Subject: [PATCH 3/5] Revert "prototype solution to custom components" This reverts commit 1fc822d30d3f5bd0be4d80826b345cc1dbf5739b. --- build/reactable.js | 54 ++--------------------------------- build/tests/reactable_test.js | 7 ----- lib/reactable/table.js | 54 ++--------------------------------- src/reactable/table.jsx | 48 +++---------------------------- tests/reactable_test.jsx | 7 ----- 5 files changed, 8 insertions(+), 162 deletions(-) diff --git a/build/reactable.js b/build/reactable.js index e49713f4..463dc9cc 100644 --- a/build/reactable.js +++ b/build/reactable.js @@ -991,7 +991,6 @@ window.React["default"] = window.React; _get(Object.getPrototypeOf(Table.prototype), 'constructor', this).call(this, props); this.state = { - parsedCustomComponents: false, currentPage: 0, currentSort: { column: null, @@ -1038,8 +1037,7 @@ window.React["default"] = window.React; key: 'parseChildData', value: function parseChildData(props) { var data = [], - tfoot = undefined, - customComponentsCount = 0; + tfoot = undefined; // Transform any children back to a data array if (typeof props.children !== 'undefined') { @@ -1091,17 +1089,11 @@ window.React["default"] = window.React; __reactableMeta: true }); break; - default: - // Don't know if there are other acceptable types - // that should be dismissed - // console.log("Table, got custom component", child.type) - customComponentsCount++; - break; } }).bind(this)); } - return { data: data, tfoot: tfoot, customComponentsCount: customComponentsCount }; + return { data: data, tfoot: tfoot }; } }, { key: 'initialize', @@ -1112,11 +1104,9 @@ window.React["default"] = window.React; var data = _parseChildData.data; var tfoot = _parseChildData.tfoot; - var customComponentsCount = _parseChildData.customComponentsCount; this.data = this.data.concat(data); this.tfoot = tfoot; - this.customComponentsCount = customComponentsCount; this.initializeSorts(props); } @@ -1201,27 +1191,6 @@ window.React["default"] = window.React; this.initialize(this.props); this.sortByCurrentSort(); } - }, { - key: 'componentDidMount', - value: function componentDidMount() { - for (var i = 0; i < this.customComponentsCount; i++) { - var child = this.refs['child-' + i], - childData = child.getData(), - childDataToPush = {}; - for (var key in childData) { - childDataToPush[key] = { - value: childData[key], - __reactableMeta: true - }; - } - this.data.push({ - data: childDataToPush, - props: (0, _libFilter_props_from.filterPropsFrom)(child.props), - __reactableMeta: true - }); - }; - this.setState({ parsedCustomComponents: true }); - } }, { key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { @@ -1312,30 +1281,11 @@ window.React["default"] = window.React; this.setState({ currentSort: currentSort }); this.sortByCurrentSort(); } - }, { - key: 'renderUnparsedDataTable', - value: function renderUnparsedDataTable() { - // http://www.mattzabriskie.com/blog/react-referencing-dynamic-children - var index = 0; - var children = _react['default'].Children.map(this.props.children, function (child) { - return _react['default'].addons.cloneWithProps(child, { ref: 'child-' + index++ }); - }); - - return _react['default'].createElement( - 'div', - null, - children - ); - } }, { key: 'render', value: function render() { var _this = this; - if (!this.state.parsedCustomComponents && this.customComponentsCount > 0) { - return this.renderUnparsedDataTable(); - } - var children = []; var columns = undefined; var userColumnsSpecified = false; diff --git a/build/tests/reactable_test.js b/build/tests/reactable_test.js index dcf41817..41a7fead 100644 --- a/build/tests/reactable_test.js +++ b/build/tests/reactable_test.js @@ -619,13 +619,6 @@ age: React.PropTypes.number, position: React.PropTypes.string }, - getData: function getData() { - return { - Name: this.props.name, - Age: this.props.age, - Position: this.props.position - }; - }, render: function render() { return React.createElement( Reactable.Tr, diff --git a/lib/reactable/table.js b/lib/reactable/table.js index 1a8e2cd2..24f036c5 100644 --- a/lib/reactable/table.js +++ b/lib/reactable/table.js @@ -45,7 +45,6 @@ var Table = (function (_React$Component) { _get(Object.getPrototypeOf(Table.prototype), 'constructor', this).call(this, props); this.state = { - parsedCustomComponents: false, currentPage: 0, currentSort: { column: null, @@ -92,8 +91,7 @@ var Table = (function (_React$Component) { key: 'parseChildData', value: function parseChildData(props) { var data = [], - tfoot = undefined, - customComponentsCount = 0; + tfoot = undefined; // Transform any children back to a data array if (typeof props.children !== 'undefined') { @@ -145,17 +143,11 @@ var Table = (function (_React$Component) { __reactableMeta: true }); break; - default: - // Don't know if there are other acceptable types - // that should be dismissed - // console.log("Table, got custom component", child.type) - customComponentsCount++; - break; } }).bind(this)); } - return { data: data, tfoot: tfoot, customComponentsCount: customComponentsCount }; + return { data: data, tfoot: tfoot }; } }, { key: 'initialize', @@ -166,11 +158,9 @@ var Table = (function (_React$Component) { var data = _parseChildData.data; var tfoot = _parseChildData.tfoot; - var customComponentsCount = _parseChildData.customComponentsCount; this.data = this.data.concat(data); this.tfoot = tfoot; - this.customComponentsCount = customComponentsCount; this.initializeSorts(props); } @@ -255,27 +245,6 @@ var Table = (function (_React$Component) { this.initialize(this.props); this.sortByCurrentSort(); } - }, { - key: 'componentDidMount', - value: function componentDidMount() { - for (var i = 0; i < this.customComponentsCount; i++) { - var child = this.refs['child-' + i], - childData = child.getData(), - childDataToPush = {}; - for (var key in childData) { - childDataToPush[key] = { - value: childData[key], - __reactableMeta: true - }; - } - this.data.push({ - data: childDataToPush, - props: (0, _libFilter_props_from.filterPropsFrom)(child.props), - __reactableMeta: true - }); - }; - this.setState({ parsedCustomComponents: true }); - } }, { key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { @@ -366,30 +335,11 @@ var Table = (function (_React$Component) { this.setState({ currentSort: currentSort }); this.sortByCurrentSort(); } - }, { - key: 'renderUnparsedDataTable', - value: function renderUnparsedDataTable() { - // http://www.mattzabriskie.com/blog/react-referencing-dynamic-children - var index = 0; - var children = _react2['default'].Children.map(this.props.children, function (child) { - return _react2['default'].addons.cloneWithProps(child, { ref: 'child-' + index++ }); - }); - - return _react2['default'].createElement( - 'div', - null, - children - ); - } }, { key: 'render', value: function render() { var _this = this; - if (!this.state.parsedCustomComponents && this.customComponentsCount > 0) { - return this.renderUnparsedDataTable(); - } - var children = []; var columns = undefined; var userColumnsSpecified = false; diff --git a/src/reactable/table.jsx b/src/reactable/table.jsx index 74e12ea0..e69f1bd3 100644 --- a/src/reactable/table.jsx +++ b/src/reactable/table.jsx @@ -13,7 +13,6 @@ export class Table extends React.Component { super(props); this.state = { - parsedCustomComponents: false, currentPage: 0, currentSort: { column: null, @@ -54,7 +53,7 @@ export class Table extends React.Component { } parseChildData(props) { - let data = [], tfoot, customComponentsCount = 0; + let data = [], tfoot; // Transform any children back to a data array if (typeof(props.children) !== 'undefined') { @@ -113,26 +112,19 @@ export class Table extends React.Component { __reactableMeta: true }); break; - default: - // Don't know if there are other acceptable types - // that should be dismissed - // console.log("Table, got custom component", child.type) - customComponentsCount++; - break; } }.bind(this)); } - return { data, tfoot, customComponentsCount }; + return { data, tfoot }; } initialize(props) { this.data = props.data || []; - let { data, tfoot, customComponentsCount } = this.parseChildData(props); + let { data, tfoot } = this.parseChildData(props); this.data = this.data.concat(data); this.tfoot = tfoot; - this.customComponentsCount = customComponentsCount; this.initializeSorts(props); } @@ -214,26 +206,6 @@ export class Table extends React.Component { this.sortByCurrentSort(); } - componentDidMount() { - for (var i = 0; i < this.customComponentsCount; i++) { - let child = this.refs['child-'+i], - childData = child.getData(), - childDataToPush = {}; - for (var key in childData){ - childDataToPush[key] = { - value: childData[key], - __reactableMeta: true - }; - } - this.data.push({ - data: childDataToPush, - props: filterPropsFrom(child.props), - __reactableMeta: true - }); - }; - this.setState({parsedCustomComponents: true}); - } - componentWillReceiveProps(nextProps) { this.initialize(nextProps); this.updateCurrentSort(nextProps.sortBy); @@ -325,20 +297,8 @@ export class Table extends React.Component { this.setState({ currentSort: currentSort }); this.sortByCurrentSort(); } - renderUnparsedDataTable() { - // http://www.mattzabriskie.com/blog/react-referencing-dynamic-children - let index = 0; - let children = React.Children.map(this.props.children, function (child) { - return React.addons.cloneWithProps(child, {ref: 'child-' + (index++) }); - }); - - return
{children}
; - } - render() { - if (!this.state.parsedCustomComponents && this.customComponentsCount > 0){ - return this.renderUnparsedDataTable(); - } + render() { let children = []; let columns; let userColumnsSpecified = false; diff --git a/tests/reactable_test.jsx b/tests/reactable_test.jsx index f22da064..d9b5fe19 100644 --- a/tests/reactable_test.jsx +++ b/tests/reactable_test.jsx @@ -426,13 +426,6 @@ describe('Reactable', function() { age: React.PropTypes.number, position: React.PropTypes.string }, - getData: function(){ - return { - Name: this.props.name, - Age: this.props.age, - Position: this.props.position, - } - }, render: function(){ return ( From 7a937cac277ecb672fa0c71197dda7cc6ebe34d6 Mon Sep 17 00:00:00 2001 From: Griffin Smith Date: Wed, 30 Sep 2015 01:02:06 -0400 Subject: [PATCH 4/5] Allow specifying table rows in custom components Use the structure returned by `react.createElement` to do a render of any child components with unrecognized types, and extract the data out of them if they themselves return a `
` --- build/reactable.js | 20 +++++++++++++++----- build/tests/reactable_test.js | 7 ++++--- lib/reactable/table.js | 20 +++++++++++++++----- src/reactable/table.jsx | 20 +++++++++++++++----- tests/reactable_test.jsx | 7 ++++--- 5 files changed, 53 insertions(+), 21 deletions(-) diff --git a/build/reactable.js b/build/reactable.js index 463dc9cc..e15568bd 100644 --- a/build/reactable.js +++ b/build/reactable.js @@ -1046,7 +1046,17 @@ window.React["default"] = window.React; return; } - switch (child.type) { + var reactableDescendant = undefined; + var test = undefined; + + if ([_tfoot.Tfoot, _thead.Thead, _tr.Tr].indexOf(child.type) >= 0) { + reactableDescendant = child; + } else { + reactableDescendant = new child.type(child.props).render(); + test = true; + } + + switch (reactableDescendant.type) { case _tfoot.Tfoot: if (typeof tfoot !== 'undefined') { console.warn('You can only have one , but more than one was specified.' + 'Ignoring all but the last one'); @@ -1054,9 +1064,9 @@ window.React["default"] = window.React; tfoot = child; break; case _tr.Tr: - var childData = child.props.data || {}; + var childData = reactableDescendant.props.data || {}; - _react['default'].Children.forEach(child.props.children, function (descendant) { + _react['default'].Children.forEach(reactableDescendant.props.children, function (descendant) { // TODO /* if (descendant.type.ConvenienceConstructor === Td) { */ if (typeof descendant !== 'object' || descendant == null) { @@ -1069,7 +1079,7 @@ window.React["default"] = window.React; } else if (typeof descendant.props.children !== 'undefined') { value = descendant.props.children; } else { - console.warn('exports.Td specified without ' + 'a `data` property or children, ' + 'ignoring'); + console.warn('Td specified without ' + 'a `data` property or children, ' + 'ignoring'); return; } @@ -1085,7 +1095,7 @@ window.React["default"] = window.React; data.push({ data: childData, - props: (0, _libFilter_props_from.filterPropsFrom)(child.props), + props: (0, _libFilter_props_from.filterPropsFrom)(reactableDescendant.props), __reactableMeta: true }); break; diff --git a/build/tests/reactable_test.js b/build/tests/reactable_test.js index 41a7fead..7e2c3770 100644 --- a/build/tests/reactable_test.js +++ b/build/tests/reactable_test.js @@ -626,21 +626,22 @@ React.createElement( Reactable.Td, { column: 'Name' }, - this.props.name + this.props.name || '' ), React.createElement( Reactable.Td, { column: 'Age' }, - this.props.age + this.props.age || '' ), React.createElement( Reactable.Td, { column: 'Position' }, - this.props.position + this.props.position || '' ) ); } }); + React.render(React.createElement( Reactable.Table, { className: 'table', id: 'table' }, diff --git a/lib/reactable/table.js b/lib/reactable/table.js index 24f036c5..df8bd805 100644 --- a/lib/reactable/table.js +++ b/lib/reactable/table.js @@ -100,7 +100,17 @@ var Table = (function (_React$Component) { return; } - switch (child.type) { + var reactableDescendant = undefined; + var test = undefined; + + if ([_tfoot.Tfoot, _thead.Thead, _tr.Tr].indexOf(child.type) >= 0) { + reactableDescendant = child; + } else { + reactableDescendant = new child.type(child.props).render(); + test = true; + } + + switch (reactableDescendant.type) { case _tfoot.Tfoot: if (typeof tfoot !== 'undefined') { console.warn('You can only have one , but more than one was specified.' + 'Ignoring all but the last one'); @@ -108,9 +118,9 @@ var Table = (function (_React$Component) { tfoot = child; break; case _tr.Tr: - var childData = child.props.data || {}; + var childData = reactableDescendant.props.data || {}; - _react2['default'].Children.forEach(child.props.children, function (descendant) { + _react2['default'].Children.forEach(reactableDescendant.props.children, function (descendant) { // TODO /* if (descendant.type.ConvenienceConstructor === Td) { */ if (typeof descendant !== 'object' || descendant == null) { @@ -123,7 +133,7 @@ var Table = (function (_React$Component) { } else if (typeof descendant.props.children !== 'undefined') { value = descendant.props.children; } else { - console.warn('exports.Td specified without ' + 'a `data` property or children, ' + 'ignoring'); + console.warn('Td specified without ' + 'a `data` property or children, ' + 'ignoring'); return; } @@ -139,7 +149,7 @@ var Table = (function (_React$Component) { data.push({ data: childData, - props: (0, _libFilter_props_from.filterPropsFrom)(child.props), + props: (0, _libFilter_props_from.filterPropsFrom)(reactableDescendant.props), __reactableMeta: true }); break; diff --git a/src/reactable/table.jsx b/src/reactable/table.jsx index e69f1bd3..68f1f2c8 100644 --- a/src/reactable/table.jsx +++ b/src/reactable/table.jsx @@ -62,7 +62,17 @@ export class Table extends React.Component { return; } - switch (child.type) { + let reactableDescendant; + let test; + + if ([Tfoot, Thead, Tr].indexOf(child.type) >= 0) { + reactableDescendant = child + } else { + reactableDescendant = (new child.type(child.props)).render() + test = true + } + + switch (reactableDescendant.type) { case Tfoot: if (typeof(tfoot) !== 'undefined') { console.warn ('You can only have one , but more than one was specified.' + @@ -71,9 +81,9 @@ export class Table extends React.Component { tfoot = child; break; case Tr: - let childData = child.props.data || {}; + let childData = reactableDescendant.props.data || {}; - React.Children.forEach(child.props.children, function(descendant) { + React.Children.forEach(reactableDescendant.props.children, function(descendant) { // TODO /* if (descendant.type.ConvenienceConstructor === Td) { */ if ( @@ -89,7 +99,7 @@ export class Table extends React.Component { } else if (typeof(descendant.props.children) !== 'undefined') { value = descendant.props.children; } else { - console.warn('exports.Td specified without ' + + console.warn('Td specified without ' + 'a `data` property or children, ' + 'ignoring'); return; @@ -108,7 +118,7 @@ export class Table extends React.Component { data.push({ data: childData, - props: filterPropsFrom(child.props), + props: filterPropsFrom(reactableDescendant.props), __reactableMeta: true }); break; diff --git a/tests/reactable_test.jsx b/tests/reactable_test.jsx index d9b5fe19..4887766e 100644 --- a/tests/reactable_test.jsx +++ b/tests/reactable_test.jsx @@ -429,13 +429,14 @@ describe('Reactable', function() { render: function(){ return ( - {this.props.name} - {this.props.age} - {this.props.position} + {this.props.name || ''} + {this.props.age || ''} + {this.props.position || ''} ); } }); + React.render( From 3ca5bb7370597b253fbda2c927fc6fdc13691638 Mon Sep 17 00:00:00 2001 From: Griffin Smith Date: Thu, 1 Oct 2015 21:41:27 -0400 Subject: [PATCH 5/5] Handle passing through context to child components When instantiating child components in order to render them, also pass through the props given when creating the child component. --- build/reactable.js | 2 +- build/tests/reactable_test.js | 167 +++++++++++++++++++++++----------- lib/reactable/table.js | 2 +- src/reactable/table.jsx | 2 +- tests/reactable_test.jsx | 137 +++++++++++++++++++--------- 5 files changed, 210 insertions(+), 100 deletions(-) diff --git a/build/reactable.js b/build/reactable.js index e15568bd..c298e1d4 100644 --- a/build/reactable.js +++ b/build/reactable.js @@ -1052,7 +1052,7 @@ window.React["default"] = window.React; if ([_tfoot.Tfoot, _thead.Thead, _tr.Tr].indexOf(child.type) >= 0) { reactableDescendant = child; } else { - reactableDescendant = new child.type(child.props).render(); + reactableDescendant = new child.type(child.props, child._context).render(); test = true; } diff --git a/build/tests/reactable_test.js b/build/tests/reactable_test.js index 7e2c3770..b9aa180b 100644 --- a/build/tests/reactable_test.js +++ b/build/tests/reactable_test.js @@ -611,71 +611,130 @@ }); describe('adding s to the
', function () { - before(function () { - var CustomComponent = React.createClass({ - displayName: "CustomComponent", - propTypes: { - name: React.PropTypes.string, - age: React.PropTypes.number, - position: React.PropTypes.string - }, - render: function render() { - return React.createElement( - Reactable.Tr, - null, - React.createElement( - Reactable.Td, - { column: 'Name' }, - this.props.name || '' - ), - React.createElement( - Reactable.Td, - { column: 'Age' }, - this.props.age || '' - ), - React.createElement( - Reactable.Td, - { column: 'Position' }, - this.props.position || '' - ) - ); - } + context('passing through props', function () { + before(function () { + var CustomComponent = React.createClass({ + displayName: "CustomComponent", + propTypes: { + name: React.PropTypes.string, + age: React.PropTypes.number, + position: React.PropTypes.string + }, + render: function render() { + return React.createElement( + Reactable.Tr, + null, + React.createElement( + Reactable.Td, + { column: 'Name' }, + this.props.name || '' + ), + React.createElement( + Reactable.Td, + { column: 'Age' }, + this.props.age || '' + ), + React.createElement( + Reactable.Td, + { column: 'Position' }, + this.props.position || '' + ) + ); + } + }); + + React.render(React.createElement( + Reactable.Table, + { className: 'table', id: 'table' }, + React.createElement(CustomComponent, { name: 'Griffin Smith', age: 18 }), + React.createElement(CustomComponent, { name: 'Lee Salminen', age: 23 }), + React.createElement(CustomComponent, { age: 28, position: 'Developer' }) + ), ReactableTestUtils.testNode()); }); - React.render(React.createElement( - Reactable.Table, - { className: 'table', id: 'table' }, - React.createElement(CustomComponent, { name: 'Griffin Smith', age: 18 }), - React.createElement(CustomComponent, { name: 'Lee Salminen', age: 23 }), - React.createElement(CustomComponent, { age: 28, position: 'Developer' }) - ), ReactableTestUtils.testNode()); - }); + after(ReactableTestUtils.resetTestEnvironment); - after(ReactableTestUtils.resetTestEnvironment); + it('renders the table', function () { + expect($('table#table.table')).to.exist; + }); - it('renders the table', function () { - expect($('table#table.table')).to.exist; - }); + it('renders the column headers in the table', function () { + var headers = []; + $('thead th').each(function () { + headers.push($(this).text()); + }); - it('renders the column headers in the table', function () { - var headers = []; - $('thead th').each(function () { - headers.push($(this).text()); + expect(headers).to.eql(['Name', 'Age', 'Position']); }); - expect(headers).to.eql(['Name', 'Age', 'Position']); - }); + it('renders the first row with the correct data', function () { + ReactableTestUtils.expectRowText(0, ['Griffin Smith', '18', '']); + }); - it('renders the first row with the correct data', function () { - ReactableTestUtils.expectRowText(0, ['Griffin Smith', '18', '']); - }); + it('renders the second row with the correct data', function () { + ReactableTestUtils.expectRowText(1, ['Lee Salminen', '23', '']); + }); - it('renders the second row with the correct data', function () { - ReactableTestUtils.expectRowText(1, ['Lee Salminen', '23', '']); + it('renders the third row with the correct data', function () { + ReactableTestUtils.expectRowText(2, ['', '28', 'Developer']); + }); }); - it('renders the third row with the correct data', function () { - ReactableTestUtils.expectRowText(2, ['', '28', 'Developer']); + context('passing through context', function () { + before(function () { + var RowComponent = React.createClass({ + displayName: 'CustomComponent', + contextTypes: { test: React.PropTypes.string }, + render: function render() { + return React.createElement( + Reactable.Tr, + null, + React.createElement( + Reactable.Td, + { column: 'Name' }, + this.props.name || '' + ), + React.createElement( + Reactable.Td, + { column: 'Test' }, + this.context.test || '' + ) + ); + } + }); + + var TableComponent = React.createClass({ + displayName: 'TableComponent', + childContextTypes: { test: React.PropTypes.string }, + getChildContext: function getChildContext() { + return { test: 'foobar' }; + }, + render: function render() { + return React.createElement( + Reactable.Table, + { className: 'table', id: 'table' }, + React.createElement(RowComponent, { name: 'Griffin Smith' }), + React.createElement(RowComponent, { name: 'Lee Salminen' }) + ); + } + }); + + React.render(React.createElement(TableComponent, null), ReactableTestUtils.testNode()); + }); + + after(ReactableTestUtils.resetTestEnvironment); + + it('renders the table', function () { + expect($('table#table.table')).to.exist; + }); + + it('renders the first row with the correct data', function () { + ReactableTestUtils.expectRowText(0, ['Griffin Smith', 'foobar']); + }); + + it('renders the second row with the correct data', function () { + ReactableTestUtils.expectRowText(1, ['Lee Salminen', 'foobar']); + }); }); }); diff --git a/lib/reactable/table.js b/lib/reactable/table.js index df8bd805..6e07569e 100644 --- a/lib/reactable/table.js +++ b/lib/reactable/table.js @@ -106,7 +106,7 @@ var Table = (function (_React$Component) { if ([_tfoot.Tfoot, _thead.Thead, _tr.Tr].indexOf(child.type) >= 0) { reactableDescendant = child; } else { - reactableDescendant = new child.type(child.props).render(); + reactableDescendant = new child.type(child.props, child._context).render(); test = true; } diff --git a/src/reactable/table.jsx b/src/reactable/table.jsx index 68f1f2c8..eb67dd60 100644 --- a/src/reactable/table.jsx +++ b/src/reactable/table.jsx @@ -68,7 +68,7 @@ export class Table extends React.Component { if ([Tfoot, Thead, Tr].indexOf(child.type) >= 0) { reactableDescendant = child } else { - reactableDescendant = (new child.type(child.props)).render() + reactableDescendant = (new child.type(child.props, child._context)).render() test = true } diff --git a/tests/reactable_test.jsx b/tests/reactable_test.jsx index 4887766e..65ee8b86 100644 --- a/tests/reactable_test.jsx +++ b/tests/reactable_test.jsx @@ -418,60 +418,111 @@ describe('Reactable', function() { }); describe('adding s to the
', function() { - before(function() { - var CustomComponent = React.createClass({ - displayName: "CustomComponent", - propTypes:{ - name: React.PropTypes.string, - age: React.PropTypes.number, - position: React.PropTypes.string - }, - render: function(){ - return ( - - {this.props.name || ''} - {this.props.age || ''} - {this.props.position || ''} - - ); - } + context('passing through props', function() { + before(function() { + var CustomComponent = React.createClass({ + displayName: "CustomComponent", + propTypes:{ + name: React.PropTypes.string, + age: React.PropTypes.number, + position: React.PropTypes.string + }, + render: function(){ + return ( + + {this.props.name || ''} + {this.props.age || ''} + {this.props.position || ''} + + ); + } + }); + + React.render( + + + + + , + ReactableTestUtils.testNode() + ); }); - React.render( - - - - - , - ReactableTestUtils.testNode() - ); - }); + after(ReactableTestUtils.resetTestEnvironment); - after(ReactableTestUtils.resetTestEnvironment); + it('renders the table', function() { + expect($('table#table.table')).to.exist; + }); - it('renders the table', function() { - expect($('table#table.table')).to.exist; - }); + it('renders the column headers in the table', function() { + var headers = []; + $('thead th').each(function() { + headers.push($(this).text()); + }); - it('renders the column headers in the table', function() { - var headers = []; - $('thead th').each(function() { - headers.push($(this).text()); + expect(headers).to.eql([ 'Name', 'Age', 'Position' ]); }); - expect(headers).to.eql([ 'Name', 'Age', 'Position' ]); - }); + it('renders the first row with the correct data', function() { + ReactableTestUtils.expectRowText(0, ['Griffin Smith', '18', '']); + }); - it('renders the first row with the correct data', function() { - ReactableTestUtils.expectRowText(0, ['Griffin Smith', '18', '']); - }); + it('renders the second row with the correct data', function() { + ReactableTestUtils.expectRowText(1, ['Lee Salminen', '23', '']); + }); - it('renders the second row with the correct data', function() { - ReactableTestUtils.expectRowText(1, ['Lee Salminen', '23', '']); + it('renders the third row with the correct data', function() { + ReactableTestUtils.expectRowText(2, ['', '28', 'Developer']); + }); }); - it('renders the third row with the correct data', function() { - ReactableTestUtils.expectRowText(2, ['', '28', 'Developer']); + context('passing through context', function() { + before(function() { + let RowComponent = React.createClass({ + displayName: 'CustomComponent', + contextTypes: { test: React.PropTypes.string }, + render: function(){ + return ( + + {this.props.name || ''} + {this.context.test || ''} + + ); + } + }); + + let TableComponent = React.createClass({ + displayName: 'TableComponent', + childContextTypes: { test: React.PropTypes.string }, + getChildContext: function() { + return { test: 'foobar' }; + }, + render: function() { + return ( + + + + + ); + } + }); + + React.render(, ReactableTestUtils.testNode()); + }); + + after(ReactableTestUtils.resetTestEnvironment); + + it('renders the table', function() { + expect($('table#table.table')).to.exist; + }); + + it('renders the first row with the correct data', function() { + ReactableTestUtils.expectRowText(0, ['Griffin Smith', 'foobar']); + }); + + it('renders the second row with the correct data', function() { + ReactableTestUtils.expectRowText(1, ['Lee Salminen', 'foobar']); + }); }); });