How do I make this table sortable? - reactjs

I think this is an easy problem, but I am struggling to understand how to do this since I don't have a class. I am trying to make this table sortable. I want to be able to sort based on the column that is clicked on.
Do I need to add a class? I am very new to ReactJS. Thanks for your help!
function CreateGradeTable(props) {
return (
<div>
<Table>
<TableHead>
<TableRow>
<TableCell className={props.tHead}>Student</TableCell>
<TableCell className={props.tHead}>Course</TableCell>
<TableCell className={props.tHead}>Term</TableCell>
<TableCell className={props.tHead}>Grade</TableCell>
<TableCell className={props.tHead}>Final</TableCell>
</TableRow>
</TableHead>
<TableBody>
{props.data.map((gradeDetail, i) => {
const {
gradeId,
courseName,
courseId,
studentName,
studentId,
courseTerm,
grade,
finalGradeForTerm,
} = gradeDetail;
return (
<TableRow
key={gradeId}
className={`${props.tRow} ${i % 2 !== 0 ? props.striped : ''}`}
>
<TableCell>
<Link to={`/student/${studentId}`}>
<StyledLink>{studentName}</StyledLink>
</Link>
</TableCell>
<TableCell>
<Link to={`/course/${courseId}`}>
<StyledLink>{courseName}</StyledLink>
</Link>
</TableCell>
<TableCell align="left">{courseTerm}</TableCell>
<TableCell align="left">{grade}</TableCell>
<TableCell align="left">{finalGradeForTerm}</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
);
}

I see that you are using Material-UI to display the table.
You need to use 'TableSortLabel' to display Header. This will also show ascending/descending arrows.
Then write an onClick handler on TableSortLabel to change state thus triggering re-render.
See the working example here
https://codesandbox.io/s/condescending-beaver-q0owe?fontsize=14
<TableBody>
{
props.data.sort(order === 'asc'? ascCompare: desCompare ).map((gradeDetail, i) => {
const {
grade,
courseName,
studentName,
} = gradeDetail;
return (
<TableRow
key={i}
>
<TableCell align="left">{grade}</TableCell>
<TableCell align="left">{studentName}</TableCell>
<TableCell align="left">{courseName}</TableCell>
</TableRow>
);
})}
</TableBody>

Related

Where am I missing a key prop here? I'm generating uuids for TableCell keys and I'm using row.id for the row keys

I have this bit of code where I'm getting the warning:
Each child in a list should have a unique "key" prop. Check the render
method of AllPersonnel... at TableRow
And the table cells are not rendering. Any help would be appreciated. Thanks!
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow key={uuidv4()}>
{cols.map((col) => {
return <TableCell key={col.path}>{col.name}</TableCell>
})}
</TableRow>
</TableHead>
<TableBody>
{personnel.map((row, i) => {
return (
<TableRow key={row.id}>
<TableCell key={uuidv4()}>{row.first_name}</TableCell>
<TableCell key={uuidv4()}>{row.last_name}</TableCell>
<TableCell key={uuidv4()}>{row.section}</TableCell>
<TableCell key={uuidv4()}>{row.role}</TableCell>
<TableCell key={uuidv4()}>{row.employee_type}</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
You don't need a key on your TableRow, only on the first child in the return of an iterator.
Try :
{personnel.map((row, i) => {
const key = uuidv4()
return (
<TableRow key={key}>
<TableCell>{row.first_name}</TableCell>
...
Normaly...

Material UI, Warning <td> cannot appear as a child of <div>

I'm using React, typescript and Material UI. I've created a pretty common "Create user form" with basic inputs for username, name, email, ect. Added two buttons, "Edit" and "Delete." Everything seems to function properly, however, I cannot get this error message resolved.
Warning: validateDOMNesting(...): <td> cannot appear as a child of <div>.
Here's the table from a react component:
<TableContainer className={classes.scroll} component={Paper}>
<Table stickyHeader aria-label="table">
<TableHead>
<TableRow>
<TableCell>Username</TableCell>
<TableCell align="right">First name</TableCell>
<TableCell align="right">Last name</TableCell>
<TableCell align="right">Email</TableCell>
<TableCell align="right">Connect_username</TableCell>
<TableCell align="right">Role</TableCell>
<TableCell align="left">Edit</TableCell>
<TableCell align="left">Delete</TableCell>
</TableRow>
</TableHead>
<TableBody>
{(rowsPerPage > 0
? props?.items.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
: props?.items
).map(item => (
<TableRow key={item.user_id}>
<TableCell component="th">{item.username}</TableCell>
<TableCell align="right">{item.first_name}</TableCell>
<TableCell align="right">{item.last_name}</TableCell>
<TableCell align="right">{item.email}</TableCell>
<TableCell align="right">{item.connect_username}</TableCell>
<TableCell align="right">{item.role?.map(r=>r.role).join(",")}</TableCell>
<TableCell>
<Button onClick={() => props.handleEdit(item)}>Edit</Button>
</TableCell>
<TableCell>
<Button onClick={() => props.handleConfirmDelete(item.user_id)}>Delete</Button>
</TableCell>
</TableRow>
))}
</TableBody>
<TablePagination
rowsPerPageOptions={[10, 25, { label: 'All', value: -1 }]}
count={props.items.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
</Table>
</TableContainer>
component={Paper} is likely causing this. Can you try removing it? If you want the table to appear on a Paper component, then try nesting TableContainer under Paper.
put TablePagination in the TableBody tag and wrap it in TableRow.
Had a similar issue and this fixed it.
Like this:
<TableBody>
{(rowsPerPage > 0
? props?.items.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
: props?.items)
.map(item => (
<TableRow key={item.user_id}>
<TableCell component="th">{item.username}</TableCell>
<TableCell align="right">{item.first_name}</TableCell>
<TableCell align="right">{item.last_name}</TableCell>
<TableCell align="right">{item.email}</TableCell>
<TableCell align="right">{item.connect_username}</TableCell>
<TableCell align="right">{item.role?.map(r=>r.role).join(",")}</TableCell>
<TableCell>
<Button onClick={() => props.handleEdit(item)}>
Edit
</Button>
</TableCell>
<TableCell>
<Button onClick={() => props.handleConfirmDelete(item.user_id)}>
Delete
</Button>
</TableCell>
</TableRow>
<TableRow>
<TablePagination
rowsPerPageOptions={[10, 25, { label: 'All', value: -1 }]}
count={props.items.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
</TableRow>
))}
</TableBody>
update: "#mui/material": "^5.10.9"
in my case this error was causing TablePagination and not Paper component.
working solution is wrapping pagination not only with row but also footer as follow
<TableContainer component={Paper}>
<Table>
<TableBody>
...
</TableBody>
<TableFooter>
<TableRow>
<TablePagination />
</TableRow>
</TableFooter>
</Table>
</TableContainer>
mui docs: https://mui.com/material-ui/react-table/#custom-pagination-actions

How can I display elements into each row?

currently I am trying to display countries into a Material-UI table. However, all the data is displaying into one row. Anyone know how to fix it? The country array contains elements per index.
<TableCell component="th" id={labelId} scope="row" >
{country}
</TableCell>
Try this
<Table >
<TableBody>
{generateRows()}
</TableBody>
</Table>
const countries =['india','usa']
const generateRows = (countries)=>{
return countries.map(country=>{
return (
<TableRow>
<TableCell component="th" scope="row">
{country}
</TableCell>
</TableRow >
)
})
}

Material Table - nested columns

Is it possible to make a table with nested columns using material-table library?
Final result that I want to achieve
Yes its possible with material-table . You have to use Components property to achieve that.
function App() {
const columns = [...];
const data = [...];
return (
<div className="App">
<MaterialTable
columns={columns}
data={data}
components={{
Header: props => {
return (
<TableHead>
<TableRow>
<TableCell colSpan={2} align="center">
Average A
</TableCell>
<TableCell colSpan={2} align="center">
Average B
</TableCell>
</TableRow>
<TableRow>
<TableCell align="center">Lower</TableCell>
<TableCell align="center">Upper</TableCell>
<TableCell align="center">Lower</TableCell>
<TableCell align="center">Upper</TableCell>
</TableRow>
</TableHead>
);
},
Row: ({ data }) => {
return (
<TableRow>
<TableCell align="center">{data.lowerA}</TableCell>
<TableCell align="center">{data.upperA}</TableCell>
<TableCell align="center">{data.lowerB}</TableCell>
<TableCell align="center">{data.upperB}</TableCell>
</TableRow>
);
}
}}
/>
</div>
);
}
DEMO: Codesandbox link
You can use colspan. Check example here.
https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_td_colspan

React - how to wrap a table row with Link

I have a table that I built using react mui. In that table I have a row, that looks like this:
<TableRow
hover
onClick={() => onRowClicked(item)}
role="checkbox"
aria-checked={isSelected}
tabIndex={-1}
key={item.id}
data-testid={item.id}
selected={isSelected}
className={classes.tr}
>
<TableCell padding="checkbox" classes={{body: classes.smWidth}} onClick={(event) => onCheckboxClick(event, item.id)}>
<Checkbox checked={isSelected}/>
</TableCell>
<TableCell component="th" scope="row" classes={{body: classes.id}}>{item.id}</TableCell>
<TableCell align="left">{item.text}</TableCell>
<TableCell align="left">{item.code}</TableCell>
<TableCell align="left">{item.type}</TableCell>
<TableCell align="left"><StatusLabel status={item.substatus || item.status} size="large"/></TableCell>
<TableCell align="left">{getElapsedTime(new Date(item.updated), now)}</TableCell>
</TableRow>
And this works fine, but instead of using onClick function to navigate to each item, I would like to use link, so that I also get on control/cmd click possibility to open link in a new tab. I have tried just by wrapping the whole TableRow with Link component:
<Link to={`/${item.id}`}>
<TableRow
hover
role="checkbox"
aria-checked={isSelected}
tabIndex={-1}
key={item.id}
data-testid={item.id}
selected={isSelected}
className={classes.tr}
>
<TableCell padding="checkbox" classes={{body: classes.smWidth}} onClick={(event) => onCheckboxClick(event, item.id)}>
<Checkbox checked={isSelected}/>
</TableCell>
<TableCell component="th" scope="row" classes={{body: classes.id}}>{item.id}</TableCell>
<TableCell align="left">{item.text}</TableCell>
<TableCell align="left">{item.code}</TableCell>
<TableCell align="left">{item.type}</TableCell>
<TableCell align="left"><StatusLabel status={item.substatus || item.status} size="large"/></TableCell>
<TableCell align="left">{getElapsedTime(new Date(item.updated), now)}</TableCell>
</TableRow>
</link>
But, then the whole table styling is out of place. What is the right way to do this then?

Resources