Replies: 8 comments 2 replies
-
The behavior you're seeing is due to Ariakit's handling of nested dialogs. If an element is added to the body while an Ariakit popup is open, it's automatically recognized as a nested dialog. This means it won't close the "parent" popup—in your case, the menu. See Identifying nested dialogs. This feature allows us to support portaled popups from other libraries and browser extensions that open popups, like 1Password, Google Translate, and Grammarly. If this isn't the desired behavior, you should programmatically close the menu when the dialog opens. This isn't the default behavior because you might not have control over when certain third-party nested dialogs open (browser extensions, for example). |
Beta Was this translation helpful? Give feedback.
-
Hmmm, that's going to be tricky, The menu in question has absolutely no knowledge about the dialog. Their respective states are disconnected. I'm not sure how to reliably do this in a general way, because it's not just about the particular pair of menu+dialog shown in the video.
But that menu is not the parent of that dialog at all. It is not even the menu that contains an option that could trigger that dialog by mouse.
Can this behavior be turned off or tweaked in some way? Like, say, a callback that fires when the new element is detected, and allows us to decide if the appearance of such element should be considered one of these nested dialogs or not? Also, how did this work before, in the older Ariakit version I mentioned? Can you point me to the code that made the menu be closed when a dialog appeared? |
Beta Was this translation helpful? Give feedback.
-
I found a workaround doing this on the <Menu
onBlur={(event) => {
if (!event.relatedTarget) return
if (event.currentTarget.contains(event.relatedTarget)) return
if (event.relatedTarget.getAttribute('role')?.startsWith('menu')) return
store.hide()
}}
>
{/* menu items go here */}
</Menu> That is, if the menu looses focus to something external to it that is not another menu1, we hide it. This seems to be working as expected based on a quick assessment in all relevant situations that I've tried so far. Footnotes
|
Beta Was this translation helpful? Give feedback.
-
I'm also considering the following modification to the code above: <Menu
onBlur={(event) => {
if (!event.relatedTarget) return
if (event.currentTarget.contains(event.relatedTarget)) return
- if (event.relatedTarget.getAttribute('role')?.startsWith('menu')) return
+ if (event.relatedTarget.closest('[role^="menu"]')) return
store.hide()
}}
>
{/* menu items go here */}
</Menu> Just in case the newly focused element is inside a menu, while not being a menu itself. |
Beta Was this translation helpful? Give feedback.
-
This was modified as part of a larger update to support third-party nested dialogs. Here's the original PR: #2339
Yes, that's why I put it in quotes. The thing is, this dialog could be a third-party popup rendered with React Portal or a browser extension. Technically, they would behave the same: a new element appended to the body.
There's no prop to control that behavior right now, but we could introduce one if it makes sense. But I wonder if it's really an issue in your case. If it's just because the menu appears in front of the dialog, it seems like a |
Beta Was this translation helpful? Give feedback.
-
I thought about the Although I'm still hesitant about a menu staying open in the background. That's not how menus work in most apps that have them. Here's one example in Google Docs: CleanShot.2024-03-07.at.11.24.35.mp4Same with GitHub (pressing CleanShot.2024-03-07.at.11.28.37.mp4 |
Beta Was this translation helpful? Give feedback.
-
Those are fair considerations. FWIW, the workaround shown above does not hide the menu when triggering a tool that takes screenshots (Cleanshot). It does not dismiss the menu when clicking outside the browser viewport in any way (e.g. when clicking in another browser tab or when clicking in another window that's not from the browser, like clicking in macOS menu bar or wherever). It does not even dismiss it if you right-click on the menu to click "Inspect" to check its DOM structure in the browser dev tools. Not that I planned it intentionally to work that way. I'm just finding that it is nice enough to not be that aggressive. I'm still evaluating it. And I also understand if something like that should not make it to be a feature in Ariakit. But as a workaround it is working nicely for now. |
Beta Was this translation helpful? Give feedback.
-
I had to remove the previously mentioned workaround. It proved to be a problem in some cases where we have a menu item that opens nested sub-menu-like popups that are not sub-menus. And we found no reliable way to make a condition that would detect all the exceptions. So, in the end, the original issue mentioned here (the menu overlapping the dialog) was solved by solving the z-index issue, which is something that we needed to do anyway. I still think that closing the menu is the desired default behavior (e.g. see how it is also what Slack does): CleanShot.2024-03-08.at.12.54.57.mp4But I cannot speak for the use cases that apparently prompted this change of behavior in Ariakit (e.g. the ones mentioned in this comment above). I still wish there was a way to control this or turn it off. I'd even be open to contribute this change if pointed in the right direction of what needs to be done. Thanks for all your help so far @diegohaz. |
Beta Was this translation helpful? Give feedback.
-
Context: I'm working on upgrading from
ariakit
2.0.0-next.43 to@ariakit/react
.The thing is, our app has keyboard shortcuts that can open a dialog. So it can happen that you have a menu expanded, then you press the keyboard shortcut, and without having interacted with the menu, a dialog appears, and you still want your menu to close.
TBH, this is something we never thought of, and were taking for granted. I now realize that, indeed, someone's gotta turn the menu's open state off, and I wonder how was that happening, because I also cannot find it in the ariakit code of the old version we're upgrading from.
Before the upgrade, menus close automatically when a dialog appears
When using ariakit 2.0.0-next.43 (the one we’re upgrading from), menus automatically close when a dialog opens.
I just checked that it happens automatically without doing nothing. I put a console.log on
state.open
of the menu, and when I open it and then trigger a modal via a keyboard shortcut, the menu closes.CleanShot.2024-03-06.at.18.42.32.mp4
After the upgrade, this no longer happens
However, when using @ariakit/react (with all the code adapted to use the new ariakit state management hooks based on stores), this no longer happens. And the result is a menu from behind the dialog, visible on top of the dialog.
CleanShot.2024-03-06.at.18.55.10.mp4
Beta Was this translation helpful? Give feedback.
All reactions