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, +};