Problem/Motivation

Over in #2962110-39: Add the Media Library module to Drupal core, we discovered that there is an accessibility problem with the entity operation drop-buttons exposed by Views. Namely:

The entity operations (dropbutton) links are [a] problem ... users need to know which media item they apply to. Just like bulk-ops checkbox, the dropbuttons currently get context from the HTML table row structure. But in the new grid, the dropbutton is outside of the rendered entity's <article> wrapper.

There's already an issue to address this in EntityListBuilder, but the dropbuttons in the new media library actually come from the EntityOperations views field plugin.

Proposed resolution

Change the EntityOperations plugin so that the label of the entity is embedded in a way that will be visible to assistive technology.

Remaining tasks

Write a patch, write tests, get the accessibility team's blessing, and commit.

User interface changes

Entity operation drop buttons in views may change when seen by screen readers, etc. A Views plugin may receive new administrative UI options.

API changes

None - there is markup change.

Data model changes

None anticipated.

Issue fork drupal-2973124

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

phenaproxima created an issue. See original summary.

andrewmacpherson’s picture

Title: Improve accessibility of entity operation drop-buttons » Improve accessibility of EntityOperation views plugin drop-buttons
Related issues: +#2960828: Link labels generated by the Entity List Builder should provide more context for a11y

We already have #2960828: Link labels generated by the Entity List Builder should provide more context for a11y to address the drop buttons from EntityListBuilder, which will be a more predictable situation to compute names for.

phenaproxima’s picture

Issue tags: +blocker

This issue blocks the new Media Library (added in #2962110: Add the Media Library module to Drupal core) from being stable, according to this excerpt from Slack:

andrewmacpherson [3:13 PM]
a11y review of latest media library patch done.

samuel.mortenson [3:16 PM]
@andrewmacpherson We’re opening a follow up now, which won’t block the issue getting committed but will block media library being released in a Drupal minor release until that’s fixed.
Thank you again

andrewmacpherson [3:18 PM]
For the EntityOperations views plugin?  I agree it;s not a blocker for adding the alpha module, but it's a blocker for marking it stable.

samuel.mortenson [3:19 PM]
Agreed. Just easier to work with it in another issue since right now the Media library doesn’t touch anything else

andrewmacpherson [3:21 PM]
It's only become an issue because this is the first time we're using these dropbuttons outside a HTML table context.
There are quite a few other improvements we could make to our HTML tables mind.  I need to file issues about 'em all.  Row headers + captions.

samuel.mortenson [3:22 PM]
I would assume that this will make it easier for screen readers even if you’re using a table, right? That way you don’t have to traverse for context, since the table row isn’t labeled.
andrewmacpherson’s picture

@phenaproxima:

I would assume that this will make it easier for screen readers even if you’re using a table, right? That way you don’t have to traverse for context, since the table row isn’t labeled.

Yes, that's correct. The table row context I mentioned refers to "programmatically determined link context" in WCAG. Most screen readers provide tools for dealing with tables, like quickly jumping to the start/end of a row. But you can ask a screen reader to announce the headers for the current table cell, on demand, and you don't need to move away from the link. In the absence of explicit row-level headers, some screen readers will treat the first cell in the row as a header. I think it's wise to assume these tools are power-user level; not all screen reader users will be used to them.

Screen readers also have tools for listing links, or listing buttons. In this case, you don't have the benefit of the table row context. This issue can help screen reader users here.

We also have issues to make row-level headers too - #2962585: Treat entity label as a row-level header in entity list tables and #2770835: Add support for tables with two headers in views table display.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

rodrigoaguilera’s picture

Status: Active » Needs review
Issue tags: +BarcelonaMediaSprint
StatusFileSize
new1.11 KB

Following the same strategy as #2960828: Link labels generated by the Entity List Builder should provide more context for a11y adding a visually hidden text with the label of the entity.

Status: Needs review » Needs work

The last submitted patch, 6: 2973124-accessibility-drop-button-6.patch, failed testing. View results

wim leers’s picture

This looks great!

Now we just need some test expectations to be updated, then it'll pass tests again :)

rodrigoaguilera’s picture

Assigned: Unassigned » rodrigoaguilera

I'll work on this

chr.fritsch’s picture

Status: Needs work » Needs review
StatusFileSize
new8.32 KB
new7.22 KB

Let's try to fix the tests

rodrigoaguilera’s picture

Status: Needs review » Reviewed & tested by the community

I reached a similar point fixing the tests so I think this is good to go.

wim leers’s picture

+++ b/core/modules/views/tests/src/Functional/Handler/FieldEntityOperationsTest.php
@@ -76,7 +76,8 @@ public function testEntityOperations() {
-          $result = $this->xpath('//ul[contains(@class, dropbutton)]/li/a[@href=:path and text()=:title]', [':path' => $operation['url']->toString(), ':title' => (string) $operation['title']]);
+          list($operation_label) = explode(' ', strip_tags((string) $operation['title']), 2);
+          $result = $this->xpath('//ul[contains(@class, dropbutton)]/li/a[@href=:path and contains(text(), :operation)]', [':path' => $operation['url']->toString(), ':operation' => $operation_label]);

@chr.fritsch said he'd like to see this be more elegant. I did some digging.

I found https://stackoverflow.com/questions/16241197/xpath-to-retrieve-text-with..., which led to me trying the expression string-join(//span[@class="inner-span"]/text()) on their example, and that returns


    Text-data 1
    
     text Data 3
   

Which is pretty much what we want to use here.

Hope this helps!

andrewmacpherson’s picture

The links will need to satisfy the new "Label in name" success criterion from WCAG 2.1. The short explanation is: don't put any visually-hidden text in between visible words in the link/button name. (So "Manage [Basic page] fields" would fail, for example.)

In this case, it's easy to satisfy WCAG because we only have single-word links to begin with. The changes to EntityListBuilder in patch #10 satisfy WCAG "Label in name" (for English, anyway - translators could cause a WCAG problem, so we'll need to provide guidance on this in the d.o translation handbook, say.). There's a plan to carry out an audit for WCAG "Label in name" across the whole of core, so we'll get another chance to confirm this anyway.

andrewmacpherson’s picture

Title: Improve accessibility of EntityOperation views plugin drop-buttons » Provide better context in accessible names of EntityOperation views plugin drop-button operations

title tweak, a bit clearer about what the improvement is.

alexpott’s picture

Issue summary: View changes
Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs change record, +markup change

I think we need a change record here. It is highly likely that some contrib / custom tests are going to fail because of this sensible change so I think it is worth having so when that happens there's one to point at.

andrewmacpherson’s picture

writing a change record now...

andrewmacpherson’s picture

Assigned: andrewmacpherson » Unassigned

I didn't write a change record, it stlll needs work. Here's a new patch which updates some of the remaining entity operations, in classes which extend EntityListBuilder.

After looking again at #2960828: Link labels generated by the Entity List Builder should provide more context for a11y, I don't think we need both issues, we can do it all in one issue. Some things to consider:

The approach taken by the patch at #2960828-6: Link labels generated by the Entity List Builder should provide more context for a11y is to change the link title in EntityListBuilder::buildOperations(). It uses the operation title as visible text, followed by the entity label, then entity bundle label, in the visually-hidden span. This seems to work generically, but has a few issues. I use [square brackets] for the visually-hidden part:

  • PRO: It catches all the entity operations for EntityListBuilder subclasses, including some added by contrib modules, like Devel. So we get a lot for free.
  • PRO: Many of the resulting operation titles are good. Example "Edit [Basic page node_type]".
  • CON: The format doesn't address the context of any particular operation titles, which may lead to some clumsy-sounding sentences, e.g. "Manage form display [Basic page node_type]"
  • CON: Related, some operation titles have already been adjusted in sub-classes which override EntityListBuilder::getDefaultOperations(). For example MenuListBuilder changes the "Edit" operation title to "Edit menu". There could be some more unusual sentences resulting from this. An example is "Edit menu [User account menu menu]"
  • CON: It doesn't modify the entity operations in Views, which is why we originally thought this would need two issues.
  • OK: It currently assumes there is a bundle label, but not all entity types have bundles. In that case the bundle label is always the same as the entity type label, I think?

The approach in my patch here adds visually-hidden context to all the individual operations that are added by classes which extend EntityListBuilder::getDefaultOperations(), when they add their own operations. There are some issues here too:

  • PRO: This allows us to tailor the operation titles better for individual EntityListBuilder subclasses, but...
  • CON: We have to do it in every one of the EntityListBuilder subclasses.

This patch adds context to operations in the following classes:

  • core/lib/Drupal/Core/Entity/EntityListBuilder.php
  • core/lib/Drupal/Core/Config/Entity/ConfigEntityListBuilder.php
  • core/modules/menu_ui/src/MenuListBuilder.php
  • core/modules/search/src/SearchPageListBuilder.php
  • core/modules/action/src/ActionListBuilder.php

I stopped updating them after I had seen a few. I figured I would report back here, to get feedback about this! So what do people think about the approaches from these two issues? So far it's only considering how the core English versions are phrased.

TODO - the following classes still need visually-hidden link context added in their own getDefaultOperations() methods:

  • core/modules/image/src/ImageStyleListBuilder.php
  • core/modules/taxonomy/src/VocabularyListBuilder.php
  • core/modules/views_ui/src/ViewListBuilder.php
  • core/modules/block_content/src/BlockContentTypeListBuilder.php
  • core/modules/field_ui/src/FieldConfigListBuilder.php
  • core/modules/node/src/NodeTypeListBuilder.php
  • core/modules/block/src/BlockListBuilder.php
  • core/modules/shortcut/src/ShortcutSetListBuilder.php
  • core/modules/workspaces/src/WorkspaceListBuilder.php
  • core/modules/responsive_image/src/ResponsiveImageStyleListBuilder.php
  • core/modules/user/src/RoleListBuilder.php
  • core/modules/filter/src/FilterFormatListBuilder.php
  • core/modules/comment/src/CommentTypeListBuilder.php
andrewmacpherson’s picture

Status: Needs work » Needs review
StatusFileSize
new12.19 KB
new3.38 KB

Oops, forgot to attach my patch...

Status: Needs review » Needs work

The last submitted patch, 18: 2973124-18.patch, failed testing. View results

phenaproxima’s picture

Status: Needs work » Needs review
StatusFileSize
new21.85 KB
new8.42 KB

Didn't change any tests (so we can see what this breaks, and then fix it all), but added some context wording to the rest of the list builders mentioned in #18.

One thing I would suggest is that we might want to add a helper method to the base list builder class to do this. That will give us more freedom to improve the internationalization of this approach. However, speaking a near-total n00b to the world of accessibility, I think the approach taken in #18 makes a lot of sense.

alexpott’s picture

+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityListBuilder.php
@@ -35,14 +35,14 @@ public function getDefaultOperations(EntityInterface $entity) {
+          'title' => $this->t('Enable <span class="visually-hidden">@label</span>', ['@label' => $entity->label()]),

I have a concern about this. Build sentences from already translated labels often breaks. For example if the label is What should you do about a problem called potatoes then this will read Enable What should you do about a problem called potatoes. It looks like we have a couple of problems here. One is capitalisation ie Enable What the other is it possible to imagine things that very confusing. Say the title is Enable people to learn more and then you'll have Enable Enable people to learn more

Status: Needs review » Needs work

The last submitted patch, 20: 2973124-20.patch, failed testing. View results

rodrigoaguilera’s picture

Is quoting a good practice here like Enable "Enable people to learn more" ?

alexpott’s picture

We've done that for things like

$this->t('Display "@display" needs a selected search fields to work properly. See the settings for the Entity Reference list format.', ['@display' => $this->display['display_title']]);

and

$this->t('Placeholder for the "@block" block', ['@block' => $this->label()])

So yeah I think that that is way forward.

rodrigoaguilera’s picture

Status: Needs work » Needs review
StatusFileSize
new23.53 KB

I attach a patch that changes all the operations to have the label between quotes and fixes the tests (hopefully).

Status: Needs review » Needs work

The last submitted patch, 25: 2973124-25.patch, failed testing. View results

andrewmacpherson’s picture

Re #25 - thanks for the updated patch rodrigoaguilera. I'm travelling today so I don't have time to review it in detail.

Re #23 and #24...

The quotes idea is probably good for visible text. Likewise, other punctuation like dashes or colons could help. Italics are another method which can work visually.

But for text in a visually-hidden span, there is very little benefit to using common puntuation to indicate the entity label. Screen readers don't announce it, or give any special indication, for the most part. Some screen readers do offer user preferences to control the announcement of punctuation, but the default settings are generally silence for common punctuation. The upshot is that we can't expect screen reader users to notice quote marks.

The thing that makes this tricky, is that we have no control over the phrasing in entity labels created by users - but these are the contextual information we want to convey in this issue.

The latest patches here have phrasing for the particular entity lists, but there will always be clumsy sentences this way.

So the important question is whether:

  • it's worth tailoring the operation names, or
  • going for a generic approach of operation name followed by entity label.
rodrigoaguilera’s picture

Status: Needs work » Needs review
StatusFileSize
new15.7 MB
new5.03 KB

Can you provide an example about tailoring the the operation names. Is not very clear to me.

Attached is another attemp at fixing the tests.

Status: Needs review » Needs work

The last submitted patch, 28: 2973124-28.patch, failed testing. View results

wim leers’s picture

Assigned: Unassigned » andrewmacpherson

I'm also not sure what that is asking exactly.

rodrigoaguilera’s picture

Assigned: andrewmacpherson » Unassigned
Status: Needs work » Needs review
StatusFileSize
new26.6 KB

Here is the proper patch. The interdiff is the same

Status: Needs review » Needs work

The last submitted patch, 31: 2973124-31.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 31: 2973124-31.patch, failed testing. View results

wim leers’s picture

The link Set "?!`U&gt;&amp;`A" as default was not found on the page.

This seems like an encoding issue.

Let’s use a random string that does not contain ampersands and other characters that are HTML-encoded. That will likely fix this.

(Typing on the subway, unable to actually test this.)

rodrigoaguilera’s picture

Status: Needs work » Needs review
StatusFileSize
new2.8 MB

Is not encoding, is a consistent fail because we are introducing HTML in the middle of the sentence that will be stripped by xpath text() method.

I changed the clickLink() for an xpath that searches for the two text objects that exist in that element in the right order. I can't think of a way of passing those throgh t().
Maybe in this case is better to create an xpath that translates to "click on the third link of the dropdown ul". What do you think?

I definitely learned a lot of xpath this sprint week :)

rodrigoaguilera’s picture

StatusFileSize
new1.06 KB
rodrigoaguilera’s picture

StatusFileSize
new26.59 KB

ugh, bad patch again. Now I am ignoring that folder

phenaproxima’s picture

So the important question is whether:

it's worth tailoring the operation names, or
going for a generic approach of operation name followed by entity label.

Personally, I favor pragmatism here. Internationalization issues are extraordinarily tricky, and I'm not sure what mechanism we could use to tailor the operation names. I think if we try to solve it that way in this issue, we might be here for literally years.

So IMHO, although it's sub-optimal, we should go for the most accessible short-term solution and take a generic approach. But it might be worth opening bigger conversations in other issues about how we might solve the difficult problem of true internationalization for entity operations. That's way beyond my pay grade, so I can't say I have any clue how that would proceed, but it seems like we can make a worthwhile and positive short-term gain here, so I'd favor that.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

joachim’s picture

Status: Needs review » Needs work
+++ b/core/modules/action/src/ActionListBuilder.php
@@ -99,7 +99,10 @@ public function buildHeader() {
+    if (isset($operations['delete'])) {
+      $operations['delete']['title'] = t('Delete <span class="visually-hidden">action "@label"</span>', ['@label' => $entity->label()]);

I don't think we need to add this.

The 'delete' operation will have been added by the grandparent class EntityListBuilder, and we set the context there.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

vsujeetkumar’s picture

Status: Needs work » Needs review
StatusFileSize
new26.39 KB

Re-roll patch created for 9.1.x, Please review.

Status: Needs review » Needs work

The last submitted patch, 43: 2973124_43.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

vsujeetkumar’s picture

Status: Needs work » Needs review
StatusFileSize
new26.41 KB
new945 bytes

Fixed more test, Please review.

vsujeetkumar’s picture

StatusFileSize
new945 bytes

Please ignore the above interdiff, check this.

The last submitted patch, 45: 2973124_45.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

vsujeetkumar’s picture

StatusFileSize
new27.2 KB
new676 bytes

Fixed the test, Please check and advise it is up to the mark or not.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

nishantghetiya made their first commit to this issue’s fork.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new149 bytes

The Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

mgifford’s picture

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.