How to disable onClick function on a single table row? - reactjs

I encountered the case having a Link in a specific row and onClick function which triggers if any of rows is clicked. So for the row, that has a Link I don't want to trigger onClick function, but can't find out the correct approach.
Any help will be appreciated.
Codesandobox link
And Code example below
import * as React from "react";
import Table from "#mui/material/Table";
import TableBody from "#mui/material/TableBody";
import TableCell from "#mui/material/TableCell";
import TableContainer from "#mui/material/TableContainer";
import TableHead from "#mui/material/TableHead";
import TableRow from "#mui/material/TableRow";
import Paper from "#mui/material/Paper";
import { Link } from "#mui/material";
function createData(
name: string,
calories: number,
fat: number,
carbs: number,
protein: number
) {
return { name, calories, fat, carbs, protein };
}
const rows = [
createData("Frozen yoghurt", 159, 6.0, 24, 4.0),
createData("Ice cream sandwich", 237, 9.0, 37, 4.3),
createData("Eclair", 262, 16.0, 24, 6.0),
createData("Cupcake", 305, 3.7, 67, 4.3),
createData("Gingerbread", 356, 16.0, 49, 3.9)
];
export default function BasicTable() {
return (
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Dessert (100g serving)</TableCell>
<TableCell align="right">Calories</TableCell>
<TableCell align="right">Fat (g)</TableCell>
<TableCell align="right">Carbs (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow onClick={() => alert("test")} key={row.name}>
<Link
target="_blank"
rel="noopener noreferrer"
underline="hover"
href="https://www.google.com/"
sx={{ cursor: "pointer" }}
color="inherit"
>
<TableCell component="th" scope="row">
{row.name}
</TableCell>
</Link>
<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>
))}
</TableBody>
</Table>
</TableContainer>
);
}

you can add:
onClick={(e) => e.stopPropagation()}
to your Link element. This way, when the Link is clicked, the click event won't propagate to the parent TableRow

This might help
<TableBody>
{rows.map((row,i) => (
<TableRow onClick={() => {if(!document.getElementById(`row${i}`)) alert("test")}} key={row.name}>
<Link
id={`row${i}`}
target="_blank"
rel="noopener noreferrer"
underline="hover"
href="https://www.google.com/"
sx={{ cursor: "pointer" }}
color="inherit"
>
<TableCell component="th" scope="row">
{row.name}
</TableCell>
</Link>

Related

Set Material UI table default sorting

This should be much easier than what I'm seeing and I'm sure there is a better way. How can I set the default sorting of a Material UI table like the one below. I don't want a button at the top so the sorting can be changed, I want the table to sort, by protein, by default when the table is loaded.
import * as React from 'react';
import PropTypes from 'prop-types';
import Box from '#mui/material/Box';
import Collapse from '#mui/material/Collapse';
import IconButton from '#mui/material/IconButton';
import Table from '#mui/material/Table';
import TableBody from '#mui/material/TableBody';
import TableCell from '#mui/material/TableCell';
import TableContainer from '#mui/material/TableContainer';
import TableHead from '#mui/material/TableHead';
import TableRow from '#mui/material/TableRow';
import Typography from '#mui/material/Typography';
import Paper from '#mui/material/Paper';
import KeyboardArrowDownIcon from '#mui/icons-material/KeyboardArrowDown';
import KeyboardArrowUpIcon from '#mui/icons-material/KeyboardArrowUp';
function createData(name, calories, fat, carbs, protein, price) {
return {
name,
calories,
fat,
carbs,
protein,
price,
history: [
{
date: '2020-01-05',
customerId: '11091700',
amount: 3,
},
{
date: '2020-01-02',
customerId: 'Anonymous',
amount: 1,
},
],
};
}
function Row(props) {
const { row } = props;
const [open, setOpen] = React.useState(false);
return (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
<TableCell>
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(!open)}
>
{open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
</IconButton>
</TableCell>
<TableCell component="th" scope="row">
{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>
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box sx={{ margin: 1 }}>
<Typography variant="h6" gutterBottom component="div">
History
</Typography>
<Table size="small" aria-label="purchases">
<TableHead>
<TableRow>
<TableCell>Date</TableCell>
<TableCell>Customer</TableCell>
<TableCell align="right">Amount</TableCell>
<TableCell align="right">Total price ($)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{row.history.map((historyRow) => (
<TableRow key={historyRow.date}>
<TableCell component="th" scope="row">
{historyRow.date}
</TableCell>
<TableCell>{historyRow.customerId}</TableCell>
<TableCell align="right">{historyRow.amount}</TableCell>
<TableCell align="right">
{Math.round(historyRow.amount * row.price * 100) / 100}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Box>
</Collapse>
</TableCell>
</TableRow>
</React.Fragment>
);
}
Row.propTypes = {
row: PropTypes.shape({
calories: PropTypes.number.isRequired,
carbs: PropTypes.number.isRequired,
fat: PropTypes.number.isRequired,
history: PropTypes.arrayOf(
PropTypes.shape({
amount: PropTypes.number.isRequired,
customerId: PropTypes.string.isRequired,
date: PropTypes.string.isRequired,
}),
).isRequired,
name: PropTypes.string.isRequired,
price: PropTypes.number.isRequired,
protein: PropTypes.number.isRequired,
}).isRequired,
};
const rows = [
createData('Frozen yoghurt', 159, 6.0, 24, 4.0, 3.99),
createData('Ice cream sandwich', 237, 9.0, 37, 4.3, 4.99),
createData('Eclair', 262, 16.0, 24, 6.0, 3.79),
createData('Cupcake', 305, 3.7, 67, 4.3, 2.5),
createData('Gingerbread', 356, 16.0, 49, 3.9, 1.5),
];
export default function CollapsibleTable() {
return (
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableHead>
<TableRow>
<TableCell />
<TableCell>Dessert (100g serving)</TableCell>
<TableCell align="right">Calories</TableCell>
<TableCell align="right">Fat (g)</TableCell>
<TableCell align="right">Carbs (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<Row key={row.name} row={row} />
))}
</TableBody>
</Table>
</TableContainer>
);
}
Note this is not a DataGrid, this is a Material UI Table.
You just need to call sort on the row data before rendering it
<TableBody>
{rows
.sort((a, b) => a.protein < b.protein ? -1 : 1)
.map((row) => (
<Row key={row.name} row={row} />
))}
</TableBody>

how to handle a click on a row in a simple table using Material UI

I'm using the DenseTable component of the ui material and I want to be able to get all the data from the clicked row, if anyone knows it would be a big help
Below is the source code of the component I'm using:
import * as React from 'react';
import Table from '#mui/material/Table';
import TableBody from '#mui/material/TableBody';
import TableCell from '#mui/material/TableCell';
import TableContainer from '#mui/material/TableContainer';
import TableHead from '#mui/material/TableHead';
import TableRow from '#mui/material/TableRow';
import Paper from '#mui/material/Paper';
function createData(name, calories, fat, carbs, protein) {
return { name, calories, fat, carbs, protein };
}
const rows = [
createData('Frozen yoghurt', 159, 6.0, 24, 4.0),
createData('Ice cream sandwich', 237, 9.0, 37, 4.3),
createData('Eclair', 262, 16.0, 24, 6.0),
createData('Cupcake', 305, 3.7, 67, 4.3),
createData('Gingerbread', 356, 16.0, 49, 3.9),
];
export default function DenseTable() {
return (
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} size="small" aria-label="a dense table">
<TableHead>
<TableRow>
<TableCell>Dessert (100g serving)</TableCell>
<TableCell align="right">Calories</TableCell>
<TableCell align="right">Fat (g)</TableCell>
<TableCell align="right">Carbs (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow
key={row.name}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell component="th" scope="row">
{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>
))}
</TableBody>
</Table>
</TableContainer>
);
}
i know the ui material has several components with this action but they are very elaborate and i want a simple table
You can add onClick handler in the TableRow and when clicked on the row, store the row data in the state variable or whatever the UX is
const [selectedRow, setSelectedRow] = React.useState({});
<TableRow
onClick={() => setSelectedRow(row)}
key={row.name}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
Complete code will be something like below
import * as React from "react";
import Table from "#mui/material/Table";
import TableBody from "#mui/material/TableBody";
import TableCell from "#mui/material/TableCell";
import TableContainer from "#mui/material/TableContainer";
import TableHead from "#mui/material/TableHead";
import TableRow from "#mui/material/TableRow";
import Paper from "#mui/material/Paper";
function createData(name, calories, fat, carbs, protein) {
return { name, calories, fat, carbs, protein };
}
const rows = [
createData("Frozen yoghurt", 159, 6.0, 24, 4.0),
createData("Ice cream sandwich", 237, 9.0, 37, 4.3),
createData("Eclair", 262, 16.0, 24, 6.0),
createData("Cupcake", 305, 3.7, 67, 4.3),
createData("Gingerbread", 356, 16.0, 49, 3.9)
];
export default function DenseTable() {
const [selectedRow, setSelectedRow] = React.useState({});
console.log({ selectedRow });
return (
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} size="small" aria-label="a dense table">
<TableHead>
<TableRow>
<TableCell>Dessert (100g serving)</TableCell>
<TableCell align="right">Calories</TableCell>
<TableCell align="right">Fat (g)</TableCell>
<TableCell align="right">Carbs (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow
onClick={() => setSelectedRow(row)}
key={row.name}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell component="th" scope="row">
{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>
))}
</TableBody>
</Table>
</TableContainer>
);
}

How to give round corners to the TableHead of Material UI

I am working with React and Material UI and I am trying to give a broder-radius of 10px to the header of the table so that only the TableHead have round corners.
The problem is that border-radius doesn't seem to work at all on TableHead as you can see in this screenshot:
Code (taken from the official docs, I just added the style to the TableHead):
<TableContainer>
<Table aria-label="customized table">
<TableHead style={{ backgroundColor: '#E2E2E2', borderRadius: '10px' }}>
<TableRow>
<TableCell>Dessert (100g serving)</TableCell>
<TableCell align="right">Calories</TableCell>
<TableCell align="right">Fat (g)</TableCell>
<TableCell align="right">Carbs (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow key={row.name}>
<TableCell component="th" scope="row">
{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>
))}
</TableBody>
</Table>
</TableContainer>;
How do I get this done?
As far as I know, there are no ways to set border-radius for the entire tablerow, but the problem can be solved by using a separate radius for the first and last cells:
const { Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, makeStyles } = MaterialUI
const useStyles = makeStyles((theme) => ({
table: {
minWidth: 400,
},
thead: {
backgroundColor: 'lightgray',
'& th:first-child': {
borderRadius: '1em 0 0 1em'
},
'& th:last-child': {
borderRadius: '0 1em 1em 0'
}
}
}))
function createData(name, calories, fat, carbs, protein) {
return { name, calories, fat, carbs, protein }
}
const rows = [
createData('Frozen yoghurt', 159, 6.0, 24, 4.0),
createData('Ice cream sandwich', 237, 9.0, 37, 4.3),
createData('Eclair', 262, 16.0, 24, 6.0),
createData('Cupcake', 305, 3.7, 67, 4.3),
createData('Gingerbread', 356, 16.0, 49, 3.9),
]
const App = function () {
const classes = useStyles()
return (
<TableContainer component={Paper}>
<Table className={classes.table} aria-label="customized table">
<TableHead classes={{ root: classes.thead }}>
<TableRow>
<TableCell>Dessert (100g serving)</TableCell>
<TableCell align="right">Calories</TableCell>
<TableCell align="right">Fat (g)</TableCell>
<TableCell align="right">Carbs (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow key={row.name}>
<TableCell component="th" scope="row">{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>
))}
</TableBody>
</Table>
</TableContainer>
)}
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://unpkg.com/react#17.0.2/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#17.0.2/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/#material-ui/core#4.11.3/umd/material-ui.development.js"></script>
<div id="root"></div>
You can continue using the makeStyles hook generator to style the table header.
import { makeStyles } from '#material-ui/core/styles';
const useTableHeaderStyles = makeStyles({
// .MuiTableHead-root
root: {
backgroundColor: '#E2E2E2',
borderRadius: '10px',
},
});
In the component:
const tableHeaderClasses = useTableHeaderStyles();
...
<TableHead classes={tableHeaderClasses.root}>
...
</TableHead>

material ui: reactjs: tablecell: width fit to content

I have the following code. Its same as the basic table from material ui. Only thing i have modified the First row, second column content. I am using a Grid element inside it. and also added more columns so that i can see a horizontal scroll scenario
I dont want it to break.
import React from 'react';
import Table from '#material-ui/core/Table';
import TableBody from '#material-ui/core/TableBody';
import TableCell from '#material-ui/core/TableCell';
import TableContainer from '#material-ui/core/TableContainer';
import TableHead from '#material-ui/core/TableHead';
import TableRow from '#material-ui/core/TableRow';
import Paper from '#material-ui/core/Paper';
import Grid from '#material-ui/core/Grid';
import { createMuiTheme, makeStyles, ThemeProvider } from '#material-ui/core/styles';
const hare = makeStyles({
table: {
minWidth: 650,
},
});
function createData(name, calories, fat, carbs, protein) {
return { name, calories, fat, carbs, protein };
}
const rows = [
createData('Frozen yoghurt',
159,
<Grid container alignItems="center" justify="space-between" spacing={10}>
<Grid item>
Dont
</Grid>
<Grid item>
Break
</Grid>
</Grid>,
24,
4.0
),
createData('Ice cream sandwich', 237, 9.0, 37, 4.3),
createData('Eclair', 262, 16.0, 24, 6.0),
createData('Cupcake', 305, 3.7, 67, 4.3),
createData('Gingerbread', 356, 16.0, 49, 3.9),
];
export default function BasicTable() {
const classes = hare();
return (
<TableContainer component={Paper}>
<Table className={classes.table} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Dessert (100g serving)</TableCell>
<TableCell align="right">Calories</TableCell>
<TableCell align="right">Fat (g)</TableCell>
<TableCell align="right">Carbs (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
<TableCell align="right">Calories</TableCell>
<TableCell align="right">Fat (g)</TableCell>
<TableCell align="right">Carbs (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow key={row.name}>
<TableCell component="th" scope="row">
{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>
))}
</TableBody>
</Table>
</TableContainer>
);
}
What is see is
You can use the wrap="nowrap" prop on the Grid. From the docs:
wrap - Defines the flex-wrap style property. It's applied for all screen sizes.
Just the excerpt that I've changed, and the codesandbox
<Grid container wrap="nowrap" alignItems="center" justify="space-between" spacing={10}>
<Grid item>Dont</Grid>
<Grid item>Break</Grid>
</Grid>

How do i collapse a datatable?

I'm trying to collapse a datatable in material-ui and show only the table header with total number of rows in the datatable. Is there a datatable plugin that allows me to do this?
Show the number of rows by taking the length of your array... relevant js:
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Table from '#material-ui/core/Table';
import TableBody from '#material-ui/core/TableBody';
import TableCell from '#material-ui/core/TableCell';
import TableHead from '#material-ui/core/TableHead';
import TableRow from '#material-ui/core/TableRow';
import Paper from '#material-ui/core/Paper';
import Badge from '#material-ui/core/Badge';
const useStyles = makeStyles({
root: {
width: '100%',
overflowX: 'auto',
},
table: {
minWidth: 650,
},
});
function createData(name, calories, fat, carbs, protein) {
return { name, calories, fat, carbs, protein };
}
const rows = [
createData('Frozen yoghurt', 159, 6.0, 24, 4.0),
createData('Ice cream sandwich', 237, 9.0, 37, 4.3),
createData('Eclair', 262, 16.0, 24, 6.0),
createData('Cupcake', 305, 3.7, 67, 4.3),
createData('Gingerbread', 356, 16.0, 49, 3.9),
];
export default function SimpleTable() {
const classes = useStyles();
return (
<Paper className={classes.root}>
<Table className={classes.table} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>
<Badge className={classes.margin} badgeContent={rows.length} color="primary">
</Badge>
</TableCell> <TableCell>Dessert (100g serving)
</TableCell>
<TableCell align="right">Calories</TableCell>
<TableCell align="right">Fat (g)</TableCell>
<TableCell align="right">Carbs (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map(row => (
<TableRow key={row.name}>
<TableCell align="right"></TableCell>
<TableCell component="th" scope="row">
{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>
))}
</TableBody>
</Table>
</Paper>
);
}
hide the tbody using display:none on the class of the tbody... relevant css:
.MuiTableBody-root{
display: none;
}
complete working stackblitz here

Resources