I use expanded rows in fixed-data-table-2 component. I have 3 levels on inner tables:
If I click collapse cells in inner table (second nested level), rows don't expand and last nested table is not rendered. It occurs after first click of parent row, but after second click the table is rendered.
What is more strange, this behaviour doesn't happen if a) I click first three rows of second table or b) if I expand first row in main(first) table
It happens with last rows of second table if other than first row of main table is expanded.
You can see this behaviour in this and this recording.
codesandbox
CollapseCell.jsx
import React from 'react';
import { Cell } from 'fixed-data-table-2';
const { StyleSheet, css } = require('aphrodite');
export default class CollapseCell extends React.PureComponent {
render() {
const {
data, rowIndex, columnKey, collapsedRows, callback, ...props
} = this.props;
return (
<Cell {...props} className={css(styles.collapseCell)}>
<a onClick={evt => callback(rowIndex)}>{collapsedRows.has(rowIndex) ? '\u25BC' : '\u25BA'}</a>
</Cell>
);
}
}
const styles = StyleSheet.create({
collapseCell: {
cursor: 'pointer',
},
});
TestMeet.jsx
import React, { Component } from 'react';
import debounce from 'lodash/debounce';
import { Table, Column, Cell } from 'fixed-data-table-2';
import isEmpty from 'lodash/isEmpty';
import 'fixed-data-table-2/dist/fixed-data-table.min.css';
import CollapseCell from './CollapseCell.jsx';
import SecondInnerTable from './SecondInnerTable';
const { StyleSheet, css } = require('aphrodite');
export default class TestMeetView extends Component {
static propTypes = {};
state = {
tableData: [
{
start: '5/19',
end: '5/20',
host: 'DACA',
},
{
start: '6/15',
end: '6/15',
host: 'DACA',
},
{
start: '6/16',
end: '6/17',
host: 'DACA',
},
{
start: '7/15',
end: '7/16',
host: 'DACA',
},
{
start: '7/30',
end: '7/31',
host: 'DACA',
},
],
columnWidths: {
start: 200,
end: 200,
host: 200,
action: 100,
},
tableContainerWidth: 0,
numOfExpRows: 0,
expChildRows: {},
collapsedRows: new Set(),
scrollToRow: null,
};
componentDidMount() {
this.updateWidth();
this.updateWidth = debounce(this.updateWidth, 200);
window.addEventListener('resize', this.updateWidth);
}
componentWillUnmount() {
window.removeEventListener('resize', this.updateWidth);
}
onTableColumnResizeEndCallback = (newColumnWidth, columnKey) => {
this.setState(({ columnWidths }) => ({
columnWidths: {
...columnWidths,
[columnKey]: newColumnWidth,
},
}));
};
updateWidth = () => {
if (this.tableContainer.offsetWidth === this.state.tableContainerWidth) {
return;
}
if (
this.tableContainer &&
this.tableContainer.offsetWidth !== this.state.tableContainerWidth
) {
const newTableContainerWidth = this.tableContainer.offsetWidth;
this.setState({
tableContainerWidth: newTableContainerWidth,
columnWidths: {
start: newTableContainerWidth / 3,
end: newTableContainerWidth / 3,
host: newTableContainerWidth / 3,
},
});
}
};
handleCollapseClick = (rowIndex) => {
const { collapsedRows } = this.state;
const shallowCopyOfCollapsedRows = new Set([...collapsedRows]);
let scrollToRow = rowIndex;
if (shallowCopyOfCollapsedRows.has(rowIndex)) {
shallowCopyOfCollapsedRows.delete(rowIndex);
scrollToRow = null;
} else {
shallowCopyOfCollapsedRows.add(rowIndex);
}
let numOfExpRows = 0;
if (collapsedRows.size > 0) {
numOfExpRows = collapsedRows.size;
}
let resetExpChildRow = -1;
if (collapsedRows.has(rowIndex)) {
numOfExpRows -= 1;
resetExpChildRow = rowIndex;
} else {
numOfExpRows += 1;
}
if (resetExpChildRow === -1) {
this.setState({
numOfExpRows,
scrollToRow,
collapsedRows: shallowCopyOfCollapsedRows,
});
} else {
this.setState({
numOfExpRows,
scrollToRow,
collapsedRows: shallowCopyOfCollapsedRows,
expChildRows: {
...this.state.expChildRows,
[rowIndex]: 0,
},
});
}
};
subRowHeightGetter = (index) => {
const numExpChildRows = this.state.expChildRows[index] ? this.state.expChildRows[index] : 0;
return this.state.collapsedRows.has(index) ? 242 * (numExpChildRows + 1) + 50 : 0;
};
rowExpandedGetter = ({ rowIndex, width, height }) => {
if (!this.state.collapsedRows.has(rowIndex)) {
return null;
}
const style = {
height,
width: width - 10,
};
return (
<div style={style}>
<div className={css(styles.expandStyles)}>
<SecondInnerTable
changeNumOfExpandedRows={this.setNumOfInnerExpandedRows}
parentRowIndex={rowIndex}
/>
</div>
</div>
);
};
setNumOfInnerExpandedRows = (numOfExpandedRows, rowIndex) => {
this.setState({
expChildRows: {
...this.state.expChildRows,
[rowIndex]: numOfExpandedRows,
},
});
};
render() {
let sumOfExpChildRows = 0;
if (!isEmpty(this.state.expChildRows)) {
sumOfExpChildRows = Object.values(this.state.expChildRows).reduce((a, b) => a + b);
}
return (
<div className="test-view">
<div className="container-fluid">
<div className="mb-5" ref={el => (this.tableContainer = el)}>
<Table
scrollToRow={this.state.scrollToRow}
rowsCount={this.state.tableData.length}
rowHeight={40}
headerHeight={40}
width={this.state.tableContainerWidth}
height={(this.state.numOfExpRows + sumOfExpChildRows + 1) * 242}
subRowHeightGetter={this.subRowHeightGetter}
rowExpanded={this.rowExpandedGetter}
touchScrollEnabled
onColumnResizeEndCallback={this.onTableColumnResizeEndCallback}
isColumnResizing={false}
>
<Column
cell={<CollapseCell callback={this.handleCollapseClick} collapsedRows={this.state.collapsedRows} />}
fixed
width={30}
/>
<Column
columnKey="start"
header={<Cell>Start</Cell>}
cell={props => <Cell {...props}>{this.state.tableData[props.rowIndex].start}</Cell>}
width={this.state.columnWidths.start}
isResizable
/>
<Column
columnKey="end"
header={<Cell>End</Cell>}
cell={props => <Cell {...props}>{this.state.tableData[props.rowIndex].end}</Cell>}
width={this.state.columnWidths.end}
isResizable
/>
<Column
columnKey="host"
header={<Cell>Host</Cell>}
cell={props => <Cell {...props}>{this.state.tableData[props.rowIndex].host}</Cell>}
width={this.state.columnWidths.host}
isResizable
/>
</Table>
</div>
</div>
</div>
);
}
}
const styles = StyleSheet.create({
expandStyles: {
height: '242px',
margin: '10px',
},
});
SecondInnerTable.jsx
import React, { Component } from 'react';
import { Table, Column, Cell } from 'fixed-data-table-2';
import debounce from 'lodash/debounce';
import 'fixed-data-table-2/dist/fixed-data-table.min.css';
import CollapseCell from './CollapseCell';
import ThirdInnerTable from './ThirdInnerTable';
const { StyleSheet, css } = require('aphrodite');
export default class SecondInnerTable extends Component {
state = {
tableData: [
{
dateOfSession: '5/19/18',
timeline: '4h00m/4h30m',
entries: '400/900',
},
{
dateOfSession: '5/19/18',
timeline: '4h00m/4h30m',
entries: '400/900',
},
{
dateOfSession: '5/19/18',
timeline: '4h00m/4h30m',
entries: '400/900',
},
{
dateOfSession: '5/19/18',
timeline: '4h00m/4h30m',
entries: '400/900',
},
{
dateOfSession: '5/19/18',
timeline: '4h00m/4h30m',
entries: '400/900',
},
],
tableColumns: {
dateOfSession: { label: 'Date of Session', width: 150 },
timeline: { label: 'Timeline', width: 150 },
entries: { label: 'Entries', width: 150 },
},
tableContainerWidth: 0,
tableContainerHeight: 252,
collapsedRows: new Set(),
scrollToRow: null,
};
componentDidMount() {
this.updateWidth();
this.updateWidth = debounce(this.updateWidth, 200);
window.addEventListener('resize', this.updateWidth);
}
componentWillUnmount() {
window.removeEventListener('resize', this.updateWidth);
}
onSessionsTableColumnResizeEndCallback = (newColumnWidth, columnKey) => {
this.setState(({ tableColumns }) => ({
tableColumns: {
...tableColumns,
[columnKey]: { label: tableColumns[columnKey].label, width: newColumnWidth },
},
}));
};
updateWidth = () => {
if (this.tableContainer.offsetWidth === this.state.tableContainerWidth) {
return;
}
if (
this.tableContainer &&
this.tableContainer.offsetWidth !== this.state.tableContainerWidth
) {
const newTableContainerWidth = this.tableContainer.offsetWidth - 20;
const newColumnsWidth = newTableContainerWidth / 3;
this.setState(({ tableColumns }) => ({
tableContainerWidth: newTableContainerWidth,
tableColumns: {
dateOfSession: { label: tableColumns.dateOfSession.label, width: newColumnsWidth },
timeline: { label: tableColumns.timeline.label, width: newColumnsWidth },
entries: { label: tableColumns.entries.label, width: newColumnsWidth },
},
}));
}
};
handleCollapseClick = (rowIndex) => {
const { collapsedRows } = this.state;
const shallowCopyOfCollapsedRows = new Set([...collapsedRows]);
let scrollToRow = rowIndex;
if (shallowCopyOfCollapsedRows.has(rowIndex)) {
shallowCopyOfCollapsedRows.delete(rowIndex);
scrollToRow = null;
} else {
shallowCopyOfCollapsedRows.add(rowIndex);
}
let numOfExpRows = 0;
if (collapsedRows.size > 0) {
numOfExpRows = collapsedRows.size;
}
if (collapsedRows.has(rowIndex)) {
numOfExpRows -= 1;
} else {
numOfExpRows += 1;
}
this.setState(
{
tableContainerHeight: 252 * (numOfExpRows + 1),
scrollToRow,
collapsedRows: shallowCopyOfCollapsedRows,
},
() => {
this.props.changeNumOfExpandedRows(numOfExpRows, this.props.parentRowIndex);
},
);
};
subRowHeightGetter = index => (this.state.collapsedRows.has(index) ? 272 : 0);
rowExpandedGetter = ({ rowIndex, width, height }) => {
if (!this.state.collapsedRows.has(rowIndex)) {
return null;
}
const style = {
height,
width: width - 10,
};
return (
<div style={style}>
<div className={css(styles.expandStyles)}>
<ThirdInnerTable parentRowIndex={rowIndex} />
</div>
</div>
);
};
render() {
return (
<div className="mb-2" ref={el => (this.tableContainer = el)}>
<Table
scrollToRow={this.state.scrollToRow}
rowsCount={this.state.tableData.length}
rowHeight={40}
headerHeight={50}
width={this.state.tableContainerWidth}
height={this.state.tableContainerHeight}
subRowHeightGetter={this.subRowHeightGetter}
rowExpanded={this.rowExpandedGetter}
touchScrollEnabled
onColumnResizeEndCallback={this.onSessionsTableColumnResizeEndCallback}
isColumnResizing={false}
>
<Column
cell={<CollapseCell callback={this.handleCollapseClick} collapsedRows={this.state.collapsedRows} />}
fixed
width={30}
/>
{Object.keys(this.state.tableColumns).map(key => (
<Column
key={key}
columnKey={key}
header={<Cell>{this.state.tableColumns[key].label}</Cell>}
cell={props => <Cell {...props}>{this.state.tableData[props.rowIndex][key]}</Cell>}
width={this.state.tableColumns[key].width}
isResizable
/>
))}
</Table>
</div>
);
}
}
const styles = StyleSheet.create({
expandStyles: {
height: '252px',
margin: '10px',
},
});
ThirdInnerTable.jsx
import React, { Component } from 'react';
import debounce from 'lodash/debounce';
import { Table, Column, Cell } from 'fixed-data-table-2';
import 'fixed-data-table-2/dist/fixed-data-table.min.css';
export default class ThirdInnerTable extends Component {
state = {
tableData: [
{
eventNumber: '1',
qualifyingTime: 'N/A',
selected: 'N/A',
},
{
eventNumber: '1',
qualifyingTime: 'N/A',
selected: 'N/A',
},
{
eventNumber: '1',
qualifyingTime: 'N/A',
selected: 'N/A',
},
{
eventNumber: '1',
qualifyingTime: 'N/A',
selected: 'N/A',
},
{
eventNumber: '1',
qualifyingTime: 'N/A',
selected: 'N/A',
},
],
tableColumns: {
eventNumber: { label: 'Event number', width: 150 },
qualifyingTime: { label: 'Qualifying time', width: 150 },
selected: { label: 'Selected?', width: 150 },
},
tableContainerWidth: 0,
numOfColumns: 3,
};
componentDidMount() {
this.updateWidth();
this.updateWidth = debounce(this.updateWidth, 200);
window.addEventListener('resize', this.updateWidth);
}
componentWillUnmount() {
window.removeEventListener('resize', this.updateWidth);
}
onEventsTableColumnResizeEndCallback = (newColumnWidth, columnKey) => {
this.setState(({ tableColumns }) => ({
tableColumns: {
...tableColumns,
[columnKey]: { label: tableColumns[columnKey].label, width: newColumnWidth },
},
}));
};
updateWidth = () => {
if (this.tableContainer.offsetWidth === this.state.tableContainerWidth) {
return;
}
if (
this.tableContainer &&
this.tableContainer.offsetWidth !== this.state.tableContainerWidth
) {
const newTableContainerWidth = this.tableContainer.offsetWidth;
const columnWidth = newTableContainerWidth / 3;
this.setState(({ tableColumns }) => ({
tableContainerWidth: newTableContainerWidth,
tableColumns: {
eventNumber: { label: tableColumns.eventNumber.label, width: columnWidth },
qualifyingTime: { label: tableColumns.qualifyingTime.label, width: columnWidth },
selected: { label: tableColumns.selected.label, width: columnWidth },
},
}));
}
};
render() {
return (
<div className="mb-5" ref={el => (this.tableContainer = el)}>
<Table
rowsCount={this.state.tableData.length}
rowHeight={40}
headerHeight={50}
width={this.state.tableContainerWidth}
height={252}
touchScrollEnabled
onColumnResizeEndCallback={this.onEventsTableColumnResizeEndCallback}
isColumnResizing={false}
>
{Object.keys(this.state.tableColumns).slice(0, this.state.numOfColumns).map(key => (
<Column
key={key}
columnKey={key}
header={<Cell>{this.state.tableColumns[key].label}</Cell>}
cell={props => <Cell {...props}>{this.state.tableData[props.rowIndex][key]}</Cell>}
width={this.state.tableColumns[key].width}
isResizable
/>
))}
</Table>
</div>
);
}
}
I have updated the Sandbox code, Kindly check the below link, I think it is useful for you
Code Sandbox
Or
handleCollapseClick = rowIndex => {
const { collapsedRows } = this.state;
const shallowCopyOfCollapsedRows = new Set([...collapsedRows]);
let scrollToRow = rowIndex;
if (shallowCopyOfCollapsedRows.has(rowIndex)) {
shallowCopyOfCollapsedRows.delete(rowIndex);
scrollToRow = null;
} else {
shallowCopyOfCollapsedRows.add(rowIndex);
}
let numOfExpRows = 0;
if (collapsedRows.size > 0) {
numOfExpRows = collapsedRows.size;
}
let resetExpChildRow = -1;
if (collapsedRows.has(rowIndex)) {
numOfExpRows -= 1;
resetExpChildRow = rowIndex;
} else {
numOfExpRows += 1;
}
if (resetExpChildRow === -1) {
this.setState({
tableContainerHeight: 250 * numOfExpRows,
scrollToRow,
collapsedRows: shallowCopyOfCollapsedRows
});
} else {
this.setState({
numOfExpRows,
scrollToRow,
collapsedRows: shallowCopyOfCollapsedRows,
expChildRows: {
...this.state.expChildRows,
[rowIndex]: 0
}
});
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With