Skip to content

Commit b66ec99

Browse files
committed
Allow overriding table elements
1 parent 3e5ad89 commit b66ec99

File tree

10 files changed

+497
-25
lines changed

10 files changed

+497
-25
lines changed

examples/custom-component.html

Whitespace-only changes.

examples/custom-component.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom';
3+
import Table from 'rc-table';
4+
import 'rc-table/assets/index.less';
5+
6+
const columns = [
7+
{ title: 'title1', dataIndex: 'a', key: 'a', width: 100 },
8+
{ id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 },
9+
{ title: 'title3', dataIndex: 'c', key: 'c', width: 200 },
10+
{
11+
title: 'Operations',
12+
dataIndex: '',
13+
key: 'd',
14+
render() {
15+
return <a href="#">Operations</a>;
16+
},
17+
},
18+
];
19+
20+
const data = [
21+
{ a: '123', key: '1' },
22+
{ a: 'cdd', b: 'edd', key: '2' },
23+
{ a: '1333', c: 'eee', d: 2, key: '3' },
24+
];
25+
26+
const MyTable = props => <table name="my-table" {...props} />;
27+
const HeaderWrapper = props => <thead name="my-header-wrapper" {...props} />;
28+
const HeaderRow = props => <tr name="my-header-row" {...props} />;
29+
const HeaderCell = props => <th name="my-header-cell" {...props} />;
30+
const BodyWrapper = props => <tbody name="my-body-wrapper" {...props} />;
31+
const BodyRow = props => <tr name="my-body-row" {...props} />;
32+
const BodyCell = props => <td name="my-body-cell" {...props} />;
33+
34+
const components = {
35+
table: MyTable,
36+
header: {
37+
wrapper: HeaderWrapper,
38+
row: HeaderRow,
39+
cell: HeaderCell,
40+
},
41+
body: {
42+
wrapper: BodyWrapper,
43+
row: BodyRow,
44+
cell: BodyCell,
45+
},
46+
};
47+
48+
ReactDOM.render(
49+
<div>
50+
<h2>Custom Component</h2>
51+
<Table columns={columns} data={data} components={components} />
52+
</div>,
53+
document.getElementById('__react-content')
54+
);

src/BaseTable.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class BaseTable extends React.Component {
4444
onRowContextMenu,
4545
onRowMouseEnter,
4646
onRowMouseLeave,
47+
components,
4748
} = table.props;
4849
const { getRowKey, fixed, expander } = this.props;
4950

@@ -103,6 +104,7 @@ class BaseTable extends React.Component {
103104
rowKey={key}
104105
ancestorKeys={ancestorKeys}
105106
ref={rowRef(record, i, indent)}
107+
components={components}
106108
{...expandableRow}
107109
/>
108110
)}
@@ -129,7 +131,7 @@ class BaseTable extends React.Component {
129131
}
130132

131133
render() {
132-
const { prefixCls, scroll, data, getBodyWrapper } = this.context.table.props;
134+
const { prefixCls, scroll, data, getBodyWrapper, components } = this.context.table.props;
133135
const { expander, tableClassName, hasHead, hasBody, fixed, columns } = this.props;
134136
const tableStyle = {};
135137

@@ -142,16 +144,20 @@ class BaseTable extends React.Component {
142144
}
143145
}
144146

147+
const Table = hasBody ? (components.table || 'table') : 'table';
148+
const BodyWrapper = components.body && components.body.wrapper || 'tbody';
149+
150+
145151
return (
146-
<table className={tableClassName} style={tableStyle} key="table">
152+
<Table className={tableClassName} style={tableStyle} key="table">
147153
<ColGroup columns={columns} fixed={fixed} />
148154
{hasHead && <TableHeader expander={expander} columns={columns} fixed={fixed} /> }
149155
{hasBody && getBodyWrapper(
150-
<tbody className={`${prefixCls}-tbody`}>
156+
<BodyWrapper className={`${prefixCls}-tbody`}>
151157
{this.renderRows(data, 0)}
152-
</tbody>
158+
</BodyWrapper>
153159
)}
154-
</table>
160+
</Table>
155161
);
156162
}
157163
}

src/Table.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,19 @@ export default class Table extends React.Component {
3333
rowRef: PropTypes.func,
3434
getBodyWrapper: PropTypes.func,
3535
children: PropTypes.node,
36+
components: PropTypes.shape({
37+
table: PropTypes.any,
38+
header: PropTypes.shape({
39+
wrapper: PropTypes.any,
40+
row: PropTypes.any,
41+
cell: PropTypes.any,
42+
}),
43+
body: PropTypes.shape({
44+
wrapper: PropTypes.any,
45+
row: PropTypes.any,
46+
cell: PropTypes.any,
47+
}),
48+
}),
3649
...ExpandableTable.PropTypes,
3750
}
3851

@@ -58,6 +71,7 @@ export default class Table extends React.Component {
5871
rowRef: () => null,
5972
getBodyWrapper: body => body,
6073
emptyText: () => 'No Data',
74+
components: {},
6175
}
6276

6377
constructor(props) {

src/TableCell.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export default class TableCell extends React.Component {
1111
indentSize: PropTypes.number,
1212
column: PropTypes.object,
1313
expandIcon: PropTypes.node,
14+
component: PropTypes.any,
1415
}
1516

1617
isInvalidRenderCellText(text) {
@@ -26,8 +27,16 @@ export default class TableCell extends React.Component {
2627
}
2728

2829
render() {
29-
const { record, indentSize, prefixCls, indent,
30-
index, expandIcon, column } = this.props;
30+
const {
31+
record,
32+
indentSize,
33+
prefixCls,
34+
indent,
35+
index,
36+
expandIcon,
37+
column,
38+
component: BodyCell,
39+
} = this.props;
3140
const { dataIndex, render, className = '' } = column;
3241

3342
// We should return undefined if no dataIndex is specified, but in order to
@@ -70,15 +79,15 @@ export default class TableCell extends React.Component {
7079
return null;
7180
}
7281
return (
73-
<td
82+
<BodyCell
7483
className={className}
7584
{...tdProps}
7685
onClick={this.handleClick}
7786
>
7887
{indentText}
7988
{expandIcon}
8089
{text}
81-
</td>
90+
</BodyCell>
8291
);
8392
}
8493
}

src/TableHeader.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ function getHeaderRows(columns, currentRow = 0, rows) {
3434
}
3535

3636
export default function TableHeader(props, { table }) {
37-
const { prefixCls, showHeader } = table.props;
37+
const { prefixCls, showHeader, components } = table.props;
3838
const { expander, columns, fixed } = props;
3939

4040
if (!showHeader) {
@@ -45,8 +45,10 @@ export default function TableHeader(props, { table }) {
4545

4646
expander.renderExpandIndentCell(rows, fixed);
4747

48+
const HeaderWrapper = components.header && components.header.wrapper || 'thead';
49+
4850
return (
49-
<thead className={`${prefixCls}-thead`}>
51+
<HeaderWrapper className={`${prefixCls}-thead`}>
5052
{
5153
rows.map((row, index) => (
5254
<TableHeaderRow
@@ -55,10 +57,11 @@ export default function TableHeader(props, { table }) {
5557
columns={columns}
5658
rows={rows}
5759
row={row}
60+
components={components}
5861
/>
5962
))
6063
}
61-
</thead>
64+
</HeaderWrapper>
6265
);
6366
}
6467

src/TableHeaderRow.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
import React from 'react';
22
import { connect } from 'mini-store';
33

4-
function TableHeaderRow({ row, height }) {
4+
function TableHeaderRow({ row, height, components }) {
55
const style = { height };
66

7+
let HeaderRow = 'tr';
8+
let HeaderCell = 'th';
9+
10+
if (components.header) {
11+
HeaderRow = components.header.row || HeaderRow;
12+
HeaderCell = components.header.cell || HeaderCell;
13+
}
14+
715
return (
8-
<tr style={style}>
9-
{row.map((cell, i) => <th {...cell} key={i} />)}
10-
</tr>
16+
<HeaderRow style={style}>
17+
{row.map((cell, i) => <HeaderCell {...cell} key={i} />)}
18+
</HeaderRow>
1119
);
1220
}
1321

src/TableRow.js

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React from 'react';
2+
import ReactDOM from 'react-dom';
23
import PropTypes from 'prop-types';
34
import { connect } from 'mini-store';
45
import TableCell from './TableCell';
@@ -36,6 +37,7 @@ class TableRow extends React.Component {
3637
]),
3738
renderExpandIcon: PropTypes.func,
3839
renderExpandIconCell: PropTypes.func,
40+
components: PropTypes.any,
3941
}
4042

4143
static defaultProps = {
@@ -58,12 +60,24 @@ class TableRow extends React.Component {
5860
this.shouldRender = props.visible;
5961
}
6062

63+
componentDidMount() {
64+
if (this.shouldRender) {
65+
this.saveRowRef();
66+
}
67+
}
68+
6169
componentWillReceiveProps(nextProps) {
6270
if (this.props.visible || (!this.props.visible && nextProps.visible)) {
6371
this.shouldRender = true;
6472
}
6573
}
6674

75+
componentDidUpdate() {
76+
if (this.shouldRender && !this.rowRef) {
77+
this.saveRowRef();
78+
}
79+
}
80+
6781
onRowClick = (event) => {
6882
const { record, index, onRowClick } = this.props;
6983
onRowClick(record, index, event);
@@ -99,12 +113,11 @@ class TableRow extends React.Component {
99113
store.setState({ expandedRowsHeight });
100114
}
101115

102-
saveRowRef = (node) => {
103-
this.rowRef = node;
104-
if (node) {
105-
if (!this.props.fixed) {
106-
this.setHeight();
107-
}
116+
saveRowRef() {
117+
this.rowRef = ReactDOM.findDOMNode(this);
118+
119+
if (!this.props.fixed) {
120+
this.setHeight();
108121
}
109122
}
110123

@@ -123,11 +136,20 @@ class TableRow extends React.Component {
123136
visible,
124137
height,
125138
hovered,
139+
components,
126140
hasExpandIcon,
127141
renderExpandIcon,
128142
renderExpandIconCell,
129143
} = this.props;
130144

145+
let BodyRow = 'tr';
146+
let BodyCell = 'td';
147+
148+
if (components && components.body) {
149+
BodyRow = components.body.row || BodyRow;
150+
BodyCell = components.body.cell || BodyCell;
151+
}
152+
131153
let { className } = this.props;
132154

133155
if (hovered) {
@@ -149,6 +171,7 @@ class TableRow extends React.Component {
149171
column={columns[i]}
150172
key={columns[i].key || columns[i].dataIndex}
151173
expandIcon={hasExpandIcon(i) && renderExpandIcon()}
174+
component={BodyCell}
152175
/>
153176
);
154177
}
@@ -163,8 +186,7 @@ class TableRow extends React.Component {
163186
`${prefixCls} ${className} ${prefixCls}-level-${indent}`.trim();
164187

165188
return (
166-
<tr
167-
ref={this.saveRowRef}
189+
<BodyRow
168190
onClick={this.onRowClick}
169191
onDoubleClick={this.onRowDoubleClick}
170192
onMouseEnter={this.onMouseEnter}
@@ -174,7 +196,7 @@ class TableRow extends React.Component {
174196
style={style}
175197
>
176198
{cells}
177-
</tr>
199+
</BodyRow>
178200
);
179201
}
180202
}

tests/Table.spec.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,4 +440,53 @@ describe('Table', () => {
440440
}));
441441
expect(wrapper).toMatchSnapshot();
442442
});
443+
444+
describe('custom components', () => {
445+
const MyTable = (props) => <table name="my-table" {...props} />;
446+
const HeaderWrapper = (props) => <thead name="my-header-wrapper" {...props} />;
447+
const HeaderRow = (props) => <tr name="my-header-row" {...props} />;
448+
const HeaderCell = (props) => <th name="my-header-cell" {...props} />;
449+
const BodyWrapper = (props) => <tbody name="my-body-wrapper" {...props} />;
450+
const BodyRow = (props) => <tr name="my-body-row" {...props} />;
451+
const BodyCell = (props) => <td name="my-body-cell" {...props} />;
452+
453+
const components = {
454+
table: MyTable,
455+
header: {
456+
wrapper: HeaderWrapper,
457+
row: HeaderRow,
458+
cell: HeaderCell,
459+
},
460+
body: {
461+
wrapper: BodyWrapper,
462+
row: BodyRow,
463+
cell: BodyCell,
464+
},
465+
};
466+
467+
it('renders correctly', () => {
468+
const wrapper = render(createTable({ components }));
469+
470+
expect(wrapper).toMatchSnapshot();
471+
});
472+
473+
it('renders fixed column and header correctly', () => {
474+
const columns = [
475+
{ title: 'Name', dataIndex: 'name', key: 'name', fixed: 'left' },
476+
{ title: 'Age', dataIndex: 'age', key: 'age' },
477+
{ title: 'Gender', dataIndex: 'gender', key: 'gender', fixed: 'right' },
478+
];
479+
const sampleData = [
480+
{ key: 0, name: 'Lucy', age: 27, gender: 'F' },
481+
];
482+
const wrapper = render(createTable({
483+
columns,
484+
data: sampleData,
485+
components,
486+
scroll: { y: 100 },
487+
}));
488+
489+
expect(wrapper).toMatchSnapshot();
490+
});
491+
});
443492
});

0 commit comments

Comments
 (0)