MUI table icon button in a row where row is clickable - reactjs

The table rows are clickable:
<TableRow
sx={{ '&.MuiTableRow-root': { cursor: 'pointer' } }}
hover
key={row.id}
onClick={() => handleRowClick(row.id)}
>
I have an icon button in one column:
<TableCell>
<Tooltip title='Survey options'>
<IconButton
onClick={() => console.log('button clicked!')}
>
<MoreHoriz />
</IconButton>
</Tooltip>
</TableCell>
The problem is when clicking the icon button it also triggers the onClick of the entire row:
Clicking this:
onClick={() => console.log('button clicked!')}
Also triggers this:
onClick={() => handleRowClick(row.id)}
Is there a way to separate the clicking behavior? I want to be able to click each one separately.

In the IconButton onClick, modify the code like this,
onClick={(event) => {event.stopPropagation(); console.log('button clicked!');}}
The stopPropagation() method prevents propagation of the same event from being called. Propagation means bubbling up to parent elements or capturing down to child elements.

Related

Change Material UI IconButton Icon on click

I have an IconButton inside a DataGrid component. How can I pass on a state so onClick event changes the icon inside it?
<IconButton size="small" onClick={e => {
changeStateOfIcon();
otherFunction();
}} style={{transform: "rotate(35deg)"}}>
{this.someState == 'icon1'
?
<Icon1/>
:
<Icon2/>
}
</IconButton>
for only 2 possible values for your icons you can use a Boolean state
const [active,setActive]=useState(false)
<IconButton size="small" onClick={e => {
setActive(!active)
otherFunction();
}} style={{transform: "rotate(35deg)"}}>
{active ?<Icon1/> : <Icon2/>}
</IconButton>

How to make the Collapse position to bottom-right after clicking on expand icon in AntD

I am using AntD Collapse for displaying a list of items after expand icon is clicked.
I want the position of expandIcon to go to bottom-right after all the list of the data when expand icon is clicked (just like in google news), but found only two options (left|right) for 'expandIconPosition', no option for top or bottom.
How can we align the expandIcon to bottom-right, when expand icon is clicked?
Few lines from the code for reference:
<Collapse
ghost
style={{ marginTop: "-1vh" }}
expandIcon={({ isActive }) => (
<DownOutlined
style={{ marginTop: "-2vh" }}
rotate={isActive ? 180 : 0}
/>
)}
expandIconPosition="right"
>
<Panel>
<div>
{list()} //list of items
</div>
</Panel>
</Collapse>
Here's one possible solution. Make Collapse a controlled component by specifying activeKey prop and then the value of it will be based on state. Then, by tracking the activeKeys state you can now do a conditional rendering (hide and show) on icons:
const [activePanelKeys, setActivePanelKeys] = useState([]);
const handlePanelIconClick = (panelKey, makeActive) => {
if (makeActive) {
setActivePanelKeys([...activePanelKeys, panelKey]);
} else {
setActivePanelKeys(activePanelKeys.filter((aPK) => aPK !== panelKey));
}
};
<Collapse
activeKey={activePanelKeys}
expandIconPosition="right"
expandIcon={() => <DownOutlined />}
// expandIcon={(panelProps) => (
// <DownOutlined
// onClick={() => handlePanelIconClick(panelProps.panelKey, true)}
// />
// )}
onChange={e => setActivePanelKeys(e)} //if you want to click only icon, comment this and uncomment the above expandedIcon
>
<Panel
header="This is panel header 1"
key="p1"
showArrow={activePanelKeys.indexOf("p1") === -1}
>
<div>{text}</div>
{activePanelKeys.indexOf("p1") !== -1 && (
<div style={{ textAlign: "right", marginTop: 10 }}>
<UpOutlined onClick={() => handlePanelIconClick("p1", false)} />
</div>
)}
</Panel>
{/* <PanelContent header="This is panel header 2" key="p2">
{text}
</PanelContent> */}
</Collapse>;
Here is the complete sample code:
Note: I tried to make a reusable Panel component but it seems that the reveal animation were gone. You can uncomment the commented PanelContent on the code to see the difference.
Hope that I hit what you want.

Material Table inner onClick shows only last row value

So the problem I am having is that I have a custom rendered column inside which there is a menu button clicking on it open a menu like this:
Now look at below code:
columns={[
{
title: 'Actions',
field: 'tableData.id',
render: rowData => {
return (
<div>
<IconButton aria-controls="simple-menu" aria-haspopup="true" onClick={handleClick}>
<MenuIcon />
</IconButton>
<Menu
className={classes.menu}
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
>
<MenuItem className={classes.sendMail} onClick={()=>
{console.log(rowData)}}>
<ListItemIcon className={classes.icon}>
<SendIcon fontSize="default" />
</ListItemIcon>
Send Assessment Email
</MenuItem>
</Menu>
</div>
)
},
},
The values coming in from the rows i.e rowData is fine on the first onClick of IconButton component but the second onClick of MenuItem shows only the last rowData value no matter which row i select.
Any help would be appreciated. Thank you.
EDIT: I have deployed a quick fix by setting the selected row on Menubutton inside useState and then using that value for the other actions but i wanna know if that is natively or shall i say possible on default rather then the approach i took . i tried stopping event propagation but in vain.
Thanks for asking this questions and seeing that I'm not completely alone...
I am not sure if this could serve as a solution or as a work around, but it works for me.
On the button being used to open the menu, I use the onClick event to set the selected menu row which I want to use, and save it
to the component state.
const [slcRow, setSlcRow] = React.useState(null);
const handleClick = (event, row) => {
setAnchorEl(event.currentTarget);
setSlcRow(row);
};
<Button
aria-controls="simple-menu"
aria-haspopup="true"
onClick={(event ) => {handleClick(event, row) }}
>
Open Menu
</Button>
In the MenuItem onClick event I check the state to retrieve that row and do the work needed...
const handleEditOpen = (event) => {
let row = slcRow;
loadEditData(row.clientId);
setEdit(true);
setAnchorEl(null);
};
<Menu id="long-menu" anchorEl={anchorEl} keepMounted open={open} onClose={handleClose1}>
<MenuItem onClick={(event) => { handleEditOpen(event) }}>Edit </MenuItem>
</Menu

How to show only icon to the particular user when we click on it using reactjs

I'm trying to show icon when it is hovered and clicked on the particular User but here when i click on the icon of particular user then i could see for the other users also showing the icon without hovering or clicking on it. Can anyone help me in this query?
Here is code:
<Card>
{Data.map(user => (
<CardHeader
key={user.id}
className={classes.header}
avatar={<Avatar aria-label="recipe">R</Avatar>}
action={
<div className={this.state.menu && classes.menu}>
<IconButton
id="simple-menu"
className={classes.showIcon}
aria-label="settings"
aria-controls="simple-menu"
onClick={this.handleClick}
>
<MoreVertIcon />
</IconButton>
<Menu
style={{ marginTop: "35px" }}
id="simple-menu"
keepMounted
anchorEl={this.state.menu}
open={Boolean(this.state.menu)}
onClose={this.handleClose}
>
<MenuItem onClick={this.handleClose}>View</MenuItem>
<MenuItem onClick={this.handleClose}>hide</MenuItem>
</Menu>
</div>
}
title={user.title}
subheader={user.subheader}
/>
))}
</Card>
https://codesandbox.io/s/material-ui-menu-button-visibility-fix-hujx4
For ex: if i click on Shrimp and Chorizo Paella User (dot icon) then we could see same icon visible in the other user Sherlock holmes. My objective is to show the icon whenever i hover the user card, and when we click on the icon, then the icon should visible only on the respective user card but not in other User card
The problem is you are using the same state for all of your cards. You are deciding on
the basis of state.menu for all cards resulting in the same behaviour no matter on which card the event has occured. As a result if the state.menu is set to value by hovering on any card & the menu of all the cards is visible. To solve this you will need to uniquely identify your card. For that you will need to manage state for each card. You can achieve it like this:
Create a state for each card identified by user.id using the componentDidMount lifecycle hook before return the <Card> component.
let cardStates = {}
Data.map(user => {
cardStates[user.id] = { menu: null }
});
this.setState(cardStates);
...
Convert your handle events to callbacks
<Menu
style={{ marginTop: "35px" }}
id="simple-menu"
keepMounted
anchorEl={this.state[user.id].menu}
open={Boolean(this.state[user.id].menu)}
onClose={this.handleClose}
>
<MenuItem
onClick={e => {
this.handleClose(e, user.id);
}}
>
View
</MenuItem>
<MenuItem
onClick={() => {
this.handleClose(user.id);
}}
>
hide
</MenuItem>
</Menu>
Manipulate state in handlers by accepting your user.id as argument.
handleClick = (e, uid) => {
let newCardState = {}
newCardState[uid] = { menu: e.target }
this.setState(newCardState);
};
handleClose = (uid) => {
let newCardState = {}
newCardState[uid] = { menu: null}
this.setState(newCardState);
};

Prevent event propagation on row click and dialog in material ui

I'm using material-ui and I have a table with a button inside. The button opens a Dialog and I need to be able to support clicking on the table row.
The problem is that with Portals (in react) - the events are propagated, so clicking inside the Dialog (that was opened after clicking on the button) - the click event on the table-row will get fired.
This is the row:
<TableRow onClick={rowClick}>
<TableCell>Content 1</TableCell>
<TableCell>Row clicked {count} times</TableCell>
<TableCell>
<MyDialog />
</TableCell>
</TableRow>
This is the dialog:
<>
<IconButton onClick={handleClickOpen}>
<EditIcon />
</IconButton>
<Dialog disableBackdropClick open={open} onClose={handleClose}>
<DialogTitle>Dialog</DialogTitle>
<DialogContent>Some content</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handleClose}>Save</Button>
</DialogActions>
</Dialog>
</>
Here is a working example:
https://codesandbox.io/s/dazzling-hofstadter-gzwll
And this is an animated gif that shows the issue:
I know I can set the "rowClick" on each cell (and leave the last cell without it) but this is just an example and I'm looking for a more generic solution.
It took some time to find a proper solution, but the only way to prevent the propagation of the event was to add a "click" function on the dialog itself:
<>
<IconButton onClick={handleClickOpen}>
<EditIcon />
</IconButton>
<Dialog
disableBackdropClick
open={open}
onClose={handleClose}
onClick={handleDialogClick}
>
<DialogTitle>Dialog</DialogTitle>
<DialogContent>Some content</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={handleClose} color="primary">
Save
</Button>
</DialogActions>
</Dialog>
</>
And have the handleClickDialog function stop the event propagation:
const handleDialogClick = e => {
e.stopPropagation();
};
Here is a working example:
https://codesandbox.io/s/cocky-violet-19uvd
One option is to add an onClick handler within my-dialog.js.
const handleClick = e => {
e.stopPropagation();
// doesn't do anything except stop the event
};
and then add it to your Dialog:
<Dialog
disableBackdropClick
open={open}
onClose={handleClose}
onClick={handleClick}
>
Fork of your sandbox with the changes: https://codesandbox.io/s/nostalgic-chaplygin-lql5m?fontsize=14&hidenavigation=1&theme=dark
it has nothing to do it e.stopPropagation();. The dialog and edit icon is wrapped with row and row has click function.
I would ask you to check this https://codesandbox.io/s/inspiring-bassi-b9hev
I made some changes. It will open and close the dialog box.
In your existing implementation icon is part of a dialog box which is wrong. you can check my code.
Hint:
<TableBody>
<TableRow onClick={rowClick}>
<TableCell>Content 1</TableCell>
<TableCell>Row clicked {count} times</TableCell>
<TableCell>
<IconButton onClick={() => setIsOpen(true)}>
<EditIcon /> // I change it here
</IconButton>
</TableCell>
</TableRow>
</TableBody>
...
<MyDialog isOpen={isOpen} onClose={() => setIsOpen(false)} />
New DialogBox
<Dialog
disableBackdropClick
open={props.isOpen}
onClose={() => props.onClose()}
>
<DialogTitle>Dialog</DialogTitle>
<DialogContent>Some content</DialogContent>
<DialogActions>
<Button onClick={() => props.onClose()}>Cancel</Button>
<Button onClick={() => props.onClose()}>Save</Button>
</DialogActions>
</Dialog>
you need to disable row event click for action column using stopPropagation
which will fix the issue
const handlerActionColmunClick = (event: React.MouseEvent<HTMLElement>) => {
event.stopPropagation();
};
<TableBodyCell onClick={handlerActionColmunClick }>
<IconButton
aria-label="three-dot-action-menu"
aria-controls="customized-menu"
aria-haspopup="true"
onClick={handleActionMenuClick}
>
...........
...........
...........

Resources