I'm trying to get my table to sort via the different columns following the material UI documentation. I'm not sure why its not working but upon clicking on the header the sort order shows up and i can see everything firing it just not sorting. I believe i have a tiny error somewhere in my code and after staring at it for hours i cant seem to find it.
Material UI Table Documentation
Working CodeSandbox I'm trying to follow as well that matches the documentation: CodeSandbox
Code base:
function TablePaginationActions(props) {
const theme = useTheme();
const { count, page, rowsPerPage, onChangePage } = props;
const handleFirstPageButtonClick = (event) => {
onChangePage(event, 0);
};
const handleBackButtonClick = (event) => {
onChangePage(event, page - 1);
};
const handleNextButtonClick = (event) => {
onChangePage(event, page + 1);
};
const handleLastPageButtonClick = (event) => {
onChangePage(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1));
};
return (
<div style={{ flexShrink: 0 }}>
<IconButton
onClick={handleFirstPageButtonClick}
disabled={page === 0}
aria-label="first page"
>
{theme.direction === 'rtl' ? <LastPageIcon /> : <FirstPageIcon />}
</IconButton>
<IconButton onClick={handleBackButtonClick} disabled={page === 0} aria-label="previous page">
{theme.direction === 'rtl' ? <KeyboardArrowRight /> : <KeyboardArrowLeft />}
</IconButton>
<IconButton
onClick={handleNextButtonClick}
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
aria-label="next page"
>
{theme.direction === 'rtl' ? <KeyboardArrowLeft /> : <KeyboardArrowRight />}
</IconButton>
<IconButton
onClick={handleLastPageButtonClick}
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
aria-label="last page"
>
{theme.direction === 'rtl' ? <FirstPageIcon /> : <LastPageIcon />}
</IconButton>
</div>
);
}
TablePaginationActions.propTypes = {
count: PropTypes.number.isRequired,
onChangePage: PropTypes.func.isRequired,
page: PropTypes.number.isRequired,
rowsPerPage: PropTypes.number.isRequired,
};
function descendingComparator(a, b, orderBy) {
if (b[orderBy] < a[orderBy]) {
return -1;
}
if (b[orderBy] > a[orderBy]) {
return 1;
}
return 0;
}
function getComparator(order, orderBy) {
return order === "desc"
? (a, b) => descendingComparator(a, b, orderBy)
: (a, b) => -descendingComparator(a, b, orderBy);
}
function stableSort(array, comparator) {
const stabilizedThis = array.map((el, index) => [el, index]);
stabilizedThis.sort((a, b) => {
const order = comparator(a[0], b[0]);
if (order !== 0) return order;
return a[1] - b[1];
});
return stabilizedThis.map(el => el[0]);
}
const headCells = [
{
id: "",
numeric: false,
disablePadding: true,
label: ""
},
{ id: "Holiday", numeric: true, disablePadding: false, label: "Holiday" },
{ id: "Date", numeric: true, disablePadding: false, label: "Date" },
{ id: "Branch", numeric: true, disablePadding: false, label: "Branch" },
{ id: "Hours", numeric: true, disablePadding: false, label: "Hours" },
{ id: "Web", numeric: true, disablePadding: false, label: "Web" },
{ id: "Phone", numeric: true, disablePadding: false, label: "Phone" },
{ id: "CoOp", numeric: true, disablePadding: false, label: "CoOp" },
{ id: "Submitted", numeric: true, disablePadding: false, label: "Submitted" },
{ id: "SubmittedBy", numeric: true, disablePadding: false, label: "SubmittedBy" },
{ id: "Published", numeric: true, disablePadding: false, label: "Published" },
{ id: "PublishedBy", numeric: true, disablePadding: false, label: "PublishedBy" },
];
const useStyles = makeStyles(theme => ({
visuallyHidden: {
border: 0,
clip: "rect(0 0 0 0)",
height: 1,
margin: -1,
overflow: "hidden",
padding: 0,
position: "absolute",
top: 20,
width: 1
}
}));
function EnhancedTableHead(props) {
const {
classes,
order,
orderBy,
onRequestSort
} = props;
const createSortHandler = property => event => {
onRequestSort(event, property);
};
return (
<TableHead>
<TableRow>
{headCells.map(headCell => (
<TableCell
key={headCell.id}
sortDirection={orderBy === headCell.id ? order : false}
>
<TableSortLabel
active={orderBy === headCell.id}
direction={orderBy === headCell.id ? order : "asc"}
onClick={createSortHandler(headCell.id)}
>
{headCell.label}
{orderBy === headCell.id ? (
<span className={classes.visuallyHidden}>
{order === "desc" ? "sorted descending" : "sorted ascending"}
</span>
) : null}
</TableSortLabel>
</TableCell>
))}
</TableRow>
</TableHead>
);
}
const HolidaySettings = () => {
const classes = useStyles();
const [loading, setLoading] = useState(true);
const [open, setOpen] = React.useState(false);
const [page, setPage] = React.useState(0);
const [rowsPerPage, setRowsPerPage] = React.useState(5);
const anchorRef = React.useRef(null);
const [dialogOpen, setDialogOpen] = React.useState(false);
const [dialogData, setDialogData] = React.useState({});
const [showInactive, setShowInactive] = useState(false);
const [searchResults, setSearchResults] = useState([]);
const [order, setOrder] = React.useState("asc");
const [orderBy, setOrderBy] = React.useState("Holiday");
const dispatch = useDispatch();
const onInit = useCallback(() => {
dispatch(actions.holiday_getHolidays());
dispatch(actions.holiday_getProductionHolidays());
}, [dispatch]);
useEffect(() => {
if (loading) {
onInit();
}
}, [loading]);
const rows = useSelector(state => {
let results = [];
if (showInactive) {
results = state.holidays.holidays;
} else {
results = state.holidays.activeHolidays;
}
if (state.holidays.holidays && loading) {
setLoading(false);
setSearchResults(results);
}
return results;
});
const handleRequestSort = (event, property) => {
const isAsc = orderBy === property && order === "asc";
setOrder(isAsc ? "desc" : "asc");
setOrderBy(property);
};
const handleToggle = () => {
setOpen((prevOpen) => !prevOpen);
};
const handleDialogOpen = (dataElement) => {
setDialogData(dataElement);
setDialogOpen(true);
setOpen(false);
}
const handleHolidayDelete = (dataElement) => {
dispatch(actions.holiday_deleteHoliday(dataElement));
}
const handleDialogClose = () => {
setOpen(false);
setDialogOpen(false);
};
const handleClose = (event) => {
if (anchorRef.current && anchorRef.current.contains(event.target)) {
return;
}
setOpen(false);
};
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
};
const handleInactiveChange = (e) => {
e.persist();
const { checked } = e.target;
setShowInactive(checked)
}
const handleSearch = (e) => {
e.persist();
const searchValue = e.target.value;
let results = _.map(rows, function(holiday) {
if (holiday.HolidayName.toLowerCase().indexOf(searchValue.toLowerCase()) !== -1) return holiday;
});
results = _.without(results, undefined);
setSearchResults(results);
}
return (
<div>
<div className="row">
<div className="col">
<div className="card-chart card">
<div className="card-header">
<div className="row">
<div className="col-sm-12 d-flex">
<h4 className="card-title">Holiday Settings</h4>
<div
className="ml-auto mr-5"
ref={anchorRef}
aria-controls={open ? 'menu-list-grow' : undefined}
aria-haspopup="true"
onClick={handleToggle}>
<SettingsOutlinedIcon style={{ fontSize: 20 }} />
{open ? (
<ExpandLess style={{ fontSize: 12 }} />
) : (
<ExpandMore style={{ fontSize: 12 }} />
)}
</div>
<Popper open={open} anchorEl={anchorRef.current} role={undefined} transition disablePortal>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{ transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom' }}
>
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MenuList autoFocusItem={open} id="menu-list-grow">
<MenuItem onClick={handleDialogOpen}>Add Holiday</MenuItem>
<MenuItem>
<FormControlLabel className=""
label="Show Inactive"
control={
<Checkbox
checked={showInactive || false}
value={showInactive}
onChange={handleInactiveChange}
name="Show Inactive"
color="primary"
/>
}
/>
</MenuItem>
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</div>
</div>
</div>
{loading ? (
<CanvasLoader loading={loading} />
) : (
<div className="card-body">
<div className="text-left col-12">
<Paper>
<TextField id="standard-basic" label="Search" onChange={handleSearch}/>
<TableContainer component={Paper} className="holidaysTableContainer">
<Table className="w-100" aria-label="simple table">
<EnhancedTableHead
classes={classes}
order={order}
orderBy={orderBy}
onRequestSort={handleRequestSort}
rowCount={rows.length}
/>
<TableBody>
{stableSort(searchResults, getComparator(order, orderBy))
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row, index) => {
return (
<TableRow key={row.RowId} id={row.Id} className={row.Active ? '' : 'inactive-row'}>
<TableCell>{<div className="d-flex flex-align-center justify-content-center"><CreateOutlinedIcon className="holidayEditIcon" style={{ color: '#00f2c3' }} onClick={() => { handleDialogOpen(row); }} /> {row.Active ? (<DeleteForeverOutlinedIcon className="holidayDeleteIcon" style={{ color: '#fd5d93' }} onClick={() => { handleHolidayDelete(row); }} />) : (<div></div>)}</div>}</TableCell>
<TableCell>{row.HolidayName}</TableCell>
<TableCell>{moment(row.HolidayDate).format('ddd, MMM Do YYYY')}</TableCell>
<TableCell>{row.Branch ? row.Branch : 'All'}</TableCell>
<TableCell>{row.Hours ? row.Hours : 'Closed'}</TableCell>
<TableCell>{(row.Web ? <DoneIcon style={{ color: '#00f2c3' }} value="true" /> : <CloseIcon style={{ color: '#fd5d93' }} value="false" />)}</TableCell>
<TableCell>{(row.Phone ? <DoneIcon style={{ color: '#00f2c3' }} value="true" /> : <CloseIcon style={{ color: '#fd5d93' }} value="false" />)}</TableCell>
<TableCell>{(row.CoOp ? <DoneIcon style={{ color: '#00f2c3' }} value="true" /> : <CloseIcon style={{ color: '#fd5d93' }} value="false" />)}</TableCell>
<TableCell>{(row.Submitted ? moment(row.Submitted).format('MMM Do, YYYY') : false)}</TableCell>
<TableCell>{row.SubmittedBy}</TableCell>
<TableCell>{(row.Published ? moment(row.Published).format('MMM Do, YYYY') : false)}</TableCell>
<TableCell>{row.PublishedBy}</TableCell>
</TableRow>
)
})}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={[5, 10, 25, { label: 'All', value: -1 }]}
colSpan={12}
count={searchResults.length}
rowsPerPage={rowsPerPage}
page={page}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
ActionsComponent={TablePaginationActions}
/>
</TableRow>
</TableFooter>
</Table>
</TableContainer>
</Paper>
<HolidayDialog open={dialogOpen} onClose={handleDialogClose} data={dialogData} />
</div>
</div>
)}
</div>
</div>
</div>
</div>
)
}
export default HolidaySettings;
As per your code, TableHead
and TableBody
are rendered with different ids.
Holiday name in TableHead
has id = 'Holiday'
but in TableBody
, it is 'HolidayName'
.
Holiday Date in TableHead
has id = 'Date'
but in TableBody
, it is 'HolidayDate'
.
You have to assign a similar string as
id
toTableHead
andTableBody
.
So try below changes to make sorting work
const headCells = [
{
id: "",
numeric: false,
disablePadding: true,
label: ""
},
{ id: "HolidayName", numeric: true, disablePadding: false, label: "Holiday" },
{ id: "HolidayDate", numeric: true, disablePadding: false, label: "Date" },
{ id: "Branch", numeric: true, disablePadding: false, label: "Branch" },
{ id: "Hours", numeric: true, disablePadding: false, label: "Hours" },
{ id: "Web", numeric: true, disablePadding: false, label: "Web" },
{ id: "Phone", numeric: true, disablePadding: false, label: "Phone" },
{ id: "CoOp", numeric: true, disablePadding: false, label: "CoOp" },
{ id: "Submitted", numeric: true, disablePadding: false, label: "Submitted" },
{ id: "SubmittedBy", numeric: true, disablePadding: false, label: "SubmittedBy" },
{ id: "Published", numeric: true, disablePadding: false, label: "Published" },
{ id: "PublishedBy", numeric: true, disablePadding: false, label: "PublishedBy" },
];
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