-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
fix(a11y): maintain focus when uploading an image #12746
Conversation
I am not sure what sort of automated tests you would expect to see for code like this, so I would appreciate some guidance on that |
@@ -152,6 +153,16 @@ class ImageEdit extends Component { | |||
captionFocused: false, | |||
} ); | |||
} | |||
|
|||
if ( this.state.autoFocus && this.container ) { | |||
this.setState( { |
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.
Rather than setting this in state, could the focusing just occur as part of the logic for onSelectImage
?
The issue with this is that setState
will force a render, and is generally discouraged to be incorporated into componentDidUpdate
, since the nature of the lifecycle method is such that it means we'll have a minimum of two renders (when ideally it'd just be one max per "change") and leaves open the potential for an infinite loop (since this will re-call componentDidUpdate
).
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.
The problem here is that the DOM elements do not yet exist when onSelectImage gets called. The if
statement is intended to stop the infinite loop.
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.
Another option could be to assign this value as an instance property, so that we don't need to call setState
here. It's a bit more fragile / non-standard, but specifically avoids the unneeded render.
Effectively, the difference would be:
this.setState( {
isEditing: false,
} );
this.autoFocusCaption = true;
if ( this.autoFocusCaption && this.myRef.current ) {
this.autoFocusCaption = false;
// Or:
// delete this.autoFocusCaption;
const caption = this.myRef.current.querySelector( 'figcaption' );
// ...
this.setState( { | ||
autoFocus: false, | ||
} ); | ||
const caption = this.container.querySelector( 'figcaption' ); |
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.
Ideally we'd just assign the intended focus target as the ref
. This is blocked by #9740. It seems like it'd be reasonable enough for RichText
to use forwardRef
to make the underlying element available for reference.
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 agree that having a mechanism for managing focus more globally would be good and could eventually replace this individual fix
@@ -338,6 +350,10 @@ class ImageEdit extends Component { | |||
} ) ); | |||
} | |||
|
|||
setContainer( ref ) { |
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'd encourage using createRef
for this instead, largely because it requires less code to implement.
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 can make this change, initially it did not work for me but I realize that was due to the fact that I was trying to use the <Fragment>
which does not work at all.
252c83a
to
d4b30d6
Compare
d4b30d6
to
9ed39fd
Compare
@@ -1,3 +1,4 @@ | |||
/* global React */ |
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.
We should never reference any globals, nor use React directly.
The creatRef
function is available from the @wordpress/element
package.
The line below further down in the file:
import { Component, Fragment } from '@wordpress/element';
...can be changed to:
import { Component, Fragment, createRef } from '@wordpress/element';
@@ -152,6 +153,16 @@ class ImageEdit extends Component { | |||
captionFocused: false, | |||
} ); | |||
} | |||
|
|||
if ( this.state.autoFocus && this.container ) { | |||
this.setState( { |
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.
Another option could be to assign this value as an instance property, so that we don't need to call setState
here. It's a bit more fragile / non-standard, but specifically avoids the unneeded render.
Effectively, the difference would be:
this.setState( {
isEditing: false,
} );
this.autoFocusCaption = true;
if ( this.autoFocusCaption && this.myRef.current ) {
this.autoFocusCaption = false;
// Or:
// delete this.autoFocusCaption;
const caption = this.myRef.current.querySelector( 'figcaption' );
// ...
I guess this is something that was discussed in person at WCUS? 🙂Wasn't immediately clear what is the goal of this PR and how to test it. Had a quick look and I'm not sure focus should be moved to the caption. I'd tend to think moving focus shouldn't be used to assume a specific work flow. Entering a caption is only one of the available tasks for users after they've uploaded an image. For example, users might want to:
Also, focusing the caption would force keyboard users to tab backwards to be able to access any other action other than inserting a caption. Instead, I'd consider to move focus to the block focusable container: this way all the block UI is available in the natural tab order: |
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.
See comment.
@afercia argument in favor of placing the focus in the caption is that it encourages the creation of the text alternative but if you are insistent on the outer block focus, lmk |
Focus should be managed programmatically only to "repair" a focus flow in a way that is as close as possible to the native interaction. Not to assume a specific flow. A bit surprisingly, but very welcomed, this is also what the React accessibility guidelines suggest as a general recommendation:
|
What's the current status of this pull request? Are there plans for revisions? Is the direction clear? The notes of my previous review are still valid.
There was no prior conversation. My understanding is that it improved a scenario where otherwise a focus loss would occur. |
Hi There! I've triaging PRs today and it seems this one is stale. I'm going to close now but please consider reopening if you have some time to dedicate to it. Thanks for your efforts. |
Description
Changed the image block to maintain focus when uploading an image
How has this been tested?
Tested this in the browser (Chrome) only and also tested with Voiceover on OS X
Types of changes
Introduces the concept of an "autoFocus" state to the Image Block component, Introduces a ref to the same component, focuses the figcaption when the autoFocus state is set
Bug fix
Checklist: