Dialog within Popover in React HeadlessUI - javascript

I have a navbar that uses HeadlessUI's Popover on mobile for the hamburger menu. By default, this menu closes when you click out/focus on an element that is not in it.
Now I'm trying to add a modal (HeadlessUI Dialog) that I want to open when clicking on a button that is in the popover menu. The modal is used within a ModalButton component definition (<><button><dialog></>). This is done for separation of concerns (everything relating to the modal is within ModalButton).
The issue is: when I'm in the navbar's popover menu and click on the button to open the dialog. The browser focuses on this new dialog, and so the popover loses focus, making it close. Since it closed, the button (and thus the dialog sibling) are no longer rendered, and so the dialog disappears instantly.
For reference, this is a pseudocode of the react tree:
<navbar>
<popover>
<> {/* "ModalButton" containing both the button and the dialog */}
<button /> {/* Button that opens the dialog */}
<dialog /> {/* This uses a portal internally (with HeadlessUI) */}
</>
</popover>
</navbar>
I can think of a few ways to solve this but neither are very good:
Pull the modal higher in the tree, outside of the popover/hamburger menu but still inside the navbar. But that breaks separation of concerns since the navbar now has to worry about the open state of the modal.
Put the modal somewhere at the top of the tree, and use some kind of global state (requires a state management library) to handle the opening of the modal.
Maybe there's a way to prevent the Popover from closing when focusing on the dialog? (but still allow it to close when focusing anything that is not the dialog)
I'd love to hear any ideas on fixing this issue.

You should put the dialog higher up in the tree. Usually these can go at the page level, or even the app level, depending on how global these dialogs are.
You can then use your favourite global state manager or the useContext hook to tell these dialogs to open programmatically from anywhere in your app.
In which case the popover closing automatically shouldn't be an issue anymore.

Related

Vuetify v-btn in v-bottom-navigation stays active when not pressed

I am having an issue with a button in my bottom nav in Vuetify. I have defined a bunch of buttons as routes (I am using Nuxtjs), so a button will be active if I am on that page.
There is a button in the bottom nav bar that is not a route though, and it exists to activate a v-navigation-drawer. When I click this button, the drawer slides out fine. When I click outside the drawer to get it to slide back, though, both the button of the page I am on and the button that activates the drawer are active. I want the button that activates the drawer to never be active.
Visual--
I am on the app's home page.
Here is what the bottom nav looks like before clicking the button for the v-navigation-drawer
Here is what the bottom nav looks like after I close the v-navigation-drawer. The button furthest to the right is what activates the navigation drawer, and it should never remain active after I close the drawer. Yet it does, and I need to click another button to deactivate it.
I have tried many things suggested on other questions here to fix this, including but not limited to the following: defining a custom active class for this button, using the exact prop, and using a watcher to change the value of the active value in the nav bar. None of these things have worked.
Here is the code where the issue is. I have removed the irrelevant buttons, keeping only the home and profile buttons (the profile is the problem one which stays active undesirably):
<v-bottom-navigation
app
fluid
grow
color="primary"
class="d-flex d-sm-none"
>
<v-btn
value="home"
to="/home"
nuxt
exact
>
<v-icon>
mdi-home
</v-icon>
</v-btn>
<v-btn
value="profile"
exact
#click="showProfileNavDrawer"
>
<v-icon>
mdi-account-circle
</v-icon>
</v-btn>
</v-bottom-navigation>
The profile button stays active after I close the navigation drawer it opens (while another button is active at the same time due to vue's router) and I can only deactivate the profile button by clicking another button.
How might I eliminate this issue?
EDIT
I think I figured out what was wrong, for any people coming from a search engine. It appears to be a limitation of Vuetify's bottom nav. It is intended specifically for navigation and since this button was on the bottom nav bar without an assigned route (all it did was slide out a navigation drawer, no route change), it associates itself with the current route temporarily until another route is navigated to and the bottom navigation bar's state is modified. This interpretation may be wrong, but it seems to match the behavior.
I was unable to fix the problem though due to my low knowledge of CSS (first time building a website, my day job is mostly data engineering) so I just changed my webapp's design. Apologies for anyone who was hoping for an answer :(
#genp according to the docs for v-btn, exact will match every route if passed in as a prop but no route specified. Could your btn be matching for the current route and so triggering it's active state? How about you remove the exact prop from the button in the nav. Does anything change?
Docs here.

React Material UI: How to prevent the Popover component overlay the screen when Menu component opens?

I am using Menu component to show up a menu when user hovers on an element. However, I noticed that when the menu opens up it also contains a Popover component that opens up and covers the entire screen as an overlay thus preventing interaction with the screen. I do not want the overlay to open when I open the Menu component. How can I achieve this?
The Menu Component inherits from Popover, which inherits from Modal. So all Modal props are also available to Menu. The ones that might help you;
hideBackdrop: If true, the backdrop is not rendered.
disableScrollLock: Disable the scroll lock behavior.
More at https://material-ui.com/api/modal/ .

Dropdown closes on clicking scrollbar of dropdown in a modal but works fine when used outside of a modal in React?

I created an autocomplete component and when I use it in modal, the dropdown closes on clicking scrollbar(onBlur event of input gets fired) but when I use the same component outside of modal, scrolling with scrollbar works fine.
I am not able to find the cause of the issue. So please help!!
This is the code-sandbox link
(To reproduce the issue first use scrolling with scrollbar on main page, then open add item modal and use second input field in it which is same autocomplete component).

Mobile screen reader issue for react based applications

I am working on react based application for mobile screen readers. Use case is that for a dialog with menu items, I have to keep one button to dismiss the dialog, which will be on top of the dialog. I have to set it's tabIndex to 0 to make it accessible, which results in dismiss button being the first focusable item.
Expectation is that after screen reader lands focus on first menu item, the dismiss button should be accessible. How to approach this problem?
I have tried the following:
<Dialog open={this.state.menuOpen} className="hide-default-dialog-container"
content={<div className="actionsheet-view-bg" onClick={() => {
this.setState({menuOpen: !this.state.menuOpen})
}}> {this.getDismissButtonForActionSheet()}
<div className="actionsheet-view-container"> {this.getActionSheetItems()} </div>
</div>}
/>
You have four options here:-
Option 1. Move the close button to the end of the dialog next to any save / update buttons.
The 'x' in the top right of dialog boxes is expected, but there is no reason why you can't add a close button at the bottom of your dialog instead, placed after any save / update buttons.
So in the footer of your dialog have
<button>Save</button> <button>Cancel</button>
You could still maintain the 'x' in the top right but use aria-hidden="true" on it without a tabindex.
Option 2. Change the button to be after the content in the DOM but use absolute positioning to place it visually
You could move the close button after the content (in the DOM) and then use CSS to position it in the top right of the dialog.
This is a bit of a grey area on whether it would constitute a logical tab order but I think it is still accessible enough to be a good pass.
Option 3. Manage the focus programatically.
When the dialog opens you could just programatically focus the first item. Just make sure you trap focus within your dialog so the close button can be reached.
Option 4. Do nothing
This is the best option
There is nothing wrong with the close button being the first focusable item (in fact it may be preferential to allow users who accidentally open the dialog to close it quickly).
Screen reader users are used to exploring dialogs and so it won't impact them if the first focusable item is the close button. It may even be reassuring to know that you have provided an accessible close button (as many developers forget about keyboard users and make buttons inaccessible to close dialogs).
Semantics, Escape and Focus Management.
To further increase the accessibility change your close div into a button as that way you don't have to worry about adding tabindex as it is semantically correct. (I have assumed the close button is a div as you said you added a tabindex="0" - if it is already a button then the tabindex is not needed)
Secondly make sure the dialog can be closed using the Escape key as that is expected behaviour.
Finally ensure focus remains confined to the modal (not just via the tab key, you need to add aria-hidden="true" to all the content outside of the dialog while it is open as screen reader users navigate with more than just the tab key, in fact it is a secondary way of navigating, screen reader users navigate by headings 1-6, links, forms, buttons etc.).
This may require structuring your page to make it easier
For example:-
<header aria-hidden="false"></header>
<main aria-hidden="false"></main>
<footer aria-hidden="false"></footer>
<dialog aria-hidden="true"></dialog>
would turn into the following when the dialog is open (and reverted when the dialog is closed).
<header aria-hidden="true"></header>
<main aria-hidden="true"></main>
<footer aria-hidden="true"></footer>
<dialog aria-hidden="false"></dialog>

Semantic UI Modal dialog not closing properly

I'm using Semantic UI (http://semantic-ui.com/) as the front end CSS library and also AngularJS. In my application, I used modals to manage data input and views. There's a problem. I am opening and closing modals using following commands.
$('#addNewEpisodeModal').modal('show');
$('#addNewEpisodeModal').modal('hide');
But sometimes when I close the modal, it's not closing properly. Modal dialog closing, but there's a black dimmer (background) not closing. And I will show you how it's working. This is my modal when I open it.
And this is the view when I close that modal. The dimmed background is not closing. Can anyone tell me what's the problem here ?
I have to refresh the page in order to make it working again. What's the reason for this ?
I encountered that problem once and my solution was to hide the dimmer manually together with the modal:
$('#addNewEpisodeModal').modal('hide').modal('hide dimmer');
I can't comment yet, so without more clarification from you (e.g. example code), this is nothing more than a guess on my part. However, here are my thoughts:
Semantic UI manipulates your .modal element by extracting it from wherever you had it, wrapping it in a .modals element, and inserting that element into the body of your page. This happens when .modal('show') is first called, and not before.
Once your modal is hidden, the .modals element receives a new class of hidden, which will hide the black overlay. My assumption is that you have overwritten this .hidden class, or the styles that it gives to the element. I would try looking at the CSS in Chrome's developer tools, and seeing if this is the case.

Categories