Skip to content
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

Closed
wants to merge 1 commit into from

Conversation

dylanb
Copy link

@dylanb dylanb commented Dec 9, 2018

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:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows the accessibility standards.
  • My code has proper inline documentation.

@dylanb
Copy link
Author

dylanb commented Dec 9, 2018

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( {
Copy link
Member

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).

Copy link
Author

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.

Copy link
Member

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' );
Copy link
Member

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.

Copy link
Author

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 ) {
Copy link
Member

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.

https://reactjs.org/docs/refs-and-the-dom.html

Copy link
Author

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.

@gziolo gziolo requested a review from tofumatt December 12, 2018 08:29
@gziolo gziolo added the [Focus] Accessibility (a11y) Changes that impact accessibility and need corresponding review (e.g. markup changes). label Dec 12, 2018
@gziolo gziolo added this to the 4.8 milestone Dec 12, 2018
@gziolo gziolo added the [Type] Bug An existing feature does not function as intended label Dec 12, 2018
@gziolo gziolo requested a review from afercia December 12, 2018 08:31
@gziolo gziolo added the [Block] Image Affects the Image Block label Dec 12, 2018
@dylanb dylanb force-pushed the fix/a11y-image-focus branch from 252c83a to d4b30d6 Compare December 18, 2018 21:18
@dylanb dylanb force-pushed the fix/a11y-image-focus branch from d4b30d6 to 9ed39fd Compare December 18, 2018 21:19
@youknowriad youknowriad modified the milestones: 4.8, 4.9 Dec 19, 2018
@@ -1,3 +1,4 @@
/* global React */
Copy link
Member

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( {
Copy link
Member

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' );
	// ...

@afercia
Copy link
Contributor

afercia commented Jan 14, 2019

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:

  • align the image
  • set it full width
  • access other options in the toolbar
  • and yes, also insert a caption

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:

screenshot 2019-01-14 at 18 50 04

Copy link
Contributor

@afercia afercia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment.

@dylanb
Copy link
Author

dylanb commented Jan 26, 2019

@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

@afercia
Copy link
Contributor

afercia commented Jan 26, 2019

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:

Use it to repair the keyboard focus flow when it is disturbed, not to try and anticipate how users want to use applications.

@youknowriad youknowriad removed this from the 5.3 (Gutenberg) milestone Mar 18, 2019
@aduth
Copy link
Member

aduth commented Apr 24, 2019

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.

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.

There was no prior conversation. My understanding is that it improved a scenario where otherwise a focus loss would occur.

@aduth aduth added the [Status] Stale Gives the original author opportunity to update before closing. Can be reopened as needed. label Apr 24, 2019
@tofumatt tofumatt removed their request for review June 2, 2019 22:37
@gziolo gziolo added the First-time Contributor Pull request opened by a first-time contributor to Gutenberg repository label Jun 3, 2019
@youknowriad
Copy link
Contributor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Block] Image Affects the Image Block First-time Contributor Pull request opened by a first-time contributor to Gutenberg repository [Focus] Accessibility (a11y) Changes that impact accessibility and need corresponding review (e.g. markup changes). [Status] Stale Gives the original author opportunity to update before closing. Can be reopened as needed. [Type] Bug An existing feature does not function as intended
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants