-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Added closeOnEsc, hideHeaderClose & enforceFocus #247
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Added option for disabling close on ESC (prop: closeOnEsc, defaults to true) and switched to using @keyup.esc handler. Added option to hide header close button (prop: hideHeaderClose, defaults to false) Added in the enforceFocus handler to make sure focus never leaves dialog while it is open. This provides better ARIA compliance as some screen readers (and browsers) will let you tab through document even though a dialog is open. Also added aria-labeledby and aria-describedby attributes, as well as switched the modal header and footer to semantic elements for better ARIA accessibility.
lib/components/modal.vue
Outdated
document !== e.target && | ||
this.$refs.content && | ||
this.$refs.content !== e.target && | ||
!this.$refs.content.contains(e.target) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing a closing )
Missing closing bracket on line 206
@tmorehouse Seems good, Thanks. |
I'm having issues using the escape key to close a modal opened with I've tried calling this manually with a synthetic event (since the method expects an event), but no luck. I also tried attaching a |
This seems relevant to this thread, but should I open an issue for this? |
@alexsasharegan Thanks for report. It would be so nice if you can create a JSFiddle / Playground and Issue reproducing same problem so we can work on it easier :) |
Yeah, if you can generate a small file, I can look it over to see what might be going on (might be timing issue as to when the handlers are installed/fired) |
I don't use jsFiddle much, but hopefully you can see a working example forked straight from the docs. |
Looks like I did do that wrong. Take two! |
Thanks, I'll take a look at this a little later today. |
P.S. @tmorehouse I'm totally new to Vue transitions, but I've dealt with the timing of modal shown events before. It's inherently asynchronous, and typically comes down to a listener on |
Thanks again! Will fiddle round with this in a short while. |
And for my two cents, I would consider applying your mounted() {
this.$on('shown', this.enforceFocus);
if (this.visible === true) {
this.show();
}
}, So now you won't depend on an event object: enforceFocus(e) {
// If focus leaves modal, bring it back
// eventListener bound on document
if (this.is_visible) {
this.$refs.content.focus();
}
} You could also make it a prop option, but I think modals should always acquire focus once they're active. @tmorehouse, sorry if I'm blowing you up right now! I'm in the middle of a work project using this, so I'm in the thick of it. I respect your time! Get to it whenever you have a chance of course! |
We just want to make sure if a keyboard (or screen reader) user tabs outside of the modal root element that the modal body is focused, and that the user can still tab around the contents of the modal. So we must listen on the |
Ah, that's certainly something I hadn't considered. Sounds like my use case falls outside of the current control flow though. Maybe you can start with a check for whether or not the event object is present to determine it's not a document/body event. |
I'll definitely tackle the |
I believe I have a fix now for the Also, rather than focusing the modal content on open, it now will focus the first focusable item in the modal content (first starting in the footer, then if no element found, then checks the body, and if not found checks the header, and then if not found as a last resort focuses the modal itself). I'm going to mock up a fiddle for testing, and will update this thread later. |
And I just realized I should have been listening to |
@tmorehouse I checked in the newest release, and I'm seeing your changes in the modals. Have you fixed the Thanks for your work on all this! |
When the modal is shown, it should focus the first non-disabled focusable item (with the search for the focusable item starting in the footer and working it way into the body, then the header, else the modal itself. This also ensures that hitting ESC will close the modal. For the enforceFocus, we must listen to the body/document element for the focus events, to detect if focus has left the modal (say via hitting TAB) to another focusable element within the document. If a user tabs through the modal, it will cycle through the modal's focusable content. See current fiddle: https://jsfiddle.net/pi0/bofh9aaa/ which is using the latest release. The first focusable element found is the "close" button (in the footer) |
Cool! Yeah, I stripped my focus hack out and it works much much better! Thank you! Are you following my issue with the shown event? If you listen to |
Haven't moved the 'shown' event emit to the transition element yet. |
Okay, okay. Just making sure that piece didn't get miscommunicated. Again, thank you! I'm having fun using bootstrap-vue! Lots of little gems like alerts with timeout dismissals built in! |
The only issue I can think of regarding placing the shown emit on the transition, would be if you are using THe only way around this might be to introduce a couple of new events to be emitted, such as |
There are actually two "shown" events that are fired by the modal. One is fired on itself, and one fired on the app
The second one is more akin to the namespaced events in Bootstrap, and is available by listening to |
Currently the So there would need to be a bit of refactoring of code logic to change this detection to happen before the show transition starts. |
Yes, I can see that being a bit of work. Hopefully I can attempt some contributions. Still, I find it very important. While it's probably a UI anti-pattern, sometimes it's necessary to have a modal pop up conditionally from another modal (maybe an "are you sure?" or something with terms of use). I've found the four base events necessary to properly time the multiple modal transitions. |
Added option for disabling close on ESC (prop: closeOnEsc, defaults to true) and switched to using @keyup.esc handler.
Added option to hide header close button (prop: hideHeaderClose, defaults to false)
Added in the enforceFocus handler to make sure focus never leaves dialog while it is open. This provides better ARIA compliance as some screen readers (and browsers) will let you tab through document even though a dialog is open.
Also added aria-labeledby and aria-describedby attributes, as well as switched the modal header and footer to semantic elements for better ARIA accessibility.