I use table material UI and
useEffect(() => {
if (props.grouping){
const { groupProperty, rows } = props;
const groups = props.rows.reduce((acc, row) => {
acc[row[groupProperty]] = acc[row[groupProperty]] || [];
acc[row[groupProperty]].push(row);\
return acc;
}, {});
setGroups(groups)
const keys = Object.keys(groups);
setDataGroup(keys)
}
}, [props.rows,props.schema]);
<TableBody>
<>
{props.grouping && (
dataGroup.map(key => (
<TableRow className={clsx(classes.row, props.rowClassName)}>
<TableCell colspan="5">
<CardActions disableActionSpacing>
<b>
{key}
</b>
<IconButton
className={expanded[key] ? classes.expandOpen : classes.expand}
onClick={() => handleExpandClick(key)}
aria-expanded={expanded[key]}
aria-label="Show more"
>
<ExpandMoreIcon />
</IconButton>
</CardActions>
<Collapse in={expanded[key]} timeout="auto" unmountOnExit>
<MuiTable {...other}>
<TableBody>
{groups[key].map(row => (
<TableRow >
<TableCell >{row.wageTitle}</TableCell>
<TableCell numeric>{row.buy}</TableCell>
<TableCell numeric>{row.sell}</TableCell>
<TableCell numeric>{row.buyMaximum}</TableCell>
<TableCell numeric>{row.sellMaximum}</TableCell>
</TableRow>
)
)}
</TableBody>
</MuiTable>
</Collapse>
</TableCell>
</TableRow>
))
)}
.....
<Table grouping={true} rows={tableData} className={classes.table} schema={schema} groupProperty="deductionTypeTitle" ></Table>
I use this link How to group table in reactjs?
..But I want this grouping to be based on two data like this: https://codesandbox.io/s/l9ijbc?file=/demo.js
Someone can help me
I don't know if the question captures what I had in my but I will explain below...
I fetched data from API and mapped into a collapsible table. Full details of the data should be embedded in EACH row such that onclick on each row, reveals the full details. Here's the code below
function Encounter() {
const [open2, setOpen2] = useState(false);
const [details, setDetails] = useState([]);
const getDetails = async () => {
try {
const fetch = await Axios.get(
"https://pshs3.herokuapp.com/all/encounter"
);
setDetails(fetch.data.data)
} catch (err) {
console.log(err);
}
};
useEffect(() => {
getDetails();
}, []);
return (
<Wrapper>
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
<TableCell/>
<TableCell >
Enrollment ID
</TableCell>
<TableCell >
Encounter
</TableCell>
<TableCell>
Facility Code
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{details.map((detail, idx) => {
return (
<>
<TableRow sx={{ "& > *": { borderBottom: "unset" } }}>
<TableCell>
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(!open)}
>
{open ? (
<KeyboardArrowUpIcon />
) : (
<KeyboardArrowDownIcon />
)}
</IconButton>
</TableCell>
<TableCell key={idx}>
{detail.enrollment_id}
</TableCell>
<TableCell key={idx}>
{detail.encounter}
</TableCell>
<TableCell key={idx}>
{detail.faciity_code}
</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">
Details
</Typography>
<Tooltip />
</Box>
</Collapse>
</TableCell>
</TableRow>
</>
);
})}
</TableBody>
</Table>
</Wrapper>
);
}
export default Encounter;
The problem I have is how to implement the open and setOpen state to individual row, also the Tooltip component(which is a the full table details from the API) to display full details of each row onclick which should correspond to the selected row in question.
Here are two solutions to the first problem.
1. Create a separate component for each <TableRow />
This component will have its own state and allows you to collapse/expand each row individually.
2. Use a dictionary for the open state
Since you have multiple (dynamic) rows, you can introduce a dictionary for the open state.
const [open, setOpen] = useState({});
For each row, you will use the open[idx] property to determine if the row should be "open"
<Collapse in={open[idx]} timeout="auto" unmountOnExit>
And in the <IconButton /> component, set the state based on the current row state.
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(current => ({ ...current, [idx]: !current[idx] }))}
>
Firstly add a variable in your details object:
const getDetails = async () => {
try {
const fetch = await Axios.get(
"https://pshs3.herokuapp.com/all/encounter"
);
const response = fetch.data.data;
response.map((elem) => elem.open = false)
setDetails(response)
} catch (err) {
console.log(err);
}
};
Then you can change the open variable for each element in details:
<IconButton
aria-label="expand row"
size="small"
onClick={() => detail.open = !detail.open)}
>
You might need to update the state, so change your onClick:
onClick={() => changeOpenStatus(idx))}
and the function:
const changeOpenStatus = (idx) => {
const newDetails = {...details}
newDetails[idx].open = !newDetails[idx].open;
setDetails(newDetails)
}
I loop an array and inside that, I loop another array within a switch statement.
<TableBody>
{(rowsPerPage > 0
? filteredItems.slice(
page * rowsPerPage,
page * rowsPerPage + rowsPerPage
)
: filteredItems
).map((row, index) => (
<TableRow key={index}>
{headers.map(({ value, idx }) => {
switch (value) {
case 'status':
return (
<TableCell key={idx}>
{row.status ? (
<Button
variant='contained'
className={classes.red}
onClick={() =>
changeStatus(row.id, row.status)
}
>
Deactivate
</Button>
) : (
<Button
variant='contained'
className={classes.green}
onClick={() =>
changeStatus(row.id, row.status)
}
>
Activate
</Button>
)}
</TableCell>
);
default:
if (value.split('.').length === 1) {
return (
<TableCell component='th' scope='row' key={idx}>
{row[value]}
</TableCell>
);
} else {
let obj = value.split('.')[0];
let nestObj = value.split('.')[1];
return (
<TableCell component='th' scope='row' key={idx}>
{row[obj] && row[obj][nestObj]}
</TableCell>
);
}
}
})}
</TableRow>
))}
</TableBody>
In both places I have added the key property. Why do I get this error?
I have 2 map function in my file. I want the iterator variable of the second map function to be passed as an argument of another function on every button click. Fairly new to react so do not know what should i do to make it correct
return (
<Table className={classes.table} aria-labelledby="tableTitle">
<EnhancedTableHead />
<TableBody>
{dataList.map((item, item_idx) => {
return (
<TableRow
hover
tabIndex={-1}
key={item.id}
>
<TableCell padding="checkbox">
</TableCell>
<TableCell padding="none">
{item.dataName}--{item.datatId}
</TableCell>
<TableCell padding="none" ><SwitchButton/></TableCell>
<TableCell >
{
this.state.user.map((ab, ab_idx) =>
<li className = "display">
{isSwitchOn === false ? <input type = 'search' placeholder = "id" id="name" name = "name" onChange={(e) => this.handleidChange(e,ab_idx)}/>: " All"}
{isSwitchOn === false ? <a href="javascript:void(0)" className = "removeIcon" style={{display:(this.state.user.length > 1 )? "inline" : "none"}} onClick={() => {this.removeItem(this.state,ab_idx)}} >Remove</a> : "" }
</li>
)}
</TableCell>
<TableCell padding="none">
<Button className="user-edit-btn" style={{display:(isSwitchOn === false) ? "block" : "none", textTransform: 'capitalize',}} variant="contained" onClick={() => {this.insertTextBox(item)}} >
Add User
</Button>
</TableCell>
</TableRow>
);
})}
{(
<TableRow style={{ height: 49 }}>
<TableCell colSpan={6} />
</TableRow>
)}
</TableBody>
</Table>
)
I want "ab" to be passed as an argument of the function insertTextBox(item) whenever I click on ADD USER Button. While doing that gives me an error "Error: Adjacent JSX elements must be wrapped in an enclosing tag.
Did you want Fragment??"
You need to bind handleidChange in your constructor like this:
this.handleidChange = this.handleidChange.bind(this)
Then your handler should be like this:
handleidChange(ab_idx, e){
//do smth
}
Then pass the handler this way:
<input type = 'search' placeholder = "id" id="name" name = "name"
onChange={this.handleidChange.bind(this, ab_idx)}/>`
I would like to implement Google's Material UI Menu Item inside of a TableCell, as shown in their docs here, as seen below:
Here is my current approach:
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import {
Grid,
IconButton,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Paper,
Menu,
MenuItem,
Button,
} from '#material-ui/core';
import { ExpandLess, ExpandMore } from '#material-ui/icons';
const styles = theme => ({});
const Questions = ({ data, classes, openMenu, anchorEls, handleClose }) => {
const CustomTableCell = withStyles(theme => ({
head: {
backgroundColor: theme.palette.common.black,
color: theme.palette.common.white,
},
body: {
fontSize: 14,
},
}))(TableCell);
const formatData = rawData => Object.keys(rawData).map(key => rawData[key]);
const n = { key: 'hi', rating: 55, text: 'wassup' };
return (
<Grid container alignItems={'center'} direction={'column'} spacing={8}>
<Paper className={classes.root}>
<Button
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={e => openMenu('dude', e)}
>
<ExpandMore />
</Button>
<Menu
id={`dude`}
key="menu"
anchorEl={anchorEls.dude}
open={Boolean(anchorEls.dude)}
onClose={e => handleClose('dude', e)}
>
<MenuItem onClick={e => handleClose('dude', e)}>Delete</MenuItem>
<MenuItem onClick={e => handleClose('dude', e)}>Flag</MenuItem>
<MenuItem onClick={e => handleClose('dude', e)}>
Mark Answered
</MenuItem>
</Menu>
<Table className={classes.table}>
<TableHead>
<TableRow>
<CustomTableCell>Question</CustomTableCell>
<CustomTableCell numeric>Rating</CustomTableCell>
<CustomTableCell>Upvote</CustomTableCell>
<CustomTableCell>Downvote</CustomTableCell>
<CustomTableCell>Options</CustomTableCell>
</TableRow>
</TableHead>
<TableBody>
<TableRow className={classes.row} key={n.key}>
<CustomTableCell component="th" scope="row">
{n.text}
</CustomTableCell>
<CustomTableCell numeric>{n.rating}</CustomTableCell>
<CustomTableCell>
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={() => ''}
>
<ExpandLess />
</IconButton>
</CustomTableCell>
<CustomTableCell>
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={() => ''}
>
<ExpandMore />
</IconButton>
</CustomTableCell>
<CustomTableCell>
<Button
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={e => openMenu(n.key, e)}
>
<ExpandMore />
</Button>
<Menu
id={`simple-menu-${n.key}`}
key="menu"
anchorEl={anchorEls[n.key]}
open={Boolean(anchorEls[n.key])}
onClose={e => handleClose(n.key, e)}
>
<MenuItem onClick={e => handleClose(n.key, e)}>
Delete
</MenuItem>
<MenuItem onClick={e => handleClose(n.key, e)}>dude</MenuItem>
<MenuItem onClick={e => handleClose(n.key, e)}>choc</MenuItem>
</Menu>
</CustomTableCell>
</TableRow>
</TableBody>
</Table>
</Paper>
</Grid>
);
};
Questions.propTypes = {
data: PropTypes.object.isRequired,
anchorEls: PropTypes.object.isRequired,
classes: PropTypes.object.isRequired,
openMenu: PropTypes.func.isRequired,
handleClose: PropTypes.func.isRequired,
};
Questions.defaultProps = {};
export default withStyles(styles)(Questions);
This is working, however the menu is not appearing in the correct place, even when passing in the related event e. I put in a dummy element before the table to test whether or not the table was affecting the menu, and found that the dummy worked just fine, as you can see in the screenshot below.
And the improperly placed menu from the button in the table:
Any ideas on what could be happening? Obviously, the context and anchorEl isn't correctly identifying the location on the page, but not sure what to do to combat that.
Problem in your openMenu function.You need to set the event target in your openMenu function, and set null in handleClose function. I am giving here a little example which may help you:
class DemoList extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
anchorEls: []
}
}
handleActionClick = (id, event) => {
let { anchorEls } = this.state;
anchorEls[id] = event.target;
this.setState({ anchorEls });
}
handleActionClose = (id, event) => {
let { anchorEls } = this.state;
anchorEls[id] = null;
this.setState({ anchorEls });
}
render() {
let { classes } = this.props;
const { data, anchorEls } = this.state;
return (
<Paper className="main">
<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">Action</TableCell>
</TableRow>
</TableHead>
<TableBody>
{data.map(row => (
<TableRow key={row.id}>
<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">
<IconButton
aria-label="more"
aria-controls="long-menu"
aria-haspopup="true"
onClick={e => this.handleActionClick(row.id, e)}
>
<MoreVert />
</IconButton>
<Menu
id={row.id}
anchorEl={anchorEls[row.id]}
keepMounted
open={Boolean(this.state.anchorEls[row.id])}
onClose={e => this.handleActionClose(row.id, e)}
>
<MenuItem onClick={e => this.handleActionClose(row.id, e)}> View Details </MenuItem>
<MenuItem onClick={e => this.handleActionClose(row.id, e)}> Assign </MenuItem>
</Menu>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Paper>
)
}
}
After hacking away I found that removing the CustomTableCell declaration and changing all the CustomTableCells to normal TableCells resolved the problem. 😫 Not sure why that would fix it given that the CustomTableCell code was pulled straight from the Demos page.