[FIX] web: Add missing aria attributes in menus

Some aria attributes were missing on menus, and are required to ensure a
good compatibility with keyboard users and screen readers.

To solve the above, the following attributes are included:
- `aria-expanded`: this should be present in all dropdown menus. This is
  set on elements with class `dropdown-toggle` and `o_closed_menu`.
- `role`: used to improve given information about what elements are for.
  It is added in the following cases:
  1. Non-button elements that have the class `dropdown-menu` are set to
     `role="button"`.
  2. Menu items that have the class `dropdown-item` are set to
     `role="menuitem"`.
  3. Menu separators that have the class `dropdown-divider` are set to
     `role="separator"`.

This is a continuation of baa1c515c5

closes odoo/odoo#29719
This commit is contained in:
Luis González
2018-12-22 22:22:14 +00:00
parent 67d3d30cf2
commit c0104303c6
8 changed files with 25 additions and 22 deletions
+1 -1
View File
@@ -506,7 +506,7 @@ var dom = {
var $dropdownMenu = $('<ul/>', {class: 'dropdown-menu'});
$extraItemsToggle = $('<li/>', {class: 'nav-item dropdown o_extra_menu_items'})
.append($('<a/>', {href: '#', class: 'nav-link dropdown-toggle o-no-caret', 'data-toggle': 'dropdown'})
.append($('<a/>', {role: 'button', href: '#', class: 'nav-link dropdown-toggle o-no-caret', 'data-toggle': 'dropdown', 'aria-expanded': false})
.append($('<i/>', {class: 'fa fa-plus'})))
.append($dropdownMenu);
$extraItemsToggle.insertAfter($items.last());
@@ -69,6 +69,7 @@ return Widget.extend({
},
toggle_save_menu: function (is_open) {
this.$save_search
.attr('aria-expanded', !(_.isUndefined(is_open)) ? is_open : (this.$save_search.attr('aria-expanded') === 'false'))
.toggleClass('o_closed_menu', !(_.isUndefined(is_open)) ? !is_open : undefined)
.toggleClass('o_open_menu', is_open);
this.$save_name.toggle(is_open);
@@ -250,7 +251,7 @@ return Widget.extend({
var $filter = $('<div>', {class: 'position-relative'})
.addClass(filter.user_id ? 'o-searchview-custom-private'
: 'o-searchview-custom-public')
.append($('<a>', {href: '#', class: 'dropdown-item'}).text(filter.name))
.append($('<a>', {role: 'menuitem', href: '#', class: 'dropdown-item'}).text(filter.name))
.append($('<span>', {
class: 'fa fa-trash-o o-remove-filter',
on: {
@@ -199,6 +199,7 @@ var FiltersMenu = DropdownMenu.extend({
}
$.when(def).then(function () {
self.$addCustomFilter
.attr('aria-expanded', self.generatorMenuIsOpen)
.toggleClass('o_closed_menu', !self.generatorMenuIsOpen)
.toggleClass('o_open_menu', self.generatorMenuIsOpen);
self.$('.o_add_filter_menu').toggle();
@@ -177,6 +177,7 @@ var GroupByMenu = DropdownMenu.extend({
_toggleCustomGroupMenu: function () {
this.generatorMenuIsOpen = !this.generatorMenuIsOpen;
this._renderGeneratorMenu();
this.$addCustomGroup.attr('aria-expanded', this.generatorMenuIsOpen);
if (this.generatorMenuIsOpen) {
this.$groupSelector.focus();
}
@@ -53,7 +53,7 @@ var SwitchCompanyMenu = Widget.extend({
} else {
a = '<span style="margin-right: 24px;"/>';
}
companiesList += '<a href="#" class="dropdown-item" data-menu="company" data-company-id="' +
companiesList += '<a role="menuitem" href="#" class="dropdown-item" data-menu="company" data-company-id="' +
company[0] + '">' + a + company[1] + '</a>';
});
this.$('.dropdown-menu').html(companiesList);
+16 -16
View File
@@ -228,7 +228,7 @@
<t t-name="WebClient.DebugManager">
<li class="o_debug_manager" role="menuitem">
<a href="#" title="Open Developer Tools" aria-label="Open Developer Tools" data-toggle="dropdown" aria-expanded="false" tabindex="-1" data-display="static">
<a role="button" href="#" title="Open Developer Tools" aria-label="Open Developer Tools" data-toggle="dropdown" aria-expanded="false" tabindex="-1" data-display="static">
<span class="fa fa-bug"/>
</a>
<div class="dropdown-menu dropdown-menu-right o_debug_dropdown" role="menu"/>
@@ -843,7 +843,7 @@
<span class="o_badge_text" t-att-title="el.display_name"><span role="img" t-attf-aria-label="Tag color: #{colornames[color]}"/><t t-esc="el.display_name"/></span>
</t>
<t t-if="colorField">
<a href="#" class="dropdown-toggle o-no-caret" data-toggle="dropdown">
<a role="button" href="#" class="dropdown-toggle o-no-caret" data-toggle="dropdown" aria-expanded="false">
<t t-raw="_badge_text"/>
</a>
</t>
@@ -1070,10 +1070,10 @@
<t t-if="widget.items.length !== 0">
<t t-set="group" t-value="0"/>
<t t-foreach="widget.items" t-as="item">
<div t-if="group !== item.groupId" class="dropdown-divider" data-removable="1"/>
<div t-if="group !== item.groupId" role="separator" class="dropdown-divider" data-removable="1"/>
<div class="o_menu_item"
t-att-data-id="item.itemId">
<a href="#" t-attf-class="dropdown-item#{item.isActive ? ' selected ' : ''}">
<a role="menuitem" href="#" t-attf-class="dropdown-item#{item.isActive ? ' selected ' : ''}">
<t t-esc="item.description"/>
<span t-if="item.isRemovable" t-att-data-id="item.itemId" class="o_trash_button" > <span class="fa fa-trash-o"></span> </span>
<span t-if="item.hasOptions" t-att-data-id="item.itemId" class="o_submenu_switcher">
@@ -1083,10 +1083,10 @@
<ul t-if="item.hasOptions &amp;&amp; item.isOpen" class="list-unstyled">
<t t-set="subgroup" t-value="0"/>
<t t-foreach="_.values(item.options)" t-as="option">
<li t-if="subgroup !== option.groupId" class="dropdown-divider" data-removable="1"/>
<li t-if="subgroup !== option.groupId" role="separator" class="dropdown-divider" data-removable="1"/>
<li class="o_item_option"
t-att-data-item_id="item.itemId" t-att-data-option_id="option.optionId">
<a href="#" t-attf-class="dropdown-item#{item.currentOptionId === option.optionId ? ' selected': ''} pl-5"><t t-esc="option.description"/></a>
<a role="menuitem" href="#" t-attf-class="dropdown-item#{item.currentOptionId === option.optionId ? ' selected': ''} pl-5"><t t-esc="option.description"/></a>
</li>
<t t-set="subgroup" t-value="option.groupId"/>
</t>
@@ -1107,8 +1107,8 @@
</div>
<t t-name="GroupbyMenuGenerator">
<div t-if="Object.keys(widget.presentedFields).length !== 0 &amp;&amp; widget.items.length !== 0" class="dropdown-divider o_generator_menu"/>
<button t-if="Object.keys(widget.presentedFields).length !== 0" type="button" class="dropdown-item o_generator_menu o_add_custom_group">Add Custom Group</button>
<div t-if="Object.keys(widget.presentedFields).length !== 0 &amp;&amp; widget.items.length !== 0" role="separator" class="dropdown-divider o_generator_menu"/>
<button t-if="Object.keys(widget.presentedFields).length !== 0" type="button" class="dropdown-item o_generator_menu o_add_custom_group" aria-expanded="false">Add Custom Group</button>
<div t-if="widget.generatorMenuIsOpen" class="dropdown-item-text o_generator_menu">
<select class="w-auto o_input o_group_selector o_add_group">
<t t-foreach="widget.presentedFields" t-as="field">
@@ -1122,8 +1122,8 @@
</t>
<t t-name="FiltersMenuGenerator">
<div class="dropdown-divider"/>
<button t-if="Object.keys(widget.fields).length !== 0" type="button" class="dropdown-item o_add_custom_filter o_closed_menu">Add Custom Filter</button>
<div role="separator" class="dropdown-divider"/>
<button t-if="Object.keys(widget.fields).length !== 0" type="button" class="dropdown-item o_add_custom_filter o_closed_menu" aria-expanded="false">Add Custom Filter</button>
<div class="dropdown-item-text o_add_filter_menu">
<button class="btn btn-primary o_apply_filter" type="button">Apply</button>
<button class="btn btn-secondary o_add_condition" type="button">
@@ -1353,9 +1353,9 @@
<span class="fa fa-star"/> Favorites <span t-if="widget.isMobile" class="fa fa-chevron-right float-right mt4"/>
</button>
<div class="dropdown-menu o_favorites_menu" role="menu">
<div class="dropdown-divider user_filter"/>
<div class="dropdown-divider shared_filter"/>
<a role="menuitem" href="#" class="dropdown-item o_save_search o_closed_menu">Save current search</a>
<div role="separator" class="dropdown-divider user_filter"/>
<div role="separator" class="dropdown-divider shared_filter"/>
<a role="button" href="#" class="dropdown-item o_save_search o_closed_menu" aria-expanded="false">Save current search</a>
<div role="menuitem" class="dropdown-item-text o_save_name">
<input type="text" class="o_input"></input>
</div>
@@ -1372,7 +1372,7 @@
</div>
</div>
<div class="dropdown-item-text o_save_name">
<button role="menuitem" type="button" class="btn btn-primary">Save</button>
<button type="button" class="btn btn-primary">Save</button>
</div>
</div>
</div>
@@ -1532,7 +1532,7 @@
<t t-name="UserMenu">
<li class="o_user_menu">
<a class="dropdown-toggle" data-toggle="dropdown" data-display="static" aria-expanded="false" href="#">
<a role="button" class="dropdown-toggle" data-toggle="dropdown" data-display="static" aria-expanded="false" href="#">
<img class="rounded-circle oe_topbar_avatar" t-att-src="_s + '/web/static/src/img/user_menu_avatar.png'" alt="Avatar"/>
<span class="oe_topbar_name"/>
</a>
@@ -1553,7 +1553,7 @@
<t t-name="SwitchCompanyMenu">
<li class="o_switch_company_menu">
<a class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false" href="#" aria-label="Dropdown menu" title="Dropdown menu">
<a role="button" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false" href="#" aria-label="Dropdown menu" title="Dropdown menu">
<span t-attf-class="#{widget.isMobile ? 'fa fa-building-o' : 'oe_topbar_name'}"/>
</a>
<div class="dropdown-menu dropdown-menu-right" role="menu"/>
+2 -2
View File
@@ -24,8 +24,8 @@
<a t-if="widget.deletable and widget.id" role="menuitem" class="dropdown-item o_column_delete" href="#">Delete</a>
</t>
<t t-if="widget.has_active_field and widget.archivable">
<a href="#" class="dropdown-item o_column_archive_records">Archive All</a>
<a href="#" class="dropdown-item o_column_unarchive_records">Unarchive All</a>
<a role="menuitem" href="#" class="dropdown-item o_column_archive_records">Archive All</a>
<a role="menuitem" href="#" class="dropdown-item o_column_unarchive_records">Unarchive All</a>
</t>
</div>
</span>
+1 -1
View File
@@ -97,7 +97,7 @@
<t t-extend="UserMenu.Actions">
<t t-jquery=".dropdown-divider" t-operation="before">
<a href="#" data-menu="shortcuts" class="dropdown-item d-none d-md-inline-block">Shortcuts</a>
<a role="menuitem" href="#" data-menu="shortcuts" class="dropdown-item d-none d-md-inline-block">Shortcuts</a>
</t>
</t>