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

Address PR feedback on block labels #19664

Closed
wants to merge 1 commit into from

Conversation

talldan
Copy link
Contributor

@talldan talldan commented Jan 15, 2020

Description

PR #18132 was merged accidentally prior to any code review, but rather than revert I'll attempt to tackle review feedback in follow-ups. #19597 also tackles much of the feedback (thanks @ellatrix!)

This PR:

  • Moves the experimental functions getAccessibleBlockLabel and getBlockLabel to the block-editor package as selectors.
  • Moves stripHTML from the dom package to rich-text and renames to toPlainText.
  • Instead of one experimental getLabel function that can be declared on blocks there's now separate properties:
    • label, which can be declared as a string, the name of an attribute that should appear in the block navigator.
    • getAccessibilityLabel( attributes ) a function that takes attributes for tailoring an accessibility label for the block. Breadcrumb: add accessibility label #19597 exposes this in navigation mode. It falls back to using the above label when unspecified

How has this been tested?

  1. Add a Navigation Block (make sure it's enabled as an experimental feature).
  2. Ensure the navigation block has menu items.
  3. View the Block navigator.
  4. Observe the block labels for menu items
  5. Inspect the block mover for a menu item using dev tools
  6. Observe the screen reader text includes the new block label
  7. Inspect the menu item's block wrapper
  8. Observe the aria label includes the new block label

Other blocks worth testing for the aria-label - Paragraph, Heading, More, Image.

Types of changes

Task

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows the accessibility standards.
  • My code has proper inline documentation.
  • I've included developer documentation if appropriate.
  • I've updated all React Native files affected by any refactorings/renamings in this PR. .

@talldan talldan added the [Type] Task Issues or PRs that have been broken down into an individual action to take label Jan 15, 2020
@talldan talldan self-assigned this Jan 15, 2020
@talldan talldan requested a review from aduth January 15, 2020 11:31
@MarcoZehe
Copy link
Contributor

LGTM.

@talldan talldan force-pushed the split/block-label-and-accessibility-label branch from 78cea2d to 2862a18 Compare January 17, 2020 03:27
@talldan
Copy link
Contributor Author

talldan commented Jan 17, 2020

I've rebased to incorporate changes from #19597, and tidied up a few things.

@@ -20,6 +20,33 @@ import { __ } from '@wordpress/i18n';
import BlockIcon from '../block-icon';
import ButtonBlockAppender from '../button-block-appender';

function BlockNavigationRow( { block, isSelected, onClick, children } ) {
Copy link
Member

Choose a reason for hiding this comment

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

Minor: On the point of naming, I don't think it would be commonly expected to refer to an entry in a list as a "row", but rather as an "item", e.g. BlockNavigationListItem.

Edit: Also noting that the class name itself assigned to the div is already referring to this as an "item".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've done a few things here based on the PR comments:

  • Moved this component into a separate file item.js, renamed the component to BlockNavigationItem.
  • This component should ideally have the classname block-editor-block-navigation-item on its root element, but there's no need for a class name there. Given that, I've now changed the class names for the inner elements to block-editor-block-navigation-item__block (as this element represents an individual block) and block-editor-block-navigation-item__button.
  • The navigation list appender was still defined in BlockNavigationList but was using the styles from the BlockNavigationItem. I've made this a new component BlockNavigationItem.Appender and it uses the classname block-editor-block-navigation-item__appender internally.

Comment on lines +1571 to +1657
// Strip any HTML (i.e. RichText formatting) before returning.
return toPlainText( label );
Copy link
Member

Choose a reason for hiding this comment

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

Following from my comment at #18132 (comment)

If we're expecting the accessible label to be associated with an attribute, I still find it strange we'd need to do this, because in my view, if the attribute value can represent a label, it should already be plain text.

I guess I can understand based on what you're saying in #18132 (comment) that this could help enable re-use of attributes where the attribute itself serves some purpose (which may require it to contain markup) but can also represent its title (but only after some post-processing). The example you gave is something like a heading's title. I kinda feel like this should still be left to the block though, or at least:

  • If we're already going to need to provide some way for a block to define the logic associated with deriving an accessible label from attributes, then this would force some consistency and simplify the interface (i.e. __experimentalLabel is not an overloaded block setting)
  • What would be the full extent of the "global" post-processing we apply here? I find it troubling that we assume that this value could contain rich text. What else could it contain? My point is that if we force this to be handled by a block, then we don't need to answer this question, because that's left to the block to decide.

The way I'd see it is something more like:

registerBlockType( 'core/heading', {
	attributes: {
		title: {
			type: 'string',
		},
	},
	getLabel: ( { attributes } ) => toPlainText( attributes.title ),
	// ...
} );

Copy link
Contributor Author

@talldan talldan Jan 20, 2020

Choose a reason for hiding this comment

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

The main issue is formatting, for example the Navigation Link block, which has a label that can be formatted (so could contain tags like em/strong). I didn't want this to appear in the block navigation.

The label is no longer a function, instead you can denote a single attribute:

__experimentalLabel: 'label'

so there's no way for toPlainText to be called from there.

In terms of why it's no longer a function I'll need to defer to @mtias or @youknowriad on this, but I think it probably needs some time to mature or to think about the wider use cases.

Copy link
Member

Choose a reason for hiding this comment

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

The main issue is formatting, for example the Navigation Link block, which has a label that can be formatted (so could contain tags like em/strong). I didn't want this to appear in the block navigation.

Yeah, this makes sense to me that we wouldn't want these tags to be represented. My concern is more that with the implementation as proposed, it (implicitly) bakes in handling of specific attribute values where a string may or may not contain some HTML. It all seems very loosely-defined. The strings containing HTML is an implementation detail of rich text, and is neither something the block editor should be aware of, nor that it actively considers (we just process all strings, regardless whether they're "rich text" strings). It also raises questions for which I don't think we have a good baseline: What criteria do we have for considering whether it makes sense to bake-in such processing? Should we also strip shortcodes from strings? Should we Array#join an array of strings, if that's how a block chooses to represent some attribute value?

I'd also invite some perspective from Matias and Riad on whether we should avoid the function.

My initial impression is that, by delegating this to the block implementer, it may be slightly less convenient, but is much clearer how this relationship is represented. A block implementer knows that they're working with a rich text value, and they would know that the label must be provided as plain text. It's up to them to satisfy that requirement. We could always consider to add the attribute-name form as an abstracted shorthand in the future, if we can answer the clearer picture for how those problematic attribute value forms be interpreted by the block editor.

Copy link
Contributor Author

@talldan talldan Jan 21, 2020

Choose a reason for hiding this comment

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

What are the negatives, though? Perhaps a slight overhead in cleaning these strings.

The positives seem to outweigh the negatives to me—not only is this easier for block implementors, but also the editor won't show an unusual looking html string if a block implementor forgot to convert the text. It feels like it's a very easy thing to miss for a block developer, and it'd look really buggy to the end user if it were missed.

Copy link
Member

Choose a reason for hiding this comment

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

Would it be strange to serialise the block and strip the HTML from that?

Copy link
Member

Choose a reason for hiding this comment

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

After all, this is what a screen reader would read if there were no label. It would read the whole content of the block?

Copy link
Contributor Author

@talldan talldan Jan 21, 2020

Choose a reason for hiding this comment

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

Would it be strange to serialise the block and strip the HTML from that?

After all, this is what a screen reader would read if there were no label. It would read the whole content of the block?

Most of the points I raised above were related to the block navigation UI:
Screen Shot 2020-01-21 at 3 12 47 pm

When there's no label it's just the block title that's output. Also worth noting that this is output as a string, so any markup will appear as plain text (e.g. <em>text</em> instead of text). It doesn't use dangerouslySetInnerHTML

It's the same for the accessiblity label, since this is now implemented in navigation mode, the content of the block wouldn't be read, just the block title. If tags were present in the label it'd be output in the string used as the aria-label:

aria-label="<em>text</em>"

All of that html would be read "less em greater text less slash em greater".

A block outputs HTML, so not an issue there.

If that can be avoided, why not?

Perhaps an alternative could be to not build it into the selector, but instead where the label is consumed.

const blockName = getBlockName( state, clientId );

const {
__experimentalGetAccessibilityLabel: getAccessibilityLabel,
Copy link
Member

Choose a reason for hiding this comment

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

I think we need some more consistency with how these things are named.

We reference both "accessible" and "accessibility" labels? Can we pick one?

Or also: Why do we need to qualify it? Can't we just call it a label? I worry the qualifier might make this some overly specialized thing, if all we truly care about is having some meaningful way to describe the instance of the block, which can be (but not necessarily only be) used as its accessible label. This ties to what I mention in https://github.com/WordPress/gutenberg/pull/18132/files#r365328275 and https://github.com/WordPress/gutenberg/pull/18132/files#r367984986.

Copy link
Contributor Author

@talldan talldan Jan 20, 2020

Choose a reason for hiding this comment

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

We reference both "accessible" and "accessibility" labels? Can we pick one?

True, I can sort out the consistency.

Or also: Why do we need to qualify it? Can't we just call it a label? I worry the qualifier might make this some overly specialized thing, if all we truly care about is having some meaningful way to describe the instance of the block, which can be (but not necessarily only be) used as its accessible label.

I'm not sure I fully understand this criticism. It's the same reason we often use aria-label, aria-description or screen-reader-text to add extra information over and above the existing visual labels. Sometimes that's because labels aren't present, but there are plenty of cases where they are, but are not adequate for screen reader usage (block movers are an example).

Replying to your linked comment:

Even in the implementations we've revised so far, we're only ever handling the "accessibility" case. So what's the point of "visual"?

So there's a bit of history here. Originally, the mobile devs implemented an __experimentalGetAccessibilityLabel for blocks. That's why the majority of the existing cases are accessibility labels.

The navigation link block is the only block that implements a 'visual' label at the moment. There was a specific requirement to do that at the time.

Now I'm attempting to tidy things up (but perhaps making things worse!). My original PR #18132 was an attempt to try to consolidate these APIs into a single label, but the reality is that the accessible label often doesn't work visually, and vice versa.

Paragraph and Heading are two instances where there's probably a bit more information in the accessibility label than we'd consider showing visually. But they've already been assessed as being useful for screen readers.

Perhaps the mistake is that they shouldn't both be called 'labels' as that implies an association. Maybe one should be getAccessibilityDescription and the other a label? They are used differently at the moment (one in navigation mode, the other in the block navigator) so this probably makes sense.

Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps the mistake is that they shouldn't both be called 'labels' as that implies an association. Maybe one should be getAccessibilityDescription and the other a label? They are used differently at the moment (one in navigation mode, the other in the block navigator) so this probably makes sense.

The trouble with AccessibilityDescription is that this is a separate property on Accessibility objects on various platforms. Windows, Linux, and Mac have an AccessibleDescription which is secondary information to the AccessibilityLabel. AccessibilityLabel is the primary thing that says what a button or other thing is named, or will do when executed. The description is additional information. On iOS, this is called AccessibilityHint. It is what VoiceOver speaks after a delay, on a Home Screen element, for example, this is "Double tap to open". In mobile Gutenberg, this is often used to communicate helpful things like "Double-tap to add a block" or instructions pertaining to a particular block type.

So in terms of Accessibility terminology, AccessibilityLabel is the perfectly correct and unambiguous term to use here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the background @MarcoZehe.

Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I fully understand this criticism. It's the same reason we often use aria-label, aria-description or screen-reader-text to add extra information over and above the existing visual labels.

I think the parallels you mention are appropriate, but I see them as backing up my worries. I may be mistaken, but the way I understand those attributes is as something of an escape hatch where the design of a visual label would not sufficient on its own in providing a meaningful name. And that, whenever possible, we should optimize for a label which can be well-understood in all contexts (visual or non-visual).

It may very well be true that in some cases we'd want to present these labels with some added contextual clarification (in other words, maybe we do want to use them in aria-label, perhaps with some prefix or suffix text). But are those additional details going to be distinct per-block, or something generic that we can decide at a framework level? In the latter case, it affords us the opportunity to remove this from consideration of the block implementer.

At the end of the day, what conceptual details are we wanting the block to provide here? Is it just a short description of the contents of that block? My initial reaction here is that this is something which can be implemented without tying it so specifically to the implementation detail of how that label is used. Or, at least, based on my understanding of how we're currently proposing to use that label, it seemed perhaps premature.

but the reality is that the accessible label often doesn't work visually, and vice versa.

I admit that I'm not fully aware of all of the history here, and that this is fair reasoning for why they be treated distinctly. But I think my approaching this from a perspective of ignorance might be useful in considering to reevaluate this. We should always be open to the possibility that we've been doing it wrong all along. What is it specifically that makes one appropriate visually and not for screen readers, and vice-versa, like you mention? Are those differences reasonable, or symptomatic of a larger problem?

If it helps, my motivation here is largely to avoid forcing the block implementer needing to be aware of a distinction or needing to make a choice (especially where a choice could have negative accessibility implications if done incorrectly), and to distill the requirement to one which maximizes reusability.

@aduth
Copy link
Member

aduth commented Jan 17, 2020

Depending whether this one can be merged in time for next week's plugin release, we should want to make sure we make the minimal changes necessary ahead of the release where we're anticipating to need to make breaking changes.

I opened #19725 to mark wp.dom.stripHTML as unstable, considering it is proposed to be removed here, since otherwise we would be bound to some backward compatibility.

@talldan talldan force-pushed the split/block-label-and-accessibility-label branch 2 times, most recently from a133ce7 to 0a2e6e4 Compare January 20, 2020 07:06
@talldan talldan requested review from nerrad and ntwb as code owners January 20, 2020 07:06
@@ -32,6 +32,20 @@ _Returns_

- `boolean`: Whether the last change was automatic.

<a name="getAccessibleBlockLabel" href="#getAccessibleBlockLabel">#</a> **getAccessibleBlockLabel**
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should I make these experimental selectors?

@MarcoZehe
Copy link
Contributor

To further endorse why we need AccessibilityLabels: Speech is strictly a sequential medium. Information is processed one after another. That also means information must be provided in a way that gives the most context in the most efficient way possible. That means most important info first, followed by the next less important bit, etc. And it needs to convey information that a sighted person would gather from looking at the screen, skimming parts of it, etc. Sight is a parallel medium, sighted people can take in several bits of information at once. So for blocks, for example, the type and position information are information gathered in two places: The label for the block and the position on the screen relative to others. The third bit, the block contents, can be skimmed while looking at the block itself, which also is happening while looking at its position.

Now, how do we put that into perspective for a sequential medium like speech or braille? We gather these pieces together from all these different sources: The block type, the position, the contents, and string them together so a speech medium can give this information to someone who cannot see the screen, cannot skim it.

That label, however, if applied visually, would clutter up the screen by providing redundant information. That's why applying screen reader labels visually sometimes just doesn't make sense and would reduce usability for sighted people. They already have that information, it would be repeated by applying the screen reader specific info to the visual label.

Now, there are cases where a text label on a button does make more sense for everyone than an icon and alternative text for it, especially when it is discovered that many sighted people don't know what that icon is, due to different cultural backgrounds, for example. In such cases, aligning the screen reader label and visual label does make sense.

But in cases like this where the screen reader information does contain information from different sources on the screen, redundantly showing that information visually would not make sense.

I hope this further explains why it is sometimes necessary to have a special label for screen readers. And why this case for block labels in navigation mode is one of them.

@talldan talldan force-pushed the split/block-label-and-accessibility-label branch from 0a2e6e4 to ea11eb9 Compare February 7, 2020 08:48
@talldan talldan mentioned this pull request Feb 10, 2020
18 tasks
Copy link
Contributor

@draganescu draganescu left a comment

Choose a reason for hiding this comment

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

I tested this and it still works as described for the Navigation block (with this PR's updates that is)

@talldan
Copy link
Contributor Author

talldan commented Feb 16, 2021

Closing this as stale.

@talldan talldan closed this Feb 16, 2021
@talldan talldan added the [Status] Stale Gives the original author opportunity to update before closing. Can be reopened as needed. label Feb 16, 2021
@youknowriad youknowriad deleted the split/block-label-and-accessibility-label branch February 16, 2021 08:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Status] Stale Gives the original author opportunity to update before closing. Can be reopened as needed. [Type] Task Issues or PRs that have been broken down into an individual action to take
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants