I'm working on an application using MaterialUI that embeds in other pages using an iFrame. Whenever we show a component that renders into a Popover (both MUI select and menu components do this), the iFrame scrolls / jumps position as the popover opens.
Here is an example where this happens.
https://erehd6.csb.app
Happens in Chrome and Firefox, but not Safari.
To reproduce, you will need to scroll the iFrame off the top of the page. Then the popover demo buttons will exhibit this behavior, or the overflow menus to access code sources off the site (github/stackblitz/copy JS source/copy TS source):
<!doctype html>
<html lang="en">
<body style="margin: 0;">
<div style="height: 1000px;"></div>
<iframe src="https://v4.mui.com/api/popover/" style="border: 0; width: 100vw; height: 100vh;"></iframe>
<div style="height: 1000px;"></div>
</body>
</html>
How do I prevent this scrolling/jump from happening? The Popper component does not have this same issue, but I can't figure out how to replace the Popover with the Popper in a way that doesn't require a library fork (and a huge amount of re-testing our application). This happens in all versions of MUI btw, but I couldn't put the current site into an iFrame.
I tried replacing the transition component of Grow with Fade but that did not help. I'm still trying to pinpoint the exact issue, it's somewhere in the positioning code for the Popover.
Adding the disableAutoFocus={true} property to any Popover or any other component relying on Popover should prevent any scrolling weirdness. You might need to use disableEnforceFocus as well, depending on what behavior you desire. These are props from the Modal component (of which Popover is a "child" of): https://mui.com/material-ui/api/modal/
I just ran into a similar problem when using a <Menu /> in an iframe, which when opened, caused the browser to scroll the page and focus on the iframe. In the case of this <Menu />, I also had to add a autoFocusItem: false property to the MenuListProps, so these are the relevant props for me:
<Menu
MenuListProps={{
autoFocusItem: false,
}}
disableAutoFocus
/* other props */
>
{/* MenuList components */}
</Menu>
Adding to what #amitt said. In order to allow for both accessibility and to fix the menu jumping you can in addition to disableAutoFocus and autoFocusItem: false, you can manage your own focus state.
By using a ref, you can control the focus on enter.
const onFocus = () => {
ref?.current?.children[2].children[0].firstChild.focus();
};
<Menu
MenuListProps={{
autoFocusItem: false,
}}
disableAutoFocus
ref={ref}
TransitionProps={{
onEntered: onFocus,
}}
/* other props */
>
{/* MenuList components */}
</Menu>
Related
I am looking for someone to help me with the following issue:
I am trying to implement a modal system where the modals are dependent on the route using NextJS.
So say I am on the following page:
website.com/help
and say I have the modals category1, category2, and category3, all of which I would prefer to be able to persist after closing them (they are loading iframes), however this is not a must.
Then I would want the routes for these modals to be:
website.com/help/category1
website.com/help/category2
website.com/help/category3
I have already found this in the official NextJS examples:
with-route-as-modal
But the problem I am having is that with queryString routing, when you reload/visit the modal route directly, the modal wont be used. So the content will become full screen (it is a page of its own).
With dynamic routing, this is not an issue, since if you visit the modal route directly the modal will be kept. But the problem I am having with this is that these modals aren't exactly modals, the original page "behind" the modal just becomes the body background. The other issue is that I wont be able to persist these.
Here is the demo on stackblitz:
https://stackblitz.com/github/vercel/next.js/tree/canary/examples/with-route-as-modal?file=README.md
I would appreciate anyone helping me with this so much because it has really driven me mad trying to solve this!
I have a solution that you may like. For the example you provided you only need to import <Grid/> in /article/[articleId].js like it's done in /index.js
so when you open the modal the articles/posts list is shown in the background
// article/[articleId].js
import Grid from '../../components/Grid';
<>
<Modal
isOpen={true}
onRequestClose={() => router.push('/', undefined, { scroll: false })}
contentLabel="Post modal"
>
<Article id={articleId} pathname={router.pathname} />
</Modal>
<Grid />
</>
don't forget to give scroll false in router.push() and <Link/> so when you go back to articles/posts list it doesn't jump to the top of the page
router.push('/', undefined, { scroll: false })
<Link
key={index}
href="/article/[articleId]"
as={`/article/${id}`}
scroll={false}
>
<a className={styles.postCard}>{id}</a>
</Link>
you can see an example here: https://stackblitz.com/edit/github-rkrovd
I have a website with a body long enough to be scrollable, and on a button click I add a modal overlay with createPortal in _document.js:
<body>
<Main />
<NextScript />
<div id="modal-root"></div>
</body>
adding the modal works fine, but the backdrop covers 100vh not 100% of the body, even though it is styled as height: 100%.
in dev tools I see that the modal-root height is 0, and my backdrop somehow assumes height: 100vh, even though the body element's height is more than that. (I tried adding height: 100% to modal_root in globals.css but it didn't work)
I remade the simple popover example on this page except using mouse hover props:
https://codesandbox.io/s/eager-dewdney-yt3v-yt3vb
<Button
variant="contained"
onMouseEnter={e => setAnchorEl(e.currentTarget)}
// onMouseLeave={() => setAnchorEl(null)}
>
As soon as onMouseLeave is uncommented the above code sandbox will break silently. The UI will appear fine but the popover will not display. I have found the same to happen in my actual project.
Commenting onMouseLeave will at least allow onMouseEnter to work correctly with the popover, but it is then stuck on screen.
Am I not using onMouseLeave correctly here?
If it is being used correctly but appears to be a library related bug, what mouse based alternative could be used in place of onMouseLeave above?
I ran into the same issue, and I did not want to use a Tooltip.
The fix is to add a style to the popup to suppress the additional onMouseLeave() event that gets fired right after the popup opens:
<Popover
style={{ pointerEvents: 'none' }}
>
{children}
</Popover>
I found the fix here.
I'm currently having troubles with the material-ui drawer (https://material-ui-next.com/ one)
When I open the mini variant, the collapse inside my menu resets (closes because of the "remount").
However I would like them to persist their current state (open/closed).
Does Anyone know a way to achieve this?
The drawer:
<Drawer type="permanent"
classes={{paper: classNames(classes.drawerPaper, !this.state.open && classes.drawerPaperClose)}}
open={this.state.open}>
<div className={classNames(classes.drawerInner)}>
<Navigation updateTitle={this.updateTitle}/>
</div>
</Drawer>
The navigation component: https://pastebin.com/webdmLXp
Rendered with open collapse:
After clicking the burger button:
Sure Mr who I share my lastname with, you need to keep state of each <MenuItem>. Just add an onClick to each menuItem and toggle the state in your component. This does not really relate to Material-ui, but is more a general React question.
I am trying out Drawer component of Google's material UI library. I have it configured and working but the problem is, it's overlapping on the underlying navigation bar on my web app. Is there a way I can have the drawer functionality of this library restricted to {entireScreen - topNavigationBar} area?
Here is what my render function returns
<div>
<NavBar />
<div>
<Button onClick={this.handleLeftOpen}>Menu</Button>
<Drawer
open={this.state.open.left}
onRequestClose={this.handleLeftClose}
onClick={this.handleLeftClose}>
{leftDrawerList}
</Drawer>
</div>
</div>
If you look at Drawer styles, you'll see that it has z-index: 1300 property. So I suppose you should give your Navbar z-index that is greater than 1300