enter image description here
I want to implement react material ui tooltip with customizes tooltip component .
which will use tooltip ,list item,list component from react material ui
I tried using list item
Install MUI dependencies
cd into project root directory and run npm install #mui/material #emotion/react #emotion/styled and npm install #mui/icons-material
Import the required components
import { Box, List, Tooltip, ListItem, ListItemButton, ListItemIcon, ListItemText, Divider } from '#mui/material';
import MailIcon from '#mui/icons-material/Mail';
import ModeIcon from '#mui/icons-material/Mode';
import * as React from 'react';
Write the List component
From https://mui.com/material-ui/react-list/ and https://mui.com/material-ui/react-tooltip/\
The two first item have a tooltip here : "Inbox" and 'Drafts"
export default function BasicList() {
return (
<Box sx={{ width: '100%', maxWidth: 360, bgcolor: 'background.paper' }}>
<List>
<Tooltip title="Inbox">
<ListItem disablePadding>
<ListItemButton>
<ListItemIcon>
<MailIcon />
</ListItemIcon>
<ListItemText primary="Inbox" />
</ListItemButton>
</ListItem>
</Tooltip>
<Tooltip title="Drafts">
<ListItem disablePadding>
<ListItemButton>
<ListItemIcon>
<ModeIcon />
</ListItemIcon>
<ListItemText primary="Drafts" />
</ListItemButton>
</ListItem>
</Tooltip>
</List>
<Divider />
<List>
<ListItem disablePadding>
<ListItemButton>
<ListItemText primary="Trash" />
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton component="a" href="#simple-list">
<ListItemText primary="Spam" />
</ListItemButton>
</ListItem>
</List>
</Box>
);
}
Related
i am building a ecommerce web app with react and i am unable to set state profileOptionsLayer when i click on the highlighted listitem both the logs are displayed on the console but component dosent rerender and state "profileOptionsLayer" is not updated either ,i am unable to locate the reason need help!
ALL imports .....
const TemporaryDrawer = ({
profileDrawerlayer,
setprofileDrawerlayer,
setuserDetails,
userDetails,
}) => {
const [profileOptionsLayer, setprofileOptionsLayer] = useState();
console.log(profileOptionsLayer);
return (
<>
<Drawer
anchor={"right"}
open={profileDrawerlayer}
onClose={() => {
setprofileDrawerlayer(false);
}}
>
<Box
sx={{ width: 250, background: "lightblue", height: "100%" }}
role="presentation"
onClick={() => {
setprofileDrawerlayer(false);
}}
onKeyDown={() => {
setprofileDrawerlayer(false);
}}
>
<List>
////////////////////////////////////// Below ///////////////////////////////////////////////
<ListItem disablePadding>
<ListItemButton
onClick={() => {
console.log("dsadsa");
setprofileOptionsLayer("View"); <= unable to set this state
console.log(profileOptionsLayer);
console.log("dsadsa");
}}
>
<ListItemIcon>
<VisibilityIcon />
</ListItemIcon>
<ListItemText primary={"View Profile"} />
</ListItemButton>
</ListItem>
/////////////////////////////////////// UP /////////////////////////////////////////
<ListItem
disablePadding
onClick={() => {
localStorage.removeItem("userId");
setuserDetails("");
}}
>
<ListItemButton>
<ListItemIcon>
<LogoutIcon />
</ListItemIcon>
<ListItemText primary={"Logout"} />
</ListItemButton>
</ListItem>
</List>
<Divider />
</Box>
</Drawer>
{profileOptionsLayer &&<ProfileOptions {...{ userDetails }} />}
</>
);
};
export default TemporaryDrawer;
I was trying to have two action buttons on the left and right end of the list component.
on click of secondary action (right side delete icon) the ripple is limited to the only icon.
on click of primary action(left delete icon) the ripple effect is on the whole row.
Expected Behaviour :
I want the ripple effect on the primary, similar to that of the secondary action button.
And important I cannot disable the text ripple effect as temporary solution.
Code Sample:
Code-Sandbox link
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemIcon from "#material-ui/core/ListItemIcon";
import ListItemText from "#material-ui/core/ListItemText";
import ListItemSecondaryAction from "#material-ui/core/ListItemSecondaryAction";
import DeleteIcon from "#material-ui/icons/Delete";
import IconButton from "#material-ui/core/IconButton";
const useStyles = makeStyles((theme) => ({
root: {
width: "100%",
maxWidth: 360,
backgroundColor: theme.palette.background.paper
}
}));
export default function SelectedListItem() {
const classes = useStyles();
const [selectedIndex, setSelectedIndex] = React.useState(1);
const handleListItemClick = (event, index) => {
setSelectedIndex(index);
};
return (
<div className={classes.root}>
<List component="nav" aria-label="main mailbox folders">
<ListItem
button
selected={selectedIndex === 0}
onClick={(event) => handleListItemClick(event, 0)}
>
<ListItemIcon>
<IconButton edge="end" aria-label="delete">
<DeleteIcon />
</IconButton>
</ListItemIcon>
<ListItemText primary="Inbox" />
<ListItemSecondaryAction>
<IconButton edge="end" aria-label="delete">
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
<ListItem
button
selected={selectedIndex === 1}
onClick={(event) => handleListItemClick(event, 1)}
>
<ListItemIcon>
<IconButton edge="end" aria-label="delete">
<DeleteIcon />
</IconButton>
</ListItemIcon>
<ListItemText primary="Drafts" />
<ListItemSecondaryAction>
<IconButton edge="end" aria-label="delete">
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
</List>
</div>
);
}
I think it's because is used to add secondary action to button so when you click on the secondary action area it prevent primaryAction to happen. So in your case when you click on right icon it contains the ripple affect inside ListItemSecondaryAction area. If you want to disable ripple on the List you can add prop 'disableRipple' on your ListItem and it will be disabled but if you want it conditional ie. when user clicks on icon ripple should happen only on icon and if clicked on button only in button than you can try stop propagation when clicked on button ( might not work ) but you can give it a try.
I've created a work around sharing codesandbox link with you
https://codesandbox.io/s/material-demo-forked-i7k7e?file=/demo.js
<ListItem
button
disableRipple
selected={selectedIndex === 0}
onClick={(event) => handleListItemClick(event, 0)}
style={{ position: "relative" }}
>
<div style={{ zIndex: 1 }}>
<ListItemIcon>
<IconButton edge="end" aria-label="delete">
<DeleteIcon />
</IconButton>
</ListItemIcon>
<ListItemText primary="Inbox" />
<ListItemSecondaryAction>
<IconButton edge="end" aria-label="delete">
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
</div>
<ButtonBase
style={{
position: "absolute",
bottom: 0,
top: 0,
left: 0,
right: 0,
width: "100%",
zIndex: 0
}}
/>
</ListItem>
I am using Material-UI for my React project and having issues with getting the drawer to function properly when I add in nested list items. Everything was working great until I added those in. I believe the root cause is that by clicking on the dropdown menu and changing the state of the list item to be open I'm causing the application to re-render. Not sure how to solve this.
Problem: Nested list items close the drawer automatically when you click the top level. The user then has to open the drawer again to see the list items in the drop down.
Desired Functionality: The user clicks the menu item to open the drawer. The user can click on "Leadership Triad" and see the menu items within while the drawer stays open. When the user clicks off, the drawer closes.
Code Sandbox
https://codesandbox.io/s/material-ui-nested-menu-forked-qqdiv
My Code
import React, { useState } from "react";
import { makeStyles } from "#material-ui/core/styles";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import Typography from "#material-ui/core/Typography";
import IconButton from "#material-ui/core/IconButton";
import MenuIcon from "#material-ui/icons/Menu";
import Drawer from "#material-ui/core/Drawer";
import List from "#material-ui/core/List";
import Divider from "#material-ui/core/Divider";
import ListItem from "#material-ui/core/ListItem";
import ListItemIcon from "#material-ui/core/ListItemIcon";
import ListItemText from "#material-ui/core/ListItemText";
import InboxIcon from "#material-ui/icons/MoveToInbox";
import HomeIcon from "#material-ui/icons/Home";
import AccountCircle from "#material-ui/icons/AccountCircle";
import ExitToAppIcon from "#material-ui/icons/ExitToApp";
import ExpandLess from "#material-ui/icons/ExpandLess";
import ExpandMore from "#material-ui/icons/ExpandMore";
import PeopleIcon from "#material-ui/icons/People";
import BusinessIcon from "#material-ui/icons/Business";
import Menu from "#material-ui/core/Menu";
import MenuItem from "#material-ui/core/MenuItem";
import { Link as RouterLink } from "react-router-dom";
import Collapse from "#material-ui/core/Collapse";
const useStyles = makeStyles(theme => ({
root: { flexGrow: 1 },
menuButton: { marginRight: theme.spacing(2) },
title: { flexGrow: 1 },
list: { width: 250 },
nested: { paddingLeft: theme.spacing(4) },
}));
const Header = () => {
const [drawerOpen, setDrawerOpen] = useState(false);
const [anchorEl, setAnchorEl] = useState(null);
const [leadershipTriadMenuOpen, setLeadershipTriadMenuOpen] = useState(false);
const handleLeadershipTriadClick = () => {
setLeadershipTriadMenuOpen(!leadershipTriadMenuOpen);
};
const handleClick = event => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const toggleDrawer = () => {
setDrawerOpen(!drawerOpen);
};
const classes = useStyles();
return (
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<IconButton
edge="start"
className={classes.menuButton}
color="inherit"
aria-label="menu"
onClick={() => toggleDrawer()}
>
<MenuIcon />
<Drawer
anchor="left"
open={drawerOpen}
onClose={() => toggleDrawer()}
>
<div className={classes.list}>
<List>
<ListItem button component={RouterLink} to="/">
<ListItemIcon>
<HomeIcon color="primary" />
</ListItemIcon>
<ListItemText primary="Home" />
</ListItem>
<ListItem
button
onClick={() => handleLeadershipTriadClick()}
>
<ListItemIcon>
<HomeIcon color="primary" />
</ListItemIcon>
<ListItemText primary="Leadership Triad" />
{leadershipTriadMenuOpen ? (
<ExpandLess />
) : (
<ExpandMore />
)}
</ListItem>
<Collapse
in={leadershipTriadMenuOpen}
timeout="auto"
unmountOnExit
>
<List component="div" disablePadding>
<ListItem button className={classes.nested}>
<ListItemIcon>
<HomeIcon />
</ListItemIcon>
</ListItem>
</List>
</Collapse>
<ListItem button>
<ListItemIcon>
<InboxIcon color="primary" />
</ListItemIcon>
<ListItemText primary="Testing" />
</ListItem>
<ListItem button>
<ListItemIcon>
<InboxIcon color="primary" />
</ListItemIcon>
<ListItemText primary="Testing" />
</ListItem>
</List>
<Divider />
<List>
<ListItem
button
component={RouterLink}
to="/admin/companies"
>
<ListItemIcon>
<BusinessIcon color="primary" />
</ListItemIcon>
<ListItemText primary="Companies" />
</ListItem>
<ListItem
button
component={RouterLink}
to="/admin/users"
>
<ListItemIcon>
<PeopleIcon color="primary" />
</ListItemIcon>
<ListItemText primary="Users" />
</ListItem>
</List>
<Divider />
<List>
<ListItem button component={RouterLink} to="/profile">
<ListItemIcon>
<AccountCircle color="primary" />
</ListItemIcon>
<ListItemText primary="Profile" />
</ListItem>
<ListItem button component={RouterLink} to="/logout">
<ListItemIcon>
<ExitToAppIcon color="primary" />
</ListItemIcon>
<ListItemText primary="Logout" />
</ListItem>
</List>
</div>
</Drawer>
</IconButton>
<Typography variant="h6" className={classes.title}>
Leadership Program
</Typography>
<IconButton color="inherit" onClick={handleClick}>
<AccountCircle />
</IconButton>
<Menu
id="admin-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
>
<MenuItem
component={RouterLink}
to="/profile"
onClick={handleClose}
>
Profile
</MenuItem>
<MenuItem
onClick={handleClose}
component={RouterLink}
to="/logout"
>
Logout
</MenuItem>
</Menu>
</Toolbar>
</AppBar>
</div>
);
};
export default Header;
Have you tried surrounding the IconButton with a ListItemSecondaryAction?
<ListItemSecondaryAction>
<IconButton onClick={handleOnClick}>
{openState ? <ExpandLess /> : <ExpandMore />}
</IconButton>
</ListItemSecondaryAction>
I was trying to do something similar, where a list item had two clickable areas with two different actions: click the ListItem (to go to a page) or click the IconButton (to expand the ListItem) to display nested ListItems. Without this ListItemSecondaryAction, when I would click on a list item, it would both route and expand the ListItem, which was not desirable.
Adding the SecondaryAction separated click actions of the ListItem and the IconButton. The documentation isn't great, so it took me a while to understand the purpose, but it's here.
I've been at this for a while. So I have some nested navigation json that I am using. The top level navigation is loading fine (the nav.map) once I move further down the rabbit hole I find myself not returning the top level or the sub level navigation. Everything compiles successfully. Am I just missing it?
return(
<List component="nav" className={classes.root}>
{nav.map(function(element) {
<ListItem
button
onClick={handleClick}
id={element.toplevel}
key={element.toplevel}
>
<ListItemText primary={element.toplevel} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItem>;
return element.children.map(function(child) {
return (
<Collapse timeout="auto" unmountOnExit>
<List component="div" disablePadding>
<ListItem button className={classes.nested}>
<ListItemIcon>
<StarBorder />
</ListItemIcon>
<ListItemText primary={child.name} />
</ListItem>
</List>
</Collapse>
);
});
})}
</List>
);
Make sure that your map callback functions actually return the jsx code. Your return statements are not set right.
One way that is commonly used in react-land to make the jsx code more readable is the arrow function syntax. This way you get rid of the return statements and return the whole function body (it's just syntactic sugar).
Next thing: be aware of your closing tags. I just assumed that your list item of the element object closes after the ListItemText tag and that your second map function opens a new list item after your element ListItem. jsx only lets you return one root tag at a time. This is why (as the comment below has suggested) using an empty <> ... </> tag pair as a root element will solve this issue.
return (
<List component="nav" className={classes.root}>
{data.nav.map((element) => (
<>
<ListItem
button
onClick={handleClick}
id={element.toplevel}
key={element.toplevel}
>
<ListItemText primary={element.toplevel} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItem>
{
element.children.map((child) => (
<Collapse timeout="auto" unmountOnExit>
<List component="div" disablePadding>
<ListItem button className={classes.nested}>
<ListItemIcon>
<StarBorder />
</ListItemIcon>
<ListItemText primary={child.name} />
</ListItem>
</List>
</Collapse>
))
}
</>
))}
</List>
);
Sandbox link: https://codesandbox.io/s/material-demo-uv7c7?fontsize=14&hidenavigation=1&theme=dark
Try this, i have restructured it a bit.
<List component="nav" className={classes.root}>
{nav.map(function(element) {
return(
<ListItem
button
onClick={handleClick}
id={element.toplevel}
key={element.toplevel}
>
<ListItemText primary={element.toplevel} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItem>
{element.children.map(function(child) {
return (
<Collapse timeout="auto" unmountOnExit>
<List component="div" disablePadding>
<ListItem button className={classes.nested}>
<ListItemIcon>
<StarBorder />
</ListItemIcon>
<ListItemText primary={child.name} />
</ListItem>
</List>
</Collapse>
)
})
}
}))}
</List>
);
Currently Using React with Material UI v1.0 in implementing a list but I don't want to repeat my code.
The existing Code looks like this.
import List from 'material-ui/List';
import DashboardIcon from 'material-ui-icons/Dashboard';
import BuildIcon from 'material-ui-icons/Build';
import Listings from './BarComponents';
function SideBar() {
return (
<div>
<List>
<ListItem button>
<ListItemIcon>
<DashboardIcon />
</ListItemIcon>
<ListItemText primary="Dashboard" />
</ListItem>
<ListItem button>
<ListItemIcon>
<BuildIcon />
</ListItemIcon>
<ListItemText primary="Control Panel" />
</ListItem>
</List>
</div>
);
}
export default SideBar;
I want to get avoid repeating creating the list items so i've created a new file and passed the props into, code is below.
import React from 'react'
import { ListItem, ListItemIcon, ListItemText } from 'material-ui/List';
export default function Listings(props) {
return(
<div>
<ListItem button>
<ListItemIcon>
<props.icon />
</ListItemIcon>
<ListItemText primary={props.prim} />
</ListItem>
</div>
);
}
And also this
<Listings icon={DashboardIcon} prim="Dashboard" />
<Listings icon={BuildIcon} prim="Build" />
Into the original file for a replacement of
<ListItem button>
<ListItemIcon>
<DashboardIcon />
</ListItemIcon>
<ListItemText primary="Dashboard" />
</ListItem>
<ListItem button>
<ListItemIcon>
<BuildIcon />
</ListItemIcon>
<ListItemText primary="Control Panel" />
</ListItem>
Is the best way to pass a component e.g though
and call it via Thanks in advance.
You can use a dynamic component.
renderElement(name, props = {}) {
var MyComponent = name
return <MyComponent {...props} />;
}
render() {
return(
<div>
<ListItem v-for="item in list" key={item.id} button={item.button}>
<ListItemIcon>
{renderElement(props.icon)}
</ListItemIcon>
<ListItemText primary={props.prim} />
</ListItem>
</div>
);
}