When accessing local task links (normally themed as tabs) with a screen-reader it is not possible to determine the context, or role, of the links or which link is active. This is because there is no semantic markup to provide this information.

theme_menu_local_tasks http://api.drupal.org/api/function/theme_menu_local_tasks/7

theme_menu_local_task http://api.drupal.org/api/function/theme_menu_local_task/7

Recommendation

Provide semantic markup for ul and active li item to indicate the role of the links and which task (tab) is active.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Everett Zufelt’s picture

Issue tags: -undefined +Accessibility, +markup

accessibility

Everett Zufelt’s picture

This issue can be broken down into two parts:

1. Provide markup in theme_menu_local_tasks to provide indication that the ul of links is actually functionally a tabstrip.

2. Provide markup to indicate which link is the current active task.

Part 1 is quite simple, part two I have researched and the following is what is required.

1. Modify menu_local_tasks to pass information about the current active task to theme_menu_item_link (called as theme('menu_item_link', ...)).

2. Modify theme_menu_item_link to test if this is the current active task and then use a similar technique as described in #526712: l() does not accept a parameter for supplemental text for screen-readers comment 25 to provide contextual information for screen-reader users.

Dave Cohen’s picture

We also need to know the depth of the menu item. (see #371453: theme_menu_local_task does not differentiate between primary and secondary local tasks) Or is that what you mean by "context"?

bowersox’s picture

subscribing

Nick Lewis’s picture

Could someone offer a sample of what a "semantic" active secondary task might look like??

Everett Zufelt’s picture

@Dave

If possible I would like to provide the functionality to distinguish menu typs. But, I do feel like this is best dealt with in the separate issue that you have opened.

@Nick

I believe that what Dave is talking about here is thematic context (a different class for primary and secondary tasks) whereas I am talking about providing textual information to provide a context for which task is active.

Everett Zufelt’s picture

Status: Active » Needs review
FileSize
1.23 KB

Rolled a patch for this issue:

This is not complete as:

1. menu_local_tasks() only sets 'active' = TRUE in options array for theme('menu_item_link', ...) if the default task is active.

2. theme_menu_item_link() is appending " (active task)" to the link text if 'active' is TRUE, but is not wrapping the appended text in a <span class="element-invisible"></span> for testing purposes.

Any assistance tracking down the other sections where menu_local_tasks needs to set active = TRUE would be appreciated.

Dave Cohen’s picture

It would be so easy to add $level to that array. Why not do it?

I think the same array of information should be passed to theme('menu_local_task', ...) But I see that's a bigger change because that function would take new parameters.

Everett Zufelt’s picture

This patch creates a new local variable in menu_local_task $active_task and assigns it true or false depending on if the current $item[] is an active task.

$active_task is then passed as 'active' in the options array for theme('menu_item_link',....).

theme_menu_item_link() tests for 'active' and appends ' (Active task)' to the local task link. Once this has been tested I will make sure that the appended link text is wrapped in <span classs="element-invisible"> </span>

Status: Needs review » Needs work

The last submitted patch failed testing.

Everett Zufelt’s picture

The patch above failed Node Edit -> Edit tab found. The test will need to be modified in order for this patch to pass, but first we need to agree upon the language to append to the local task link.

sun’s picture

Title: theme_menu_local_tasks and task lack semantic markup to indicate role of list and active task » Links lack semantic markup to indicate an active task
Component: theme system » base system
Status: Needs work » Needs review
FileSize
1019 bytes

I believe what you really want is this.

Everett Zufelt’s picture

@Sun

Thanks for coming out with this patch.

Unfortunately setting the title attribute to "active" does not correct the accessibility problem, as it is not recognized by most screen-readers, and for some screen-readers is read instead of the link text.

Also, I do not believe that the context of "active" is required for all links, only links that are part of a tabstrip. That is, if the main menu of the site has several options, it is not necessary to know which is selected, but it is important, in many situations, to know which local task is selected. An example of where it is important to know which local task is selected is on http://api.drupal.org, where when looking at a particular function there are local tasks for different versions of drupal, but currently no way to know which task / version is selected.

sun’s picture

Hrm. I see the difference now - you added the " (active)" string to the link text, not to the title attribute like in my patch.

However, that would look a bit strange for users that are used to get a visual highlighting of the currently active link(s).

Isn't there some other, reliable way to pass on textual information/attributes to screen-readers?

Everett Zufelt’s picture

@Sun

Thanks again.

I intend to hide the "active" text with a span and class="element-invisible" once testing on the other parts of the patch are complete. I left the text visible for easier testing, making sure that "active" is actually appearing where it is needed.

There really isn't a reliable way to provide information for screen-reader users, that is different than the information presented visibly, than to use element-invisible, and in this case that would be within the link text.

I think that my modifications to menu_local_tasks() are working as desired in my second patch, and will discuss best wording for the appended text on the D7 accessibility call tomorrow.

mgifford’s picture

This is a great solution. It looks good and it will provide such an accessibility improvement for the whole interface!

Thanks for rolling the patch!

Mike

Everett Zufelt’s picture

Title: Links lack semantic markup to indicate an active task » Local tasks lack semantic markup to indicate an active task
FileSize
2.94 KB

rerolled the patch. This will still come up with one fail for Test Class "Node Edit" test "Edit tab found". I could use some help modifying the test in modules/node/node.test line 208 so that this can pass testing bot.

1. menu_local_tasks() now identifies active local tasks and passes the array option 'active' => true to theme('menu_item_link',...)

2. theme_menu_item_link() tests for $link['active'] and if true appends <span class="element-invisible"> (active tab)</span> to the $link['title'] before passing it to l().

3. Sets 'html' => true for l()'s options if active = true.

Status: Needs review » Needs work

The last submitted patch failed testing.

mgifford’s picture

The latest patch applies nicely & produces the expected results!

+1

Jeff Burnz’s picture

I question the approach this issue is taking to solve this issue (which is really one of context):

$head_title and Page $title should provide context to screen readers, last time I looked we don't use AJAX to refresh local tasks so to my mind the context is either already there, and if not, that is the place to address it.

1. The user just clicked a task or menu link - so they know where they are going.
2. The page title and heading level 1 are read out, that gives the context for the page you are on.

Currently this is not the case, since when secondary local tasks are clicked the target page when loaded does not change the title. To my mind that is the issue, I think adding invisible text to a link is a work-around - and non-standard one at that. Will screen reader users really tab through all those links (and wait to hear "active" read out) or jump headings?

Context should be provided by the semantic markup that user expects, in this case at the very least by <title> and <h1>.

My 2 cents.

bowersox’s picture

Please review the updated patch. The functionality of the prior patch worked as advertised and I've added a few things:

  • Updated the corresponding test in modules/node/node.test. We had to use assertRaw() because assertLink() does not handle HTML in the link text.
  • The only part we need to translate through t() is the '(active tab)' text. Let me know if you disagree with this approach.
  • Minor code and indent tweaks.

Based on my testing, the active tab hidden text works in the following places:

  • the Seven /admin screens
  • works in the Garland screens, eg /node/1/edit
  • works with 2 levels of tabs, eg /admin/appearance/settings/garland, in both themes

Further review and feedback would be great.

@jmburnz, I see your point but I wonder if we could rally the community to change the page title or h1 in all these cases. One tricky case is the node View tab. I'm not sure we could add the word View into the h1 or title tag. That would need to appear on every page for users logged in with edit permissions. If we can't put the word View into the h1 or title, this patch seems like a way to queue users that View is the current operation. I'm curious what you and others think.

Everett Zufelt’s picture

Status: Needs work » Needs review

@jmburnz

Thanks for te feedback. Local tasks are functionally a tabstrip of one or many tabs, one of which is active. Themed correctly * all * sighted users have the ability to know which tab is selected. * no * blind user has the ability of knowing which tab is selected. From a purely subjective perspective, I'd rather have a tab identified as "active" than extra information in pagetitle or head_title. But, this is only a preference from the perspective of one screen-reader user. Perhaps we need to address page and node titles as well, but this will need to be done in a different patch.

@brandon

Patch looks good. I do think that we need to use a placeholder in the t() string to provide context for translators, but I'm happy to be shown to be incorrect about this.

Status: Needs review » Needs work

The last submitted patch failed testing.

mgifford’s picture

I'm all for wrapping 'active tab' in a t(). It is an interface level that should be translated.

I don't think we should be piping the brackets through it though. @brandon you are using:
t('(active tab)')

But I think this is more standard:
'(' . t('active tab') . ')'

Mike

mohammed76’s picture

hello.

I do agree about the necessity of having a way to recognize the active task, so thanks for opening this issue.

if it isn't much work, I would rather have the active task change be reflected in the title of the page, as this is more user friendly and is more expected. this would also benifit screen readers's users and sighted people alike from a usability point of view.

however, since I can't provide a patch, i would rather have usable solution roled out before the code freeze in 6 days time. the current patch sounds to be better than nothing and is surely usable.

thanks,
Mohammed al-shara.

mgifford’s picture

Glad to have you back involved Mohamed76. Would be especially great to have testing in RTL if you have time.

It is reflected in the title already. And it's nice that D7 even tosses in the content type that is being edited:
Edit Article Example | test sandbox install

This allows individuals to focus over the element and determine what that specific link is the active task. It's not an either or but both.

Mike

Jeff Burnz’s picture

Hey, thanks for pointing that our Mike, is it possible to have this on one the test sites spoken about yesterday, I know there a few who cant have a D7 setup and would like to see the patches as applied.

Jeff Burnz’s picture

Hey, thanks for pointing that our Mike, is it possible to have this on one the test sites spoken about yesterday, I know there a few who cant have a D7 setup and would like to see the patches as applied.

mgifford’s picture

Status: Needs work » Needs review
FileSize
3.83 KB

Ok, I've rerolled this patch with the i18n changes, applied it to one of our test servers --> http://drupal7.dev.openconcept.ca/

Create a new account here - http://drupal7.dev.openconcept.ca/user/register

And then send me a message to let me know so I can approve your account. I'll then give you admin access to add/remove content.

Not sure there's any better way to do this.

Mike

Status: Needs review » Needs work

The last submitted patch failed testing.

bowersox’s picture

I've re-rolled the patch in 29 with only one change: a fix to the Node edit test in modules/node/node.test. There was a mistake in the assertRaw() line that is fixed now: $this->assertRaw($edit_link, t('Edit tab is active.'));. The test passes on my environment so we'll see about the bot.

Everett Zufelt’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch failed testing.

bowersox’s picture

Testing the patch gives an error on node #34 but passes on my environment. Can anyone advise how best to troubleshoot the test?

I'm assuming this test is the failing test:

$edit_link = '<a href="/node/' . $node->nid . '/edit" class="active">' . t('Edit') . '<span class="element-invisible"> (' . t('active tab') . ')</span></a>';
$this->assertRaw($edit_link, t('Edit tab is active.'));
Brigadier’s picture

Yeah, looks like that's the test that fails and I think I see why.

<a class="active" href="/drupal-head-cvs/node/1/edit">Edit<span class="element-invisible"> (active tab)</span></a>

The order of the class and href attributes are reversed. If the order is reliable then changing it in the patch for node.test will fix it. Looking at the available Assertions for SimpleTest I don't see any that work on the dom instead of just text though. I guess just switch the text around?

bowersox’s picture

@Brigadier

Thanks for the help. A reliable option may be to go back to matching on text like the original test did. Rather than assertRaw() we could do assertText('Edit (active tab)').

bowersox’s picture

Status: Needs work » Needs review
FileSize
3.68 KB

Please review this updated patch. The only change is the assertText() test as discussed. Thanks, everyone!

Everett Zufelt’s picture

This looks good to me. Once again wondering why we are not using a translation placeholder to provide context for translators for 'active tab'?

mgifford’s picture

+1

Applied this here with no problems - http://drupal7.dev.openconcept.ca

This is a critical issue to get in core before sept 1st as per - http://openconcept.ca/blog/everett/final_stretch_help_needed_for_drupal_...

This is also will likely influence the following issue http://drupal.org/node/467296

We want the phrase 'active tab' to be translated so that when a blind screen reader is viewing the site in Thai that they get the description all in Thai:

แก้ไข (แท็บที่ใช้งาน)

Rather than in Thai & English:
แก้ไข (active tab)

This is based on a machine translation http://translate.google.ca of what appears for an English screen reader:
Edit (active tab)

Everett Zufelt’s picture

Status: Needs review » Reviewed & tested by the community

This patch looks good, applies well and works as it should.

This effectively identifies the active tabe for screen-reader users.

mohammed76’s picture

Status: Reviewed & tested by the community » Needs review

Hello mike,

thank you for giving me access to a test site. I can see how this issue is implimented on the site. the active tap has text telling us so: "view [active tab]". however switching to any other tab just changes the text of the active tab, like when clicking contact it says "contact [active tab]" however the title of the page still says "mohammed| open concept]" and the heading of the title also is still labled "mohammed". I was hoping ti become like "contact mohammed | open concept. or something like "mohammed | contact"

anyway, it may be too late to do so now. what should be done so the current fix goes to core? it's certainly very helpful and usable.

thanks,
Mohammed.

Everett Zufelt’s picture

Status: Needs review » Reviewed & tested by the community

Cross posted, resetting to RTBC

sun’s picture

Status: Reviewed & tested by the community » Needs work
+++ includes/menu.inc	27 Aug 2009 01:10:01 -0000
@@ -1262,7 +1262,14 @@ function theme_menu_item_link($link) {
+  if (!empty($link['active'])) {
+    $link_text .= '<span class="element-invisible"> (' . t('active tab') . ')</span>';
+    $link['localized_options']['html'] = TRUE;
+  }

This is not going to work out. By setting 'html' = TRUE, you potentially expose unsafe HTML from the original menu title.

The original $link_text must be check_plain()'ed then.

+++ includes/menu.inc	27 Aug 2009 01:10:01 -0000
@@ -1531,7 +1538,7 @@ function menu_local_tasks($level = 0) {
-            $link = theme('menu_item_link', array('href' => $tasks[$p]['href']) + $item);
+            $link = theme('menu_item_link', array('href' => $tasks[$p]['href'], 'active' => TRUE) + $item);
             $tabs_current .= theme('menu_local_task', $link, TRUE);
@@ -1571,21 +1578,24 @@ function menu_local_tasks($level = 0) {
-            $link = theme('menu_item_link', array('href' => $tasks[$p]['href']) + $item);
+            $link = theme('menu_item_link', array('href' => $tasks[$p]['href'], 'active' => $active_task) + $item);
...
           else {
-            $link = theme('menu_item_link', $item);
+            $link = theme('menu_item_link', array('active' => $active_task) + $item);
           }
-          // We check for the active tab.
-          if ($item['path'] == $path) {
+          if ($active_task) {
             $tabs_current .= theme('menu_local_task', $link, TRUE);

'active' actually is the purpose of the second argument (TRUE) to theme_menu_local_task().

This might need some re-consideration.

+++ modules/node/node.test	27 Aug 2009 01:10:02 -0000
@@ -203,9 +203,9 @@ class PageEditTestCase extends DrupalWeb
+    $this->assertText(t('Edit') . ' (' . t('active tab') . ')', t('Edit tab is active.'));

Since the proper escaping is mentioned in this review, we should also verify this in a test.

4 days to code freeze. Better review yourself.

mgifford’s picture

Thanks for this sun. We do need a little bit more information however.

The first point is excellent and we can easily roll a patch to resolve that.

We're not exactly sure how you want us to address your 2nd & 3rd points however.

We've discussed passing along strings for translation like this:

$link_text = t('%menu_item_link_text<span class="element-invisible"> (active tab)</span>', array('%menu_item_link_text' => $link['title']))

to give context rather than splitting up the task & the active tab text. I think it's going to be easier for everyone if we keep it like it is.

Are you asking us to write a simpletest to verify this? What escaping is mentioned where?

Everett Zufelt’s picture

@Sun

Thanks for the feedback. Along with Mike's questions above, are you suggesting hat $active_task be passed to theme('menu_item_link', ...) as a parameter? Or, is your question more general, why we need to pass the active task state to both theme('menu_item_link') and theme('menu_local_task')?

bowersox’s picture

As a developer and a translator, I would imagine it is simpler to leave 'active tab' translated separately from the actual tab names. If my site has 15 different tab names, I would be frustrated to find out that I needed to do 30 translations -- one for each tab name in both active and inactive state. I would rather just translate my 15 tabs like usual and then also add one translation for 'active tab' that would apply everywhere.

I support this important patch either way. Thanks for your work on this.

Everett Zufelt’s picture

@brandon

I may be wrong about this. But, with using a translation placeholder you are not translatineach tab when it is active and when it is not active. You are translating all of the tab names, and translating "active tab" once. The "%menu-tab-text" is just a placeholder to give translators more context about the purpose and role of the text "active tab" so that they can do a better translation. Perhaps in some languages it is more appropriate to put active tab before the tab-text, so they can then make that change when translating.

annmcmeekin’s picture

Glad to see that this issue is being worked on. Getting this information across to people who can't perceive it visually will be a real boost.

bowersox’s picture

@Everett

That makes sense. Here's what I suggest:

$active = '<span class="element-invisible">(' . t('active tab') . ')</span>';
$link_text .= t('!title !active', array('!title' => $link['title'], '!active' => $active));

This allows translators to translate '!title !active' to customize the order. And they can just translate 'active tab' once, while translating all the titles as usual.

sun’s picture

Status: Needs work » Needs review

1) Please note that check_plain() must only happen when $link['localized_options']['html'] is not already TRUE. If it is already TRUE, then the link is supposed to output HTML instead of plain-text, so you can just append your SPAN.

2) I now realize that theme_menu_local_task() applies an 'active' class only to the surrounding LI and you want to alter the link's attributes. So this should be fine... Although I'm not entirely sure whether this is the right place to do it. The 'localized_options' for the menu link should already contain a class 'active-trail' for all menu links that are in the trail to the current page. If that is not the case for local tasks, then that would be a separate bug.

At the very least, you should be using the regular $link['in_active_trail'] property (instead of the custom 'active') like elsewhere in menu.inc.

3) A test should ensure what I mentioned in 1) -- menu links containing already HTML get the additional HTML appended without being check_plain()'ed, and menu links not being HTML already need to be properly escaped.

4) Should (hopefully) be covered by 2) already.

sun’s picture

On those last follow-ups:

#49 looks good. Yes, the SPAN should not be part of the translatable string. However, the parenthesis should, and there should be no space between the placeholders, and you need to use unique placeholder names, so translators have some clue about the string at all:

$active = '<span class="element-invisible">' . t('(active tab)') . '</span>';
if (empty($link['localized_options']['html'])) {
  $link['title'] = check_plain($link['title']);
}
$link['localized_options']['html'] = TRUE;
$link_text = t('!local-task-title!active', array('!title' => $link['title'], '!active' => $active));
bowersox’s picture

thanks, @sun! that all makes sense.

Everett Zufelt’s picture

This is the patch from comment #37 with suggestions in #51 implemented.

mgifford’s picture

I applied this and it is working well. Think this is now RTBC!

bowersox’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
68 KB

This patch looks great. I tested to confirm it has no visual change in Garland/Minnelli or Seven. It correctly adds the "(active tab)" invisible text to both primary and secondary tabs. I've included one Garland screenshot where the visual appearance is unchanged while the invisible text is added to both the "Configure" primary tab and "Global settings" secondary tab.

Regarding @sun's suggestion about using $link['in_active_trail'], unfortunately the in_active_trail parameter is not available to theme_menu_item_link(). In the future if that becomes available we can upgrade to use it.

Owen Barton’s picture

Added an inline comment to indicate the purpose of the "(active)":

// Add text to indicate active tab for non-visual users.

Otherwise unchanged, so leaving RTBC.

bowersox’s picture

Yes, this new patch still applies cleanly and I tested to re-confirm that it works just like the last one. I support leaving it RTBC. Thanks, @Owen!

Dries’s picture

+++ includes/menu.inc	31 Aug 2009 21:17:19 -0000
@@ -1262,7 +1262,19 @@ function theme_menu_item_link($link) {
+    $link_text = t('!local-task-title!active', array('!local-task-title' => $link['title'], '!active' => $active));

That t() function looks a bit unnecessary?

While I can see how this can help screen readers, I'm actually surprised that this is the way to do it. I'd expect that screen readers would automatically know that this would be active based on the URL rather than some extra markup. Shouldn't we provide markup at the ul-level to communicate that this are tabs, and then let the screen reader inidicate what is the active tab?

Everett Zufelt’s picture

@Dries

Regarding the t() function, I believe that the reasoning behind this is that for some languages it is preferable to have the name of the tab displayed before the indication of "active tab" while in others it may be preferable to have "active tab" displayed before the name of the tab.

Regarding the best practices here, unfortunately no screen-reader I have used provides the user an indication if a link references the current page.

I do think that it is important to indicate that the ul is a tabstrip. perhaps the best method would be:
<h2>Node-title tabs</h2> or <h2>Tabs for Note-title</h2> likely hidden from view with class="element-invisible. Not being to familiar with the templating of nodes, would this be possible to do in theme_menu_local_tasks() or would it need to be done in the tpl file for each theme?

Pasqualle’s picture

why can't we just simply use something like

$link['attributes']['accessibility'] = t('hidden text for accessibility purposes');

but the accessibility problem of the active link can be easily fixed inside l() same as the active class is added..

you may add the required functionality to this issue: #566774: Accessibility: link title attribute

Everett Zufelt’s picture

@Pasqualle

Two different accessibility specialists involved with Drupal believe that the approach you have suggested is not appropriate, generalizing the functionality at worst will regress the accessibility of Drupal and at best requires far more research and brainstorming than the small hand full of accessibility specialists have time to contribute toward. Furthermore, in #526712: l() does not accept a parameter for supplemental text for screen-readers an approach to generalize the embeding of invisible text within links was discussed, and it was decided that generalizing, at least in te way discussed in that issue, was not appropriate.

The approach in this issue has been set to RTBC after quite a bit of discussion, both through the issue queue and on accessibility conference calls.

Status: Reviewed & tested by the community » Needs work

The last submitted patch failed testing.

sun’s picture

Issue tags: +D7 code freeze

Tagging.

Please re-roll.

mgifford’s picture

The last patch submitted relies on functions that no longer exist - http://drupal.org/files/issues/issue-521852-active-task-8.patch

@sun

Since this function no longer exists in D7, can you point me to the patch that describes how we're supposed to do this now?
http://api.drupal.org/api/search/6/menu_item_link

Found it rather frustrating scraping around to determine how to best pass along this information.

bowersox’s picture

It appears that #283723: Make menu_tree_output() return renderable output eliminated theme_menu_item(). Anyone who already has their head around the new code know how the best approach to pass along $active?

sun’s picture

Status: Needs work » Needs review
FileSize
2.08 KB

This should hopefully get you "somewhere".

mattyoung’s picture

sub

mgifford’s picture

FileSize
2.07 KB

Thanks sun, that helped out a great deal.

I had to modify it a bit, but seems to be working fine now.

Everett Zufelt’s picture

Status: Needs review » Needs work

Applied and tested this patch this morning. It appears to always add the active tab text to the first tab in the list, and not to any other tab, regardless of active status.

Does someone have an explanation of what changed so that I can attempt to reroll this patch?

mgifford’s picture

Status: Needs work » Needs review

@Everett which patch were you testing?

Setting this to needs review as I think #68 is working fine.

Everett Zufelt’s picture

Yes, the patch in #68 does work correctly. Thanks for rerolling with the required changes.

We do still need to figure out the best method of adding notification that the user is about to enter a tabstrip.

1. What is the best text

2. What function, or template, should we use to add the text.

Suggestions:

a. Tabstrip

b. Tabs for node-title

c. Node-title tabs

bowersox’s picture

@Everett

Usually the tabs come right after the node title, which is an h1. So what about <span class="element-invisible">Tabs</span>? It could be an h2 or a span. That might be better than repeating the node title twice in close proximity, especially on long pages? Do you agree?

Everett Zufelt’s picture

@Brandon

I wonder if theme_menu_local_tasks is the best place to place this. It is the function which themes the ul for the tabs. I question making the text a heading, as you say, the local tasks are normally displayed directly after the node title, which is a heading.

I also question if we need to provide text indicating the tabstrip at all. I imagine it would help new users. The word "Tabs" on its own does seem a little unclear to me.

What are your thoughts about "Tabs for this page" or something similar? If we were to make this a heading I think we can agree that it should be h2.

Status: Needs review » Needs work

The last submitted patch failed testing.

mgifford’s picture

Status: Needs work » Needs review

testing of bot was bust

Frank Ralf’s picture

bowersox’s picture

@Frank

Can you explain more about how that patch addresses this? This specific issue is to make the active tab (aka local task) have some invisible text in it for screenreaders saying "Active task" or a similar phrase.

-Brandon

Frank Ralf’s picture

@Brandon

The active vertical tab will be rendered as follows:

<li class="vertical-tab-button selected" tabindex="-1">
  <a href="#">
    <strong>URL path settings</strong>
    <span class="summary">No alias</span>
    <span id="active-vertical-tab" class="element-invisible">(active tab)</span>
  </a>
</li>

See Accessibility and Drupal > Drupal 7 accessibility notes.

Please note:
The vertical tabs are not "local tasks" as usual tabs but are derived from collapsible fieldsets by using jQuery .

hth
Frank

bowersox’s picture

FileSize
1.29 KB

Please review. This has the same functionality as the prior patch by @mgifford. FYI, this was RTBC before the Paris/September code freeze.

Tests passed:

  • No visual change -- verified in Garland and Seven
  • Correct tabs are marked as "active tab" including admin pages with 2 levels of local tasks (eg /admin/appearance/settings/garland)
  • Tested in Firefox 3.5 and Safari 4 on Mac

@Frank, Yes this patch essentially provides the same functionality in the way of an "(active tab)" indicator to screenreader users for the traditional tabs (View, Edit, etc) which often appear after the node title.

mgifford’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me Brandon. I tested it here http://drupal7.dev.openconcept.ca

Setting it back to RTBC.

mgifford’s picture

This was first RTBC now over two months ago. - Bump!

webchick’s picture

Status: Reviewed & tested by the community » Needs work

@mgifford and others: Pursuant to the Drupal 7 development schedule, patches around API clean-ups and feature exceptions were given priority during the period from Drupalcon to Oct. 15 (later extended to Oct. 17). Over the next month (from now until Dec. 1) is where we focus on accessibility, usability, and performance. Hence why some of these have been sitting in the queue for awhile.

Just a couple of things:

+++ includes/menu.inc	12 Oct 2009 04:21:00 -0000
@@ -1357,7 +1357,19 @@ function theme_menu_link(array $variable
+    if (empty($link['localized_options']['html'])) {
+      $link['title'] = check_plain($link['title']);
+    }

Can you document why you're doing this, since at first glance this appeared to be a bug, since l() is also check_plain()ing these links? (though, granted, not for those with HTML set to TRUE.)

+++ includes/menu.inc	12 Oct 2009 04:21:00 -0000
@@ -1357,7 +1357,19 @@ function theme_menu_link(array $variable
+    $link_text = t('!local-task-title!active', array('!local-task-title' => $link['title'], '!active' => $active));

I think there be a space between these two strings, either in this t() function call, or above where we make it an 'active tab'. Right now, if you disable CSS, the words get all smashed together like:

"Content(active tab)"

This review is powered by Dreditor.

bowersox’s picture

Here's a proposed comment that hopefully makes sense:

+++ includes/menu.inc	12 Oct 2009 04:21:00 -0000
@@ -1357,7 +1357,19 @@ function theme_menu_link(array $variable
+    // If the link does not contain html already, check_plain() it now.
+    // After we set html=true the link will not be check_plain'd by l().
+    if (empty($link['localized_options']['html'])) {
+      $link['title'] = check_plain($link['title']);
+    }
+    $link['localized_options']['html'] = TRUE;

Also, I propose we use t('!local-task-title !active',...) with a space in the translatable string. That seems to be cleanest but I am happy to re-roll this patch either way if the group prefers otherwise.

mgifford’s picture

FileSize
1.43 KB

@webchick - I'm not always as patient as I should be. I'd misunderstood when accessibility issues were going to be addressed (but do think that others working on this had too).

I tracked back the check_plain()ing links to @sun's contribution here:
http://drupal.org/node/521852#comment-1980856

As he said a bit earlier "By setting 'html' = TRUE, you potentially expose unsafe HTML from the original menu title."

So I'm fine with putting in a space. I wondered about that myself as I'd seen the same issue months ago. However, screen-readers don't need the space to interpret it correctly. Still good for those that turn off their CSS every now and again.

Brandon, thanks for the offer to re-roll. I'm still recovering from the flu so had to apply the patch, and bring myself up to speed on this issue again before I could comment on it. So I've got a patch with the changes.

sun’s picture

+++ includes/menu.inc	2 Nov 2009 16:09:20 -0000
@@ -1408,7 +1408,22 @@ function theme_menu_link(array $variable
+    // If the link does not contain html already, check_plain() it now.
+    // After we set html=true the link will not be check_plain'd by l().

1) HTML, when referring to HTML, always uppercase.

2) 'html', when referring to the 'html' property, uses the capitalization of the property, and, single quotes around it.

3) TRUE is always uppercase.

4) Replace "check_plain'd" with "sanitized".

This review is powered by Dreditor.

mgifford’s picture

Status: Needs work » Needs review
FileSize
1.43 KB

Thanks sun. Standards are important and sorry I wasn't aware enough of these ones.

> + // If the link does not contain HTML already, check_plain() it now.
> + // After we set 'html'=TRUE the link will not be sanitized by l().

I've updated the patch.

Status: Needs review » Needs work

The last submitted patch failed testing.

mgifford’s picture

I'm not sure why this is failing now unless the node.test has changed.

Anyways, not sure how to resolve this error:

Edit tab found. Other node.test 209 PageEditTestCase->testPageEdit()

I tried asserting this line in (as this is what is inside the anchor tag:

    $this->assertLink(t('Edit <span class="element-invisible">(active tab)</span>'), 0, t('Edit tab found.'));

and then I get xpath errors:

SimpleXMLElement::xpath(): Invalid predicate Warning drupal_web_test_case.php 1696 DrupalWebTestCase->xpath() Exception
SimpleXMLElement::xpath(): xmlXPathEval: evaluation failed Warning drupal_web_test_case.php 1696 DrupalWebTestCase->xpath()

Not sure where to go from here.

sun’s picture

That happens, because assertLink() uses xpath to search for all anchor tags in the document and tests each one against the passed string.

  protected function assertLink($label, $index = 0, $message = '', $group = 'Other') {
    $links = $this->xpath('//a[text()="' . $label . '"]');
    $message = ($message ?  $message : t('Link with label %label found.', array('%label' => $label)));
    return $this->assert(isset($links[$index]), $message, $group);
  }

So:

1) Basically, you should be able to test for "Edit (active tab)", because it tests for the contained text of an anchor tag.

2) However, you cannot use t('Edit (active tab)'), because t() does not return the expected value here. You need to pass the actual value to t() with the proper arguments (as in the original code).

Technically totally correct would thus be:

$active = '<span class="element-invisible">' . t('(active tab)') . '</span>';
$link_text = t('!local-task-title !active', array('!local-task-title' => t('Edit'), '!active' => $active));
$this->assertLink(strip_tags($link_text), 0, t('Edit tab found.'));
mgifford’s picture

Ok, this sounds great. Detailed and useful explanation. Thanks!

But it didn't work. I tried the totally correct answer you provided and also just 'Edit (active tab)' and it didn't like either of them.

Still got the same error in SimpleTest. Don't know what I could have done wrong.

bowersox’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
2.46 KB

Please review this updated patch. The only change is to modules/node/node.test:

     // Check that the title and body fields are displayed with the correct values.
-    $this->assertLink(t('Edit'), 0, t('Edit tab found.'));
+    $active = '<span class="element-invisible">' . t('(active tab)') . '</span>';
+    $link_text = t('!local-task-title !active', array('!local-task-title' => t('Edit'), '!active' => $active));
+    $this->assertText(strip_tags($link_text), 0, t('Edit tab found and marked active.'));

@sun: this is nearly identical to your suggestion, but uses assertText() instead of assertLink(). I believe assertLink() does not handle checking for link text that contains HTML mixed in. I believe it's safe to check for text instead of a link because the text "Edit (active tab)" would not appear on the page for any other reason. I couldn't find assertLink(strip_tags(...)) used anywhere else but if you have a suggestion, let us know.

mgifford’s picture

Status: Needs work » Reviewed & tested by the community

I applied the changes locally and it's working fine. With these changes and a happy bot I'm setting it back to RTBC.

webchick’s picture

Status: Needs review » Fixed

Looks good! Committed to HEAD.

mgifford’s picture

Excellent! Thanks everyone.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

Liam Morland’s picture