-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
9662 bug a11y focus popup content #9774
9662 bug a11y focus popup content #9774
Conversation
The solution in marker.js is good but while testing my changes I found that the bug it tries to prevent happens also when popup content is focused. It's just less likely to happen - user opens a popup containing a focusable element, then zooms in so that it's outside of the view and then tabs until the element in popup received focus. So instead of duplicating the logic in popup I moved it to map.js. Just as a small reminder about what that bug is about: When user tab-focuses an element that's outside of the map view, browser scrolls to make it visible which moves the canvas fully/partially outside of the view.
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.
Hi @watofundefined thanks for the contribution and for your patience. I think overall this seems pretty good. I noticed one situation where the focus seems to not work like I'd expect though.
- Open a popup by either clicking on the marker or by focusing it and pressing Enter.
- Click elsewhere on the map to close the popup.
- Repeat step 1 (can be the same marker or a different one).
- Note that there's no focused element in the popup.
- Press tab and notice that the second focusable element comes into focus.
It seems like this code will need to clear or reset the focus when a popup is closed.
I also wanted to add functionality that re-focuses the previously focused element when popup is closed.
I think this is unnecessary, at least in this PR.
Q1: Should consumers be able to disable this auto-focus functionality?
I'd say yes.
Q2 Should Esc close the opened popup? Currently it doesn't.
Let's leave this out of this PR.
@@ -477,7 +483,7 @@ export default class Popup extends Evented { | |||
this._update(event.point); | |||
} | |||
|
|||
_update(cursor: PointLike) { | |||
_update(cursor: ?PointLike) { |
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.
Did something change that made it necessary to make this a Maybe type?
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.
I should have annotated this - it's called without any arguments several times even within this file, so I couldn't resist fixing the type. I can revert it, to make the commit more focused!
src/ui/popup.js
Outdated
// This approach isn't covering all the quirks and cases but it should be good enough. | ||
// If we would want to be really thorough we would need much more code, see e.g.: | ||
// https://github.com/angular/components/blob/master/src/cdk/a11y/interactivity-checker/interactivity-checker.ts | ||
const selectors = [ |
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.
There's no need to allocate this array every time this function is called. We could move it out of the function. You could also save it as a querySelector
to avoid join
ing the array every time.
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.
I moved it to the top-level scope - leaving the [item1, item2].join(', ')
for better readability.
Let me know if that's fine with you - I can change it to whatever you think works better with the codebase.
I did try one long string, but it was around 190 characters which is just too much, and two strings on two lines didn't look right to me.
Hey @ryanhamley, no problem at all, thanks for feedback! I've added a new popup option As for the bug, I can replicate the behaviour you describe in both Firefox and Chrome. Here's what I tried (without luck):
Any ideas? Cheers! |
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.
👋 @watofundefined apologies on the delay here. @ryanhamley asked me to give this a second set of eyes.
Weirdly, I can't reproduce the bug you and @ryanhamley ran into. If document.activeElement
is still in focus correctly and its the browsers default handling of outline
I personally think it's fine as is.
This overall looks great! Just had some small requests in the tests. Let's get this merged!
test/unit/ui/popup.test.js
Outdated
const popup = new Popup({closeButton: true}) | ||
.setHTML(` | ||
<button disabled>No focus here</button> | ||
<span tabindex="0" data-testid="abc">Test</span> |
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.
For variance, Mind changing this to a different focusable element? like a contenteditable region or a:href?
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.
It seems contenteditable
is not supported by jsdom so I used select
. But while playing around with contenteditable
I've noticed that the selector is wrong - hence the update in popup.js
.
I tested it locally and it works as expected for these variants:
<div contenteditable>...</div>
(focuses)<div contenteditable="true">...</div>
(focuses)<div contenteditable="false">...</div>
(doesn't focus)
👋 @watofundefined just checking in. This looks close! are you able to make these remaining adjustments? |
Hey @tristen, sorry for the delay, it totally slipped my mind. |
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.
@watofundefined awesome! I'll let @ryanhamley hit the merge but these changes look great to me
Hi folks - this is my first PR - so please shout if I missed some of your rules or anything!
Launch Checklist
PR for bug: #9662
Commit b546e09
Q1: Should consumers be able to disable this auto-focus functionality?
Q2 Should Esc close the opened popup? Currently it doesn't.
Otherwise it's nothing big - for better keyboard UX I changed the order in which elements in popup are added to DOM - this shouldn't change anything since the close button has
position: absolute;
.I also wanted to add functionality that re-focuses the previously focused element when popup is closed.
But it was getting messy in one scenario:
document.activeElement
would be eithernull
ordocument.body
.Both popup's removal (popup.js) and popup's appearing (marker.js) are driven by map's "click" event and I don't see an easy way how to ensure they happen in different order.
So I was thinking that the marker could pass a reference to its DOM element to the popup instance.
Then popup would focus it when it'll be closed - but only if popup was closed by the 'x' button - i.e. not by clicking on another marker.
It' an easy change and it would give users good keyboard UX. (I'm happy to add it to the PR if you think it's a good idea)
Commit 068d10d
I added a lot of details to the commit message but I don't know mapbox-gl-js that well so please think if there are some valid cases when map's container is scrolled (it's the
div
with id "mapid")