diff --git a/packages/dx-react-core/src/index.js b/packages/dx-react-core/src/index.js
index 6f6012ee8b..56069d6a3b 100644
--- a/packages/dx-react-core/src/index.js
+++ b/packages/dx-react-core/src/index.js
@@ -5,8 +5,10 @@ export { Getter } from './plugged/getter';
export { Watcher } from './plugged/watcher';
export { Template } from './plugged/template';
export { TemplatePlaceholder } from './plugged/template-placeholder';
-export { combineTemplates } from './utils/templateHelpers';
export { DragDropContext } from './drag-drop/context';
export { DragSource } from './drag-drop/source';
export { DropTarget } from './drag-drop/target';
+
+export { TemplateRenderer } from './template-component';
+export { combineTemplates } from './utils/templateHelpers';
diff --git a/packages/dx-react-core/src/template-component.jsx b/packages/dx-react-core/src/template-component.jsx
new file mode 100644
index 0000000000..86b81bc3c1
--- /dev/null
+++ b/packages/dx-react-core/src/template-component.jsx
@@ -0,0 +1,2 @@
+export const TemplateRenderer = ({ template, children, ...restProps }) =>
+ template({ ...restProps, children });
diff --git a/packages/dx-react-core/src/template-component.test.jsx b/packages/dx-react-core/src/template-component.test.jsx
new file mode 100644
index 0000000000..d318ceb087
--- /dev/null
+++ b/packages/dx-react-core/src/template-component.test.jsx
@@ -0,0 +1,20 @@
+import React from 'react';
+import { mount } from 'enzyme';
+
+import { TemplateRenderer } from './template-component';
+
+describe('TemplateRenderer', () => {
+ it('should work', () => {
+ const tree = mount(
+ {children}
}
+ test={'test'}
+ >
+
+ ,
+ );
+
+ expect(tree.find('.test > .content').exists())
+ .toBe(true);
+ });
+});
diff --git a/packages/dx-react-grid-bootstrap3/src/templates/table.jsx b/packages/dx-react-grid-bootstrap3/src/templates/table.jsx
index 1966084f6c..a301d0df74 100644
--- a/packages/dx-react-grid-bootstrap3/src/templates/table.jsx
+++ b/packages/dx-react-grid-bootstrap3/src/templates/table.jsx
@@ -8,9 +8,10 @@ import {
const MINIMAL_COLUMN_WIDTH = 120;
/* eslint-disable react/prop-types */
-const tableTemplate = ({ children, ...restProps }) => (
+const tableTemplate = ({ children, tableRef, ...restProps }) => (
{children}
diff --git a/packages/dx-react-grid-material-ui/src/templates/table.jsx b/packages/dx-react-grid-material-ui/src/templates/table.jsx
index 8ccff0efe4..d47b5c921d 100644
--- a/packages/dx-react-grid-material-ui/src/templates/table.jsx
+++ b/packages/dx-react-grid-material-ui/src/templates/table.jsx
@@ -15,8 +15,8 @@ import {
const MINIMAL_COLUMN_WIDTH = 120;
/* eslint-disable react/prop-types */
-const tableTemplate = ({ children, ...restProps }) => (
- {children}
+const tableTemplate = ({ children, tableRef, ...restProps }) => (
+ {children}
);
const headTemplate = ({ children, ...restProps }) => (
{children}
diff --git a/packages/dx-react-grid/src/components/table-layout.jsx b/packages/dx-react-grid/src/components/table-layout.jsx
index 5f44380bf0..3934ebf4fa 100644
--- a/packages/dx-react-grid/src/components/table-layout.jsx
+++ b/packages/dx-react-grid/src/components/table-layout.jsx
@@ -6,13 +6,11 @@ import { findDOMNode } from 'react-dom';
import {
DropTarget,
+ TemplateRenderer,
} from '@devexpress/dx-react-core';
import {
- tableRowKeyGetter,
tableColumnKeyGetter,
- getTableCellInfo,
- findTableCellTarget,
getTableColumnGeometries,
getTableTargetColumnIndex,
getAnimations,
@@ -20,76 +18,9 @@ import {
evalAnimations,
} from '@devexpress/dx-grid-core';
-const FLEX_TYPE = 'flex';
-
-const getColumnStyle = ({ column, animationState = {} }) => ({
- width: column.width !== undefined ? `${column.width}px` : undefined,
- ...animationState,
-});
-
-const getRowStyle = ({ row }) => ({
- height: row.height !== undefined ? `${row.height}px` : undefined,
-});
-
-const renderRowCells = ({ row, columns, cellTemplate, animationState }) =>
- columns
- .filter((column, columnIndex) => !getTableCellInfo({ row, columns, columnIndex }).skip)
- .map((column, columnIndex) => {
- const key = tableColumnKeyGetter(column, columnIndex);
- const colspan = getTableCellInfo({ row, columns, columnIndex }).colspan;
- return React.cloneElement(
- cellTemplate({
- row,
- column,
- ...colspan ? { colspan } : null,
- style: getColumnStyle({ column, animationState: animationState.get(key) }),
- }),
- { key },
- );
- });
-
-const renderRows = ({
- rows,
- getRowId,
- columns,
- rowTemplate,
- cellTemplate,
- animationState,
-}) =>
- rows
- .map((row, rowIndex) => React.cloneElement(
- rowTemplate({
- row,
- style: getRowStyle({ row }),
- children: renderRowCells({ row, columns, cellTemplate, animationState }),
- }),
- { key: tableRowKeyGetter(getRowId, row, rowIndex) },
- ));
+import { RowsBlockLayout } from './table-layout/rows-block-layout';
-const renderRowsBlock = ({
- rows,
- getRowId,
- columns,
- blockTemplate,
- rowTemplate,
- cellTemplate,
- onClick,
- animationState,
-}) => blockTemplate({
- onClick: (e) => {
- const { rowIndex, columnIndex } = findTableCellTarget(e);
- if (rowIndex === -1 || columnIndex === -1) return;
- onClick({ e, row: rows[rowIndex], column: columns[columnIndex] });
- },
- children: renderRows({
- rows,
- getRowId,
- columns,
- rowTemplate,
- cellTemplate,
- animationState,
- }),
-});
+const FLEX_TYPE = 'flex';
export class TableLayout extends React.PureComponent {
constructor(props) {
@@ -229,41 +160,46 @@ export class TableLayout extends React.PureComponent {
.map(column => column.width || (column.type === FLEX_TYPE ? 0 : minColumnWidth))
.reduce((accum, width) => accum + width, 0);
- const table = tableTemplate({
- style: {
- tableLayout: 'fixed',
- minWidth: `${minWidth}px`,
- },
- ref: (node) => { if (node) this.tableNode = node; },
- children: [
- !headerRows.length ? null : React.cloneElement(
- renderRowsBlock({
- rows: headerRows,
- getRowId,
- columns,
- blockTemplate: headTemplate,
- rowTemplate,
- cellTemplate,
- onClick,
- animationState,
- }),
- { key: 'head' },
- ),
- React.cloneElement(
- renderRowsBlock({
- rows,
- getRowId,
- columns,
- blockTemplate: bodyTemplate,
- rowTemplate,
- cellTemplate,
- onClick,
- animationState,
- }),
- { key: 'body' },
- ),
- ],
- });
+ const table = (
+ { if (node) this.tableNode = node; }}
+ >
+ {[
+ ...(!headerRows.length
+ ? []
+ : [(
+
+ )]
+ ),
+ ,
+ ]}
+
+ );
return (
null;
const tableTemplateMock = ({ children, ...props }) => (
{children}
@@ -92,7 +92,7 @@ describe('TableLayout', () => {
/>,
);
- testTablePart({ tree: tree.find('table > tbody'), rows, columns });
+ testTablePart({ tree: tree.find('table tbody'), rows, columns });
});
it('should render table with headerRows and columns', () => {
@@ -112,7 +112,7 @@ describe('TableLayout', () => {
/>,
);
- testTablePart({ tree: tree.find('table > thead'), rows, columns });
+ testTablePart({ tree: tree.find('table thead'), rows, columns });
});
it('should span columns if specified', () => {
@@ -138,7 +138,7 @@ describe('TableLayout', () => {
rowColumn = rowWrappers.at(1).find('td');
expect(rowColumn.length).toBe(2);
- expect(rowColumn.at(0).children(PropsContainer).props().colspan).toBe(undefined);
+ expect(rowColumn.at(0).children(PropsContainer).props()).not.toHaveProperty('colspan');
expect(rowColumn.at(1).children(PropsContainer).props().colspan).toBe(3);
});
@@ -229,24 +229,6 @@ describe('TableLayout', () => {
.toMatchObject({ row: rows[1], column: columns[1], e: {} });
});
- it('should not pass the "colspan" parameter to the cellTemplate if it is undefined', () => {
- const cellTemplate = jest.fn().mockImplementation(cellTemplateMock);
- mount(
- row.id}
- />,
- );
-
- expect(cellTemplate.mock.calls[0][0])
- .not.toHaveProperty('colspan');
- });
-
describe('flex column', () => {
it('should add flex column if all columns have fixed widths', () => {
const rows = [{ id: 1 }, { id: 2 }];
diff --git a/packages/dx-react-grid/src/components/table-layout/row-layout.jsx b/packages/dx-react-grid/src/components/table-layout/row-layout.jsx
new file mode 100644
index 0000000000..7dc7a800ee
--- /dev/null
+++ b/packages/dx-react-grid/src/components/table-layout/row-layout.jsx
@@ -0,0 +1,67 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import {
+ TemplateRenderer,
+} from '@devexpress/dx-react-core';
+
+import {
+ tableColumnKeyGetter,
+ getTableCellInfo,
+} from '@devexpress/dx-grid-core';
+
+const getColumnStyle = ({ column, animationState = {} }) => ({
+ width: column.width !== undefined ? `${column.width}px` : undefined,
+ ...animationState,
+});
+
+const getRowStyle = ({ row }) => ({
+ height: row.height !== undefined ? `${row.height}px` : undefined,
+});
+
+export class RowLayout extends React.PureComponent {
+ render() {
+ const {
+ row,
+ columns,
+ rowTemplate,
+ cellTemplate,
+ animationState,
+ } = this.props;
+
+ return (
+
+ {
+ columns
+ .filter((column, columnIndex) => !getTableCellInfo({ row, columns, columnIndex }).skip)
+ .map((column, columnIndex) => {
+ const key = tableColumnKeyGetter(column, columnIndex);
+ const colspan = getTableCellInfo({ row, columns, columnIndex }).colspan;
+ return (
+
+ );
+ })
+ }
+
+ );
+ }
+}
+
+RowLayout.propTypes = {
+ row: PropTypes.object.isRequired,
+ columns: PropTypes.array.isRequired,
+ rowTemplate: PropTypes.func.isRequired,
+ cellTemplate: PropTypes.func.isRequired,
+ animationState: PropTypes.instanceOf(Map).isRequired,
+};
diff --git a/packages/dx-react-grid/src/components/table-layout/rows-block-layout.jsx b/packages/dx-react-grid/src/components/table-layout/rows-block-layout.jsx
new file mode 100644
index 0000000000..500760d155
--- /dev/null
+++ b/packages/dx-react-grid/src/components/table-layout/rows-block-layout.jsx
@@ -0,0 +1,64 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import {
+ TemplateRenderer,
+} from '@devexpress/dx-react-core';
+
+import {
+ tableRowKeyGetter,
+ findTableCellTarget,
+} from '@devexpress/dx-grid-core';
+
+import { RowLayout } from './row-layout';
+
+export class RowsBlockLayout extends React.PureComponent {
+ render() {
+ const {
+ rows,
+ getRowId,
+ columns,
+ blockTemplate,
+ rowTemplate,
+ cellTemplate,
+ onClick,
+ animationState,
+ } = this.props;
+
+ return (
+ {
+ const { rowIndex, columnIndex } = findTableCellTarget(e);
+ if (rowIndex === -1 || columnIndex === -1) return;
+ onClick({ e, row: rows[rowIndex], column: columns[columnIndex] });
+ }}
+ >
+ {
+ rows
+ .map((row, rowIndex) => (
+
+ ))
+ }
+
+ );
+ }
+}
+
+RowsBlockLayout.propTypes = {
+ rows: PropTypes.array.isRequired,
+ getRowId: PropTypes.func.isRequired,
+ columns: PropTypes.array.isRequired,
+ blockTemplate: PropTypes.func.isRequired,
+ rowTemplate: PropTypes.func.isRequired,
+ cellTemplate: PropTypes.func.isRequired,
+ onClick: PropTypes.func.isRequired,
+ animationState: PropTypes.instanceOf(Map).isRequired,
+};