Render the Material UI Menu as <nav> and its children as <a>? - reactjs

I've a Material UI Menu, redered as <div> > <ul> <li> by default. I wanto to turn it into a <div> <nav> <a>.
I'm able to change MenuItem and turn into a <a>. But I can't find a way to turn the <ul> into a <nav>, there is no component prop. Material UI is so customizable that I'm sure I'm missing something and it's my mistake.
<Menu open={true}>
<MenuItem component='a'>Profile</MenuItem>
<MenuItem component='a'>Settings</MenuItem>
<MenuItem component='a'>Logout</MenuItem>
</Menu>
I can also use the MenuList API as child of Menu, but it only render another level below the <ul>.

MenuList inherits the props from the List component which you can use to change the ul to a nav
<Menu
open={true}
MenuListProps={{
"aria-labelledby": "basic-button",
component: "nav"
}}
>

Related

How to get a specific ListItem from Menu Component in MUI for React

I have a List that has several ListItems which I get from an array, let's call it myCollection. Each ListItem has a MenuIcon which opens a Menu, showing an option to delete the item. The simplified code looks like this:
<List>
<Menu
open={Boolean(anchorEl)}
anchorEl={anchorEl}
...>
<MenuItem onClick={() => handleDelete(⚡collectionItem)}>Delete</MenuItem>
</Menu>
{myCollection.map((collectionItem) => (
<ListItem secondaryAction={
<IconButton onClick={(e) => setAnchorEl(e.currentTarget)}>
<MoreVertIcon />
</IconButton>
}>
{collectionItem.name}
</ListItem>
)}
</List>
My problem is that I need to know which item in myCollection was selected to pass it to the delete function. However, I don't have a reference to the current collectionItem in the myCollection array from the Menu. Moving the Menu into the map function makes no sense because multiple menus would be rendered. How can I solve this problem?
I solved it by giving the IconButtons custom HTML data attributes, e.g.
<IconButton data-collection-id={collection.id} onClick={...}>
...
</IconButton>
Then, in the onClick function, we can retrieve it by using event.currentTarget.getAttribute("data-collection-id").

How to wrap different components like <ol> with <Typography> when using MDX with MUI (NextJS)

Is there any way to wrap different MDX elements like ol or li in MUI components to get the MUI theme styles?
The MDX gives out plain HTML without any styles;
<ol>
<li>One</li>
</ol>
MUI does have a <List> component, but I just want the font to match the which can be done by wrapping <li> in <Typography> like
<ol>
<li><Typography>One</Typography></li>
</ol>
Right now my MDX component looks like
<MDXRemote {...source} components={{
wrapper: Container,
p:Typography
}}
/>
Is there any weird map method to wrap all items of a list in a custom component or is there an easy way to wrap all text in <Typography>? I'm trying to minimize most possible code from .md files.
After some time, solved it using props.
<MDXRemote {...source} components={{
ol: (props:any) => <Typography gutterBottom component="ol">{props.children}</Typography>,
}} />

how to set MenuItem be <a> inside <li>

I'm new to Material-UI.
I want to make some popover and saw Menu and MenuItem in Menu Demo. They are perfect except for one thing. I want the menu item to be link.
By default MenuItem is li, (I think) because Menu is ul. But it is not so hard that make an item link by setting component="a" or component={RouterLink}. But in this case, they lose li.
For Screen Reader like program, I heard that using an appropriate Html tag is important. (It is called Semantic Web. Right?)
In this perspective, I want to menu items to be a inside li.
-- Trial --
<MenuItem>
<a>link text</a>
</MenuItem>
a is inside li. But it looks different from the case below.
<MenuItem component="a" href="/">
link text
</MenuItem>
Then, I try
<MenuItem>
<a style={{all: "inherit"}}>
link text
</a>
</MenuItem>
It looks satisfactory, but the color changes after clicking it, (I think) because a:visited { color: ...; } is not inherited.
Finally I tried
<li>
<MenuItem component="a" href"/">
link text
</MenuItem>
</li>
Yeah. It solves all problems. But, what if I forget to add li?
My menu contains many items. Some of them are link and others are not. Then should I add li directly by distinguishing only the cases of link?
-- Question --
Is my solution is best? How about you? Please share your way.
Semantically, MenuItem is more suitable for li because it represents an item in a list. The component props's default value of MenuItem is 'li' after all. So I'd go for:
<MenuItem>
<a style={{all: "inherit"}}>
link text
</a>
</MenuItem>
But, what if I forget to add li?
That's one reason to use React, you can easily de-duplicate part of your components by creating another component and reuse it:
const useStyles = makeStyles((theme) => ({
root: {
"& a:visited": {
color: theme.palette.primary.main
}
}
}));
function MenuItemLink({ children, href, onClick }) {
const classes = useStyles();
return (
<MenuItem onClick={onClick} className={classes.root}>
<a href={href}>{children}</a>
</MenuItem>
);
}
<Menu
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
>
<MenuItemLink href="/" onClick={handleClose}>
Profile
</MenuItemLink>
<MenuItemLink href="/" onClick={handleClose}>
My account
</MenuItemLink>
<MenuItemLink href="/" onClick={handleClose}>
Logout
</MenuItemLink>
</Menu>
Live Demo

How to use Next.js Link (next/link) component in a Material UI Menu?

I have a Material ui menu in the following way:
<Menu id="simple-menu" anchorEl={anchorEl} keepMounted open={!!anchorEl} onClose={handleClose}>
<MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>Log Out</MenuItem>
</Menu>
and want to use next.js Link tags with theMenuItem. What is the best way to do this?
I tried the following things:
The following doesn't render the <a> tag, but adds href to the <li> tag.
<Link href={'#'} passHref><MenuItem onClick={handleClose}>Log Out</MenuItem></Link>
I could add a prop to MenuItem to render <a> instead of <li> tag, however, since the menu is nested under <ul> tag, I'm not sure if having <ul><a>Log Out</a></ul> is a good idea
The following throws an error
<MenuItem onClick={handleClose} component={<Link href={'#'}>Log Out</Link>}></MenuItem>
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
As mentioned in the other answer giving the Link tag within the MenuItem tag will work as required and for the styling issues you have to give a textDecoration: 'none' and color: '#000' within the Link tag to avoid the underline and blue color of the Link text.
<MenuItem>
<Link
style={{
textDecoration: 'none',
color: '#000'
}}
href="#"
>
Log Out
</Link>
</MenuItem>
This is what worked for me that also disables "the regular blue link with underline in the menu":
<MenuItem>
<Link href="#">
<a
style={{
textDecoration: 'none',
color: '#000'
}}
>
Profile
</a>
</Link>
</MenuItem>
You can put "Link" component inside the "MenuItem" component.
<MenuItem><Link href="#">Log Out</Link></MenuItem>
Add a tag inside link
<MenuItem onClick={handleClose} component={<Link href={'#'}><a>Log Out</a></Link>}></MenuItem>
Component prop inside <MenuItem> is The component used for the root node. Either a string to use a HTML element or a component. which defauts to <li> as material-ui official says.
If you want to keep <ul><li></li></ul> structure, you have to nest your <Link> component inside of <MenuItem> component.
So this may be what you wanted.
<MenuItem onClick={handleClose}>
<Link href={'#'}>Log Out</Link>
</MenuItem>
See if the below code fix this, Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object:
const ALink = ({ url, linkText }) => <Link href={url}>
<a>{ linkText }</a>
</Link>
<MenuItem onClick={handleClose} component={ ALink({url: '#', linkText: 'Log Out') }></MenuItem>
I think the other answers actually have it backward, which disrupts the normal layout of the MenuItem. (For me, the menu items became taller and the text was too low.) This worked much better, and doesn't require trying to restyle the <a> tag yourself:
<NextLink href="#" passHref>
<MenuItem component="a">
Log Out
</MenuItem>
</NextLink>

React Bootstrap Typeahead children onclick

I have been using a Premade React-Bootstrap Typeahead search-bar found here
I am having trouble accessing the child elements from the dropdown. I want to be able to have an Onclick function for the dropdown items, but it doesn't seem like I have access to the children.
Below is the code I have currently, where I use typeahead
<div className="search-bar">
<Typeahead
id="sample"
options= {cities.map((city) => (city))}
labelKey="name"
placeholder="Enter a city"
/>
</div>
How do I get an onclick for these element cities listed?
You can customize menu rendering and behavior by using the renderMenu prop:
<Typeahead
options={options}
renderMenu={(results, menuProps) => (
<Menu {...menuProps}>
{results.map((result, index) => (
<MenuItem
onClick={() => console.log('click!')}
option={result}
position={index}>
{result.label}
</MenuItem>
))}
</Menu>
)}
/>
With the menu items exposed, you're free to add your own onClick handler to each item. Here's a working example.

Resources