I have created a grid that is accordian-style. You can click the arrow on the left to expand/reduce the number of rows. This grid actually works well in other places in the project with no issues. However, in this case, I am having an issue where the expand button in the first row is firing its onClick event whenever the grid is clicked. Anywhere in the grid, even on different rows, that one event is firing.
Please help me make sense of this.
I have tried adding in the onRowClick function, and stopping propagation based on that, with no success.
Here is my code:
import React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { withStyles } from '@material-ui/core/styles';
import TableCell from '@material-ui/core/TableCell';
import Paper from '@material-ui/core/Paper';
import { AutoSizer, Column, Table } from 'react-virtualized';
import IconButton from '@material-ui/core/IconButton';
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
import CheckIcon from '@material-ui/icons/Check';
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
const styles = theme => ({
flexContainer: {
display: 'flex',
alignItems: 'center',
boxSizing: 'border-box',
},
tableRow: {
cursor: 'pointer',
},
tableRowHover: {
'&:hover': {
backgroundColor: theme.palette.grey[200],
},
},
tableCell: {
flex: 1,
},
noClick: {
cursor: 'initial',
},
button: {
"&:hover": {
backgroundColor: "transparent"
}
}
});
class MuiVirtualizedTable extends React.PureComponent {
static defaultProps = {
headerHeight: 48,
rowHeight: 48,
};
getRowClassName = ({ index }) => {
const { classes } = this.props;
return clsx(classes.tableRow, classes.flexContainer, {
[classes.tableRowHover]: false
});
};
cellRenderer = (props) => {
const { cellData, columnIndex } = props;
const { columns, classes, rowHeight } = this.props;
const style = props.rowData.child ? { height: rowHeight, backgroundColor: "#E8E8E8" } : { height: rowHeight, paddingLeft: 8, paddingRight: 4 }
let isChild = props.rowData.child;
let isExpanded = props.rowData.open;
if (props.dataKey === "open") {
return (
<TableCell
component="div"
className={clsx(classes.tableCell, classes.flexContainer, {
[classes.noClick]: false
})}
variant="body"
style={style}
align={(columnIndex != null && columns[columnIndex].numeric) || false ? 'right' : 'left'}
>
{
isChild ?
<div></div> :
<IconButton onClick={(event) => this.props.expand(props.rowData)} className={classes.button}>
{
isExpanded ? <KeyboardArrowDownIcon style={{ fontSize: 24 }} /> : <ChevronRightIcon style={{ fontSize: 24 }}></ChevronRightIcon>
}
</IconButton>
}
</TableCell>)
}
else if (props.dataKey === "select") {
return (
<TableCell
component="div"
className={clsx(classes.tableCell, classes.flexContainer, {
[classes.noClick]: false
})}
variant="body"
style={style}
align={(columnIndex != null && columns[columnIndex].numeric) || false ? 'right' : 'left'}
>
{
isChild ?
<div></div> :
<IconButton onClick={() => this.props.rowSelected(props.rowData)} className={classes.button}>
<CheckIcon fontSize="inherit" style={{ color: "#D71920" }} />
</IconButton>
}
</TableCell>)
}
else {
return (
<TableCell
component="div"
className={clsx(classes.tableCell, classes.flexContainer, {
[classes.noClick]: true
})}
variant="body"
style={style}
align={(columnIndex != null && columns[columnIndex].numeric) || false ? 'right' : 'left'}
>
{cellData}
</TableCell>
);
}
};
headerRenderer = ({ label, columnIndex }) => {
const { headerHeight, columns, classes } = this.props;
const textStyle = { color: "#D71920", fontSize: 14, fontWeight: 600 };
return (
<TableCell
component="div"
className={clsx(classes.tableCell, classes.flexContainer, classes.noClick)}
variant="head"
style={{ height: headerHeight, paddingLeft: 8 }}
align={columns[columnIndex].numeric || false ? 'right' : 'left'}
>
<span style={textStyle}>{label}</span>
</TableCell>
);
};
render() {
const { classes, columns, rowHeight, headerHeight, key, ...tableProps } = this.props;
return (
<AutoSizer>
{({ height, width }) => (
<Table
key={key}
height={height}
width={width}
rowHeight={rowHeight}
headerHeight={headerHeight}
{...tableProps}
rowClassName={this.getRowClassName}
>
{columns.map(({ dataKey, ...other }, index) => {
return (
<Column
key={dataKey}
headerRenderer={headerProps =>
this.headerRenderer({
...headerProps,
columnIndex: index,
})
}
className={classes.flexContainer}
cellRenderer={this.cellRenderer}
dataKey={dataKey}
{...other}
/>
);
})}
</Table>
)}
</AutoSizer>
);
}
}
MuiVirtualizedTable.propTypes = {
classes: PropTypes.object.isRequired,
columns: PropTypes.arrayOf(
PropTypes.shape({
dataKey: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
numeric: PropTypes.bool,
width: PropTypes.number.isRequired,
}),
).isRequired,
headerHeight: PropTypes.number,
onRowClick: PropTypes.func,
rowHeight: PropTypes.number,
};
const VirtualizedTable = withStyles(styles)(MuiVirtualizedTable);
// ---
export default function OrderGroupGrid(props) {
const [rows, setRows] = React.useState({ data: [] });
const expRow = (data) => {
console.log("onSelectExpandRow: ", data)
let row = rows.data.find(e => e.id === data.id);
row.open = !row.open;
let newRows = []
if (row.open) {
rows.data.forEach(r => {
if (r.id !== row.id) {
newRows.push(r);
}
else {
newRows.push(r);
r.items.forEach(child => {
child.parentId = r.id
child.child = true;
newRows.push(child);
})
}
})
}
else {
newRows = rows.data.filter(r => r.parentId !== row.id);
}
setRows({ data: newRows });
}
const selectRow = (row) => {
console.log("Selected Order Group: ", row)
props.selectOrderGroup(row)
}
const rowClick = (e) => {
console.log("rowClick - ", e.event);
if (e && e.event && e.event.stopPropagation) {
e.event.stopPropagation();
}
}
if (rows.data.length === 0 && props.rows) {
let newData = props.rows.map(r => {
r.open = false;
r.itemNumber = r.items.length + " items"
return r;
})
setRows({ data: newData });
}
const columns = [
{ width: 40, maxWidth: 40, label: '', dataKey: 'open', flexGrow: 1 },
{ width: 200, label: 'Order Group Description', dataKey: 'description', flexGrow: 1 },
{ width: 100, label: 'Item Number', dataKey: 'itemNumber', flexGrow: 1 },
{ width: 200, label: 'Item Description', dataKey: 'itemDescription', flexGrow: 1 },
{ width: 60, label: '', dataKey: 'select', flexGrow: 1 }
]
let rowCount = (rows && rows.data) ? rows.data.length : 0
return (
<React.Fragment>
<Paper style={{ height: "35vh", width: '100%' }}>
<VirtualizedTable
rowCount={rowCount}
rowGetter={({ index }) => rows.data[index]}
columns={columns}
expand={expRow}
rowSelected={selectRow}
onRowClick={rowClick}
/>
</Paper>
</React.Fragment>
);
}
Versions:
"react": "^16.9.0" "react-virtualized": "^9.21.1"