Skip to content

Commit

Permalink
MDL-70846 accessibility: update tree attributes to pass a11y check
Browse files Browse the repository at this point in the history
- Move aria-* atrributes from <p> to <li>
- Move "role" attribute from <p> to <li>
- Update behat tests

Based on reference implementation from:
- https://www.w3.org/TR/wai-aria-practices-1.1/examples/treeview/treeview-2/treeview-2a.html
- https://www.w3.org/WAI/GL/wiki/Using_ARIA_trees
  • Loading branch information
dcai committed Jun 3, 2021
1 parent 30b8ad5 commit e3690a3
Show file tree
Hide file tree
Showing 14 changed files with 67 additions and 66 deletions.
2 changes: 1 addition & 1 deletion blocks/navigation/amd/build/ajax_response_renderer.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions blocks/navigation/amd/src/ajax_response_renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,22 +68,22 @@ define([
var icon = null;
var isBranch = (node.expandable || node.haschildren) ? true : false;

li.attr('role', 'treeitem');
p.addClass('tree_item');
p.attr('id', id);
p.attr('role', 'treeitem');
// Negative tab index to allow it to receive focus.
p.attr('tabindex', '-1');

if (node.requiresajaxloading) {
p.attr('data-requires-ajax', true);
p.attr('data-node-id', node.id);
p.attr('data-node-key', node.key);
p.attr('data-node-type', node.type);
li.attr('data-requires-ajax', true);
li.attr('data-node-id', node.id);
li.attr('data-node-key', node.key);
li.attr('data-node-type', node.type);
}

if (isBranch) {
li.addClass('collapsed contains_branch');
p.attr('aria-expanded', false);
li.attr('aria-expanded', false);
p.addClass('branch');
}

Expand Down Expand Up @@ -141,14 +141,14 @@ define([
ul.append(li);

if (node.children && node.children.length) {
buildDOM(p, node.children);
buildDOM(li, node.children);
} else if (isBranch && !node.requiresajaxloading) {
li.removeClass('contains_branch');
p.addClass('emptybranch');
}
});

rootElement.parent().append(ul);
rootElement.append(ul);
var id = rootElement.attr('id') + '_group';
ul.attr('id', id);
rootElement.attr('aria-owns', id);
Expand All @@ -167,8 +167,8 @@ define([
item.attr('aria-expanded', true);
Aria.unhide(group);
} else {
if (element.parent().hasClass('contains_branch')) {
element.parent().removeClass('contains_branch');
if (element.hasClass('contains_branch')) {
element.removeClass('contains_branch');
element.addClass('emptybranch');
}
}
Expand Down
18 changes: 10 additions & 8 deletions blocks/navigation/renderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ protected function navigation_node($items, $attrs=array(), $expansionlimit=null,
$lis = array();
// Set the number to be static for unique id's.
static $number = 0;
$htmlidprefix = html_writer::random_id();
foreach ($items as $item) {
$number++;
if (!$item->display && !$item->contains_active_node()) {
Expand All @@ -90,8 +91,8 @@ protected function navigation_node($items, $attrs=array(), $expansionlimit=null,
$content = $item->get_content();
$title = $item->get_title();
$ulattr = ['id' => $id . '_group', 'role' => 'group'];
$liattr = ['class' => [$item->get_css_type(), 'depth_'.$depth]];
$pattr = ['class' => ['tree_item'], 'role' => 'treeitem'];
$liattr = ['class' => [$item->get_css_type(), 'depth_'.$depth], 'role' => 'treeitem'];
$pattr = ['class' => ['tree_item']];
$pattr += !empty($item->id) ? ['id' => $item->id] : [];
$isbranch = $isexpandable && ($item->children->count() > 0 || ($item->has_children() && (isloggedin() || $item->type <= navigation_node::TYPE_CATEGORY)));
$hasicon = ((!$isbranch || $item->type == navigation_node::TYPE_ACTIVITY || $item->type == navigation_node::TYPE_RESOURCE) && $item->icon instanceof renderable);
Expand All @@ -112,7 +113,7 @@ protected function navigation_node($items, $attrs=array(), $expansionlimit=null,
continue;
}

$nodetextid = 'label_' . $depth . '_' . $number;
$nodetextid = $htmlidprefix . '_label_' . $depth . '_' . $number;
$attributes = array('tabindex' => '-1', 'id' => $nodetextid);
if ($title !== '') {
$attributes['title'] = $title;
Expand All @@ -135,19 +136,20 @@ protected function navigation_node($items, $attrs=array(), $expansionlimit=null,
}

if ($isbranch) {
$ariaexpanded = $item->has_children() && (!$item->forceopen || $item->collapse);
$pattr['class'][] = 'branch';
$liattr['class'][] = 'contains_branch';
$pattr += ['aria-expanded' => ($item->has_children() && (!$item->forceopen || $item->collapse)) ? "false" : "true"];
$liattr += ['aria-expanded' => $ariaexpanded ? "false" : "true"];
if ($item->requiresajaxloading) {
$pattr += [
$liattr += [
'data-requires-ajax' => 'true',
'data-loaded' => 'false',
'data-node-id' => $item->id,
'data-node-key' => $item->key,
'data-node-type' => $item->type
];
} else {
$pattr += ['aria-owns' => $id . '_group'];
$liattr += ['aria-owns' => $id . '_group'];
}
}

Expand All @@ -161,8 +163,8 @@ protected function navigation_node($items, $attrs=array(), $expansionlimit=null,
$liattr['class'] = join(' ', $liattr['class']);
$pattr['class'] = join(' ', $pattr['class']);

$pattr += $depth == 1 ? ['data-collapsible' => 'false'] : [];
if (isset($pattr['aria-expanded']) && $pattr['aria-expanded'] === 'false') {
$liattr += $depth == 1 ? ['data-collapsible' => 'false'] : [];
if (isset($liattr['aria-expanded']) && $liattr['aria-expanded'] === 'false') {
$ulattr += ['aria-hidden' => 'true'];
}

Expand Down
2 changes: 1 addition & 1 deletion blocks/navigation/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
background-image: url('[[pix:t/collapsed_empty]]');
}

.block_navigation .block_tree [aria-expanded="false"].loading {
.block_navigation .block_tree [aria-expanded="false"] p.loading {
background-image: url('[[pix:i/loading_small]]');
}

Expand Down
18 changes: 8 additions & 10 deletions blocks/settings/renderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,24 +77,24 @@ protected function navigation_node(navigation_node $node, $attrs=array(), $depth
$content = $this->output->render($item);
$id = $item->id ? $item->id : html_writer::random_id();
$ulattr = ['id' => $id . '_group', 'role' => 'group'];
$liattr = ['class' => [$item->get_css_type(), 'depth_'.$depth], 'tabindex' => '-1'];
$pattr = ['class' => ['tree_item'], 'role' => 'treeitem'];
$liattr = ['class' => [$item->get_css_type(), 'depth_'.$depth], 'tabindex' => '-1', 'role' => 'treeitem'];
$pattr = ['class' => ['tree_item']];
$pattr += !empty($item->id) ? ['id' => $item->id] : [];
$hasicon = (!$isbranch && $item->icon instanceof renderable);

if ($isbranch) {
$liattr['class'][] = 'contains_branch';
if (!$item->forceopen || (!$item->forceopen && $item->collapse) || ($item->children->count() == 0
&& $item->nodetype == navigation_node::NODETYPE_BRANCH)) {
$pattr += ['aria-expanded' => 'false'];
$liattr += ['aria-expanded' => 'false'];
} else {
$pattr += ['aria-expanded' => 'true'];
$liattr += ['aria-expanded' => 'true'];
}
if ($item->requiresajaxloading) {
$pattr['data-requires-ajax'] = 'true';
$pattr['data-loaded'] = 'false';
$liattr['data-requires-ajax'] = 'true';
$liattr['data-loaded'] = 'false';
} else {
$pattr += ['aria-owns' => $id . '_group'];
$liattr += ['aria-owns' => $id . '_group'];
}
} else if ($hasicon) {
$liattr['class'][] = 'item_with_icon';
Expand All @@ -106,7 +106,6 @@ protected function navigation_node(navigation_node $node, $attrs=array(), $depth
if (!empty($item->classes) && count($item->classes) > 0) {
$pattr['class'] = array_merge($pattr['class'], $item->classes);
}
$nodetextid = 'label_' . $depth . '_' . $number;

// class attribute on the div item which only contains the item content
$pattr['class'][] = 'tree_item';
Expand All @@ -119,15 +118,14 @@ protected function navigation_node(navigation_node $node, $attrs=array(), $depth
$liattr['class'] = join(' ', $liattr['class']);
$pattr['class'] = join(' ', $pattr['class']);

if (isset($pattr['aria-expanded']) && $pattr['aria-expanded'] === 'false') {
if (isset($liattr['aria-expanded']) && $liattr['aria-expanded'] === 'false') {
$ulattr += ['aria-hidden' => 'true'];
}

$content = html_writer::tag('p', $content, $pattr) . $this->navigation_node($item, $ulattr, $depth + 1);
if (!empty($item->preceedwithhr) && $item->preceedwithhr===true) {
$content = html_writer::empty_tag('hr') . $content;
}
$liattr['aria-labelledby'] = $nodetextid;
$content = html_writer::tag('li', $content, $liattr);
$lis[] = $content;
}
Expand Down
2 changes: 1 addition & 1 deletion blocks/settings/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
background-image: url('[[pix:t/collapsed_empty]]');
}

.block_settings .block_tree [aria-expanded="false"].loading {
.block_settings .block_tree [aria-expanded="false"] p.loading {
background-image: url('[[pix:i/loading_small]]');
}
/*rtl:raw:
Expand Down
Loading

0 comments on commit e3690a3

Please sign in to comment.