React - how to wrap a table row with Link - reactjs

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?

Related

How to add a column link to material-ui table and redirect to summary detail page using column link id

I am working Material-UI tables, such as the following basic table here but I have added an id column at the beginning.
Assuming I have this basic table example at the route localhost:3000/basic-table within my React app, how can I take the below code and make the <tableCell>{row.id}</tableCell> into a <a href link> which will allow the user to click on this {row.id} column and then present the user with a new screen, where I can take this id and show more data about it?
I will also need a means of returning back to the main page report, i.e. parent page.
What I am trying to achieve is basically from the main parent report, a user can click on a row id, which would then present the user with a detailed report based on that id, i.e. like a drill-down report.
From the doco, I can't find any examples where I can achieve this as unsure how to add a column link.
Can anyone help with this or point me to an example.
{rows.map((row) => (
<TableRow key={row.id}>
<TableCell component="th" scope="row">
{row.id}
</TableCell>
<TableCell align="right">{row.name}</TableCell>
<TableCell align="right">{row.calories}</TableCell>
<TableCell align="right">{row.fat}</TableCell>
<TableCell align="right">{row.carbs}</TableCell>
<TableCell align="right">{row.protein}</TableCell>
</TableRow>
))}
You can use history API
import { useHistory } from 'react-router-dom';
const YourComponent = () => {
...
const history = useHistory();
return {
...
rows.map((row) => (
<TableRow key={row.id} onClick={() => history.push(yourLocation)}>
<TableCell component="th" scope="row">
{row.id}
</TableCell>
<TableCell align="right">{row.name}</TableCell>
<TableCell align="right">{row.calories}</TableCell>
<TableCell align="right">{row.fat}</TableCell>
<TableCell align="right">{row.carbs}</TableCell>
<TableCell align="right">{row.protein}</TableCell>
</TableRow>
))}
}

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

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

How do I make this table sortable?

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>

get firestore collection record count for pagination react [duplicate]

This question already has answers here:
Cloud Firestore collection count
(29 answers)
Closed 3 years ago.
I have a table to display users collection including search filter. Now I want to add a material-ui pagination, where i want to pass the total record count.
the pagination works fine. How can I get that?
Any help appreciated.!
table and table pagination code
<Table className={classes.table}>
<TableHead>
<TableRow className={classes.tableHeader}>
<TableCell >#</TableCell>
<TableCell ></TableCell>
<TableCell>Name</TableCell>
<TableCell align="right">Phone</TableCell>
<TableCell align="right">Role</TableCell>
<TableCell align="right">Service</TableCell>
<TableCell align="right">Location</TableCell>
</TableRow>
</TableHead>
<TableBody>
{filteredList && filteredList
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map(row => (
<TableRow key={row.id}>
<TableCell>{items++}</TableCell>
<TableCell align="right">
<Link to={'/admin/profile/' + row.id} key={row.id} >
<Tooltip title="View Profile">
<UserIcon/>
</Tooltip>
</Link>
</TableCell>
<TableCell component="th" scope="row">
{row.sp_Name}
</TableCell>
<TableCell align="right">{row.sp_Phone}</TableCell>
<TableCell align="right">{row.sp_Role}</TableCell>
<TableCell align="right">{row.sp_Service}</TableCell>
<TableCell align="right">{row.sp_Location}</TableCell>
{/* <TableCell align="right">
<ApproveIcon onClick={this.handleClickDialogOpen} className={classes.icon} />
</TableCell> */}
<TableCell align="right">
<Tooltip title="Delete">
<DeleteIcon onClick={() => this.handleClickDialogOpen(row.id)} className={classes.icon} />
{/* <DeleteIcon onClick={() => deleteSP(row.id)} className={classes.icon} /> */}
</Tooltip>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<TablePagination
rowsPerPageOptions={[5, 10, 25]}
component="div"
//count={5}
count ={filteredList.length} -- I want to pass count
rowsPerPage={this.state.rowsPerPage}
page={this.state.page}
backIconButtonProps={{
'aria-label': 'Previous Page',
}}
nextIconButtonProps={{
'aria-label': 'Next Page',
}}
onChangePage={this.handleChangePage}
onChangeRowsPerPage={this.handleChangeRowsPerPage}
/>
Firestore doesn't keep a count of the number of documents in a collection. If you need that, you'll have to store it yourself and keep it up to date. For an example of this, see the documentation on aggregation queries and distributed counters.
Also see: Firebase firestore collection count

Resources