Material-UI - Click on Menu Button opens all Menu Items available - reactjs

I'm having a small issue with Material-UI. And it is that everytime I click on a button to open an specific dropdown/menuItem it opens all the Dropdown/Items available in the AppBar. I did change the 'open' variable name, but it just gives me an error instead. The Material-UI documentation does not includes examples two or more dropdown menus.
Here's my code:
class MaterialTest extends Component {
state = {
anchorEl: null
};
handleClick = event => {
this.setState({ anchorEl: event.currentTarget });
};
handleClose = () => {
this.setState({ anchorEl: null });
};
render() {
const { anchorEl } = this.state;
const open = Boolean(anchorEl);
return (
<div style={mainDiv}>
<AppBar position="static" color="default" style={barStyle}>
<Toolbar style={toolStyle}>
<NavLink to="/">
<Button>Home</Button>
</NavLink>
<Button
aria-owns={anchorEl ? 'product-shipping' : null}
aria-haspopup="true"
onClick={this.handleClick}
>
Product Shipping
</Button>
<Menu
id="product-shipping"
anchorEl={anchorEl}
open={open}
onClose={this.handleClose}
>
<NavLink to="viewAll">
<MenuItem onClick={this.handleClose}>View Latest SKUs</MenuItem>
</NavLink>
<NavLink to="addSku">
<MenuItem onClick={this.handleClose}>Add New SKU</MenuItem>
</NavLink>
<MenuItem onClick={this.handleClose}>Import / Export</MenuItem>
<MenuItem onClick={this.handleClose}>Tables</MenuItem>
</Menu>
<Button
aria-owns={anchorEl ? 'inventory' : null}
aria-haspopup="true"
onClick={this.handleClick}
>
Inventory
</Button>
<Menu
id="inventory"
anchorEl={anchorEl}
open={open}
onClose={this.handleClose}
>
<NavLink to="viewInventory">
<MenuItem onClick={this.handleClose}>Site Inventory</MenuItem>
</NavLink>
<MenuItem onClick={this.handleClose}>
Warehouse Inventory
</MenuItem>
<MenuItem onClick={this.handleClose}>Add New Inventory</MenuItem>
</Menu>
<Button
aria-owns={anchorEl ? 'vendor-information' : null}
aria-haspopup="true"
onClick={this.handleClick}
>
Vendor Information
</Button>
</Toolbar>
</AppBar>
</div>
);
}
}
Any Ideas? Thank you

It might be because all the menus are being opened when anchorEl is set. The open prop is only checking for Boolean(anchorEl) and they all share this same state (so whenever it returns true they all open)
You could try setting an anchorEl2, anchorEl3 and so on in the state and change each Menu and Button accordingly.

Related

Close submenu when clicked on other submenu

I am using antd menu and submenu. and i am unable to close the submenu when other sumbenu is opened. I am using react js for the code.I am able to close main menu on other menu expansion. but have no idea how to close submenu on other submenu expansion. Here is my code
<Menu
style={{ width: 220 }}
mode="inline"
openKeys={openKeys}
onOpenChange={onOpenChange}
selectedKeys={[location.pathname]}
title="Navigation"
className="custom-menu"
>
{navigationConfig.map((menu) => {
return (
<>
{menu.submenu.length > 0 ? (
<SubMenu
icon={menu.icon ? <Icon type={menu?.icon} /> : null}
key={menu.key}
title={menu.title}
>
{menu.submenu.map((submenu) => {
return (
<Menu.Item
key={submenu.key}
onClick={submenu.onclick}
>
{submenu.icon ? (
<Icon type={submenu?.icon} />
) : null}
{submenu.title}
{submenu.path ? (
<Link
to={{
pathname: submenu.path,
state: history.location.state,
}}
onClick={() => {
setHeaderTitle(
menu.hasOwnProperty("aliasTitle")
? menu.aliasTitle
: menu.title
);
}}
/>
) : null}
</Menu.Item>
);
})}
</SubMenu>
</Menu.Item>
)}
</>
);
})}
</Menu>

Open a modal (or dialogue) from a dropdown asking the user if they would like to take an action

I would like to open a modal (or dialogue) when a user selects an option from a dropdown menu.
Eventually there will be a few options in the dropdown, and different dialogues/modals will be called and rendered depending on which dropdown option has been clicked. For now - how do I get the modal/dialogue to open with dropdown menu events?
I'm currently using the handleClose handler to attempt to open a dialogue since that event should be easy to grab and use right out of the docs for MUI Menu and MenuItem.
The origination call to the DropdownMenu component (which works well and shows the dropdown menu) occurs in a table through the click of an icon
<DropdownMenu options={['Show modal or dialogue to the user']}>
<MoreHorizIcon />
</DropdownMenu>
The DropdownMenu component (also working well itself, except for not triggering the dialogue/modal) looks like this
interface IProps extends Omit<unknown, 'children'> {
children: any;
options: string[];
}
const ITEM_HEIGHT = 48;
const DropdownMenu = ({ children, options }: IProps) => {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const showModal = () => {
return (
<AlertDialog />
)
}
const handleClose = () => {
//the native alert dialogue works well
alert('I want to show a dialog or modal the same way this alert shows up and ask the user if they are sure they want to delete something')
//why isn't the custom alert dialog being called?
showModal();
setAnchorEl(null);
};
return (
<div>
<IconButton
aria-label="more"
id="more-button"
aria-controls="long-menu"
aria-expanded={open ? 'true' : undefined}
aria-haspopup="true"
onClick={handleClick}
>
{children}
</IconButton>
<Menu
id="dropdownmenu"
MenuListProps={{
'aria-labelledby': 'more-button'
}}
anchorEl={anchorEl}
open={open}
onClose={handleClose}
PaperProps={{
style: {
maxHeight: ITEM_HEIGHT * 4.5,
width: '20ch'
}
}}
>
{options.map(option => (
<MenuItem key={option} onClick={handleClose} >
{option}
</MenuItem>
))}
</Menu>
</div>
);
};
export default DropdownMenu;
And the example starter dialogue I am using to get the ball rolling looks like this
const AlertDialog = () => {
const [open, setOpen] = React.useState(true);
const handleClose = () => {
setOpen(false);
};
return (
<div>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{"Sweet Filler Dialog"}
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Are you sure?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>NO</Button>
<Button onClick={handleClose} autoFocus>
YES
</Button>
</DialogActions>
</Dialog>
</div>
);
}
You can use a state variable to trigger the modal in the DropdownMenu component, whenever you wanted to show the dialog/modal.
const [isModalOpen, setIsModalOpen] = React.useState(false);
and then in the handleClose, you can update the modal state to open the modal.
const handleClose = () => {
setIsModalOpen(true)
setAnchorEl(null);
};
Then somewhere in your JSX of DropdownMenu, you can conditionally render the AlertDialog component like this
{isModalOpen ? <AlertDialog open={isModalOpen} setOpen={setIsModalOpen} /> : null}
Finally, update your AlertDialog component to use props to handle the closing of the modal.
const AlertDialog = ({ open, setOpen }) => {
const handleClose = () => {
setOpen(false);
};
return (
<div>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{"Sweet Filler Dialog"}
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Are you sure?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>NO</Button>
<Button onClick={handleClose} autoFocus>
YES
</Button>
</DialogActions>
</Dialog>
</div>
);
}

How to send card id to the menu items using reactjs?

My objective is to pass the id for handleClick function, If i click on the 3 dots of the respective card then it should reflect particular card id. Can anyone help me in giving id for handle Click arrow function.
Here is the code:
state = { menu: null };
handleClick = (e) => {
this.setState({ menu: e.target });
};
handleClose = () => {
this.setState({ menu: null });
};
{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}
/>
))}
here is the sample one
You can bind the function and pass the id to it. Like
handleClick = (id, e) => {
console.log(id)
this.setState({ menu: e.target, id: id });
};
<IconButton
id="simple-menu"
className={classes.showIcon}
aria-label="settings"
aria-controls="simple-menu"
onClick={this.handleClick.bind(this, user.id)}
>
<MoreVertIcon />
</IconButton>
or instead of binding the function you can create a new arrow function and pass the id like this:
<IconButton
id="simple-menu"
className={classes.showIcon}
aria-label="settings"
aria-controls="simple-menu"
onClick={(e) => this.handleClick(user.id, e)}
>
<MoreVertIcon />
</IconButton>
You can save the id in states and then use it in the menu component to perform further actions
Here is a trick solution for that kind of situations. Also this is most performant solution. You won't need to create arrow function instance for every re-render or each React node.
<IconButton
id="simple-menu"
className={classes.showIcon}
aria-label="settings"
aria-controls="simple-menu"
onClick={this.handleClick}
data-user-id={user.id} // Define your user id as native data attribute
>
And then get data attribute of your current clicked element.
handleClick = (e) => {
const { userId } = e.currentTarget.dataset; // Get your data-user-id value as string
console.log(Number(userId));
this.setState({ menu: e.target, id: Number(userId) }); // Convert to number and save to state
};
So you can create your own scenarios up to your depends.
In this case, also you might want to learn event bubbling logic to understand the difference between target and currentTarget use case.
Also you can check it out for more information about data-* attributes.
https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes

How to fix shadow for menu material-ui

When I use a few icon menus than box-shadow looks very dark. How to fix that?
1:
Codesandbox example https://codesandbox.io/embed/flamboyant-tdd-r83u1
<div>
{items.map((item, index) => {
return (
<Fragment key={index}>
<IconButton
aria-owns={open ? "long-menu" : undefined}
onClick={this.handleClick}
>
<MoreVertIcon />
</IconButton>
<Menu anchorEl={anchorEl} open={open} onClose={this.handleClose}>
{options.map(option => (
<MenuItem key={option} onClick={this.handleClose}>
{option}
</MenuItem>
))}
</Menu>
</Fragment>
);
})}
</div>
Because, actually you are triggering multiple menus with the same flag at the same time. So shadow is dark because there are multiple menus one after the other.
Below code should fix this, You don't have to render Menu in items loop
render() {
const items = [...Array(10).keys()];
const { anchorEl } = this.state;
const open = Boolean(anchorEl);
return (
<div>
{items.map((item, index) => {
return (
<Fragment key={index}>
<IconButton
aria-owns={open ? "long-menu" : undefined}
onClick={this.handleClick}
>
<MoreVertIcon />
</IconButton>
</Fragment>
);
})}
<Menu anchorEl={anchorEl} open={open} onClose={this.handleClose}>
{options.map(option => (
<MenuItem key={option} onClick={this.handleClose}>
{option}
</MenuItem>
))}
</Menu>
</div>
);
}

React Material UI Delete Selected Cells

I am using a table example from material ui.
This is the link to the online project https://codesandbox.io/s/209kpmpvx0
The example allows you to cell multiple rows and a delete icon gets displayed when you click on the row.
I would like to be able to print out all the selected rows after the delete icon has been pressed.
This is the class that allows you to select a row.
class EnhancedTable extends React.Component {
constructor(props) {
super(props);
this.state = {
selected: [],
}
handleClick = (event, id) => {
const { selected } = this.state;
const selectedIndex = selected.indexOf(id);
let newSelected = [];
this.setState({ selected: newSelected });
};
I put the following code selectedId={selected.id} into the EnhancedTableHead component.
return (
<Paper className={classes.root}>
<EnhancedTableToolbar numSelected={selected.length} />
<div className={classes.tableWrapper}>
<Table className={classes.table} aria-labelledby="tableTitle">
<EnhancedTableHead
numSelected={selected.length}
selectedId={selected.id}
order={order}
orderBy={orderBy}
onSelectAllClick={this.handleSelectAllClick}
onRequestSort={this.handleRequestSort}
rowCount={data.length}
/>
Then I added the selectedId to the EnhancedTableToolbar. I also added an event handler into the delete icon.
let EnhancedTableToolbar = props => {
const { numSelected, classes } = props;
return (
<Toolbar
className={classNames(classes.root, {
[classes.highlight]: numSelected > 0
})}
>
<div className={classes.title}>
{numSelected > 0 ? (
<Typography color="inherit" variant="subheading">
{numSelected} selected
</Typography>
) : (
<Typography variant="title" id="tableTitle">
Nutrition
</Typography>
)}
</div>
<div className={classes.spacer} />
<div className={classes.actions}>
{numSelected > 0 ? (
<Tooltip title="Delete">
<IconButton aria-label="Delete">
<DeleteIcon onClick={printdeletearray} />
</IconButton>
</Tooltip>
) : (
<Tooltip title="Filter list">
<IconButton aria-label="Filter list">
<FilterListIcon />
</IconButton>
</Tooltip>
)}
</div>
</Toolbar>
);
};
I get an error saying selectedId is undefined.
const printdeletearray = () => {
console.log(this.state.selected);
};
You made two mistakes I guess:
firstly you should pass selected as selectedId props in <EnhancedTableToolbar />:
<EnhancedTableToolbar
numSelected={selected.length}
selectedId={selected}
/>
the second mistake is that you should pass selectedId to printdeletearray() like this:
<DeleteIcon onClick={() => printdeletearray(selectedId)} />
and here is your Demo

Resources