summaryrefslogtreecommitdiff
path: root/addons/project
diff options
context:
space:
mode:
authorkujiu <kujiu@users.noreply.github.com>2018-06-22 21:22:21 +0200
committerGitHub <noreply@github.com>2018-06-22 21:22:21 +0200
commit9de1bc0eef6f5bfaa2a8d745431caa361ae91548 (patch)
tree342ddbf7fb2b7303606f864ff9f68cbf1bd8814e /addons/project
parent864357203fc6222cc989b99a5e9f428cd53f413f (diff)
[IMP] Improve compatibility with screen readers (accessibility) (#24574)
Today, Odoo is really tricky to use without seeing the screen, it must be improved to be usable. This PR forbid to use labels without a "for" attribute, add some title, rule and aria attributes in HTML. With that, Odoo will be fully usable with a screen reader. * [IMP] Labels must have a for attribute. Improve accessibility. * [IMP] Better error message when trying to read a missing cached value * [FIX] Add some aria-label and title attributes for screen readers. * [FIX] Template name is not included in the error message in case of SyntaxError in QWeb * [FIX] Improve the Tour failed at step error message to be more explicit. * [IMP] Add aria-labels * [FIX] Add missing aria-label on failing test * [IMP] aria-hidden means hidden. Fix all bad aria-hidden and hide aria-hidden for all. * [IMP] Color names on kanban views and many2many tags * [IMP] Add some checks on views for accessibility. * [IMP] Add `alt` attribute on `img` tags. * [IMP] Add aria-label and title on non-described icons * [IMP] Add button role to widgets with btn class * [IMP] Translate aria and formatted attributes. * [IMP] Remove wrong aria-labelledby * [IMP] Add menu role on dropdowns * [IMP] Buttons must be focusable * [IMP] Add aria attributes on progress bars * [IMP] Improve accessibility of basic widgets * [IMP] Change main layout to more semantic tags * [IMP] Add menuitem role when missing * [IMP] Remove wrong role='presentation' * [IMP] Improve accessibility of tab panels * [IMP] Add aria-invalid on invalid fields * [IMP] Add aria-sort on ordered columns * [IMP] Add role on alerts * [IMP] Use dialog role, header, main and footer tags for modals * [IMP] Add labels on o_status * [IMP] Improve accessibility of kanban view with feeds and articles * [IMP] Add alerts in case of new messages * [IMP] Add widget, navigation or img role to aria-labelled items
Diffstat (limited to 'addons/project')
-rw-r--r--addons/project/data/project_mail_template_data.xml2
-rw-r--r--addons/project/static/src/js/tour.js2
-rw-r--r--addons/project/static/src/xml/project.xml2
-rw-r--r--addons/project/static/tests/project_tests.js6
-rw-r--r--addons/project/views/project_portal_templates.xml6
-rw-r--r--addons/project/views/project_rating_templates.xml29
-rw-r--r--addons/project/views/project_views.xml64
7 files changed, 59 insertions, 52 deletions
diff --git a/addons/project/data/project_mail_template_data.xml b/addons/project/data/project_mail_template_data.xml
index a7d4dcc15f4..73f9a844ba9 100644
--- a/addons/project/data/project_mail_template_data.xml
+++ b/addons/project/data/project_mail_template_data.xml
@@ -18,7 +18,7 @@
<tbody>
<tr>
<td style="padding:2px;width:30%;">
- <img src="web/static/src/img/logo.png"/>
+ <img src="web/static/src/img/logo.png" alt="Logo"/>
</td>
<td style="vertical-align: top; padding: 8px 10px;text-align: left;font-size: 14px;">
<a href="web/login" style="float:right ;margin:15px auto;background: #875A7B;border-radius: 5px;color: #ffffff;font-size: 16px;padding: 10px 20px 10px 20px;text-decoration: none;">Auto Login</a>
diff --git a/addons/project/static/src/js/tour.js b/addons/project/static/src/js/tour.js
index e49c1325136..18087cdbc3b 100644
--- a/addons/project/static/src/js/tour.js
+++ b/addons/project/static/src/js/tour.js
@@ -27,7 +27,7 @@ tour.register('project_tour', {
content: _t('This will create new project and redirect us to its tasks.'),
position: 'right',
run: function (actions) {
- actions.auto(".modal:visible .btn.btn-primary");
+ actions.auto('[role="dialog"]:visible .btn.btn-primary');
},
}, {
trigger: ".o_kanban_project_tasks .o_column_quick_create input",
diff --git a/addons/project/static/src/xml/project.xml b/addons/project/static/src/xml/project.xml
index 0406bb9f855..99949bf1861 100644
--- a/addons/project/static/src/xml/project.xml
+++ b/addons/project/static/src/xml/project.xml
@@ -4,7 +4,7 @@
<div t-if="attachment_ids.length > 0" class="ml32 mr0 o_kanban_task_cover_container">
<t t-foreach="attachment_ids" t-as="attachment">
<div t-attf-class="o_kanban_task_cover_image#{cover_id == attachment['id'] ? ' o_selected' : ''}">
- <img t-attf-src="/web/image/#{attachment['id']}?unique=1" t-att-data-id="attachment['id']" t-att-data-name="attachment['name']"/>
+ <img t-attf-src="/web/image/#{attachment['id']}?unique=1" t-att-data-id="attachment['id']" t-att-data-name="attachment['name']" alt="Attachment"/>
</div>
</t>
</div>
diff --git a/addons/project/static/tests/project_tests.js b/addons/project/static/tests/project_tests.js
index e659658a7a2..e8994c41898 100644
--- a/addons/project/static/tests/project_tests.js
+++ b/addons/project/static/tests/project_tests.js
@@ -118,12 +118,12 @@ QUnit.module('project', {
assert.strictEqual(kanban.$('img').length, 0, "Initially there is no image.");
kanban.$('.o_dropdown_kanban [data-type=set_cover]').eq(0).click();
// single click on image
- $('.modal').find("img[data-id='1']").click();
- $('.modal-footer .btn-primary').click();
+ $('[role="dialog"]').find("img[data-id='1']").click();
+ $('footer.modal-footer .btn-primary').click();
assert.strictEqual(kanban.$('img[data-src*="/web/image/1"]').length, 1, "Image inserted in record");
$('.o_dropdown_kanban [data-type=set_cover]').eq(1).click();
// double click on image
- $('.modal').find("img[data-id='2']").dblclick();
+ $('[role="dialog"]').find("img[data-id='2']").dblclick();
assert.strictEqual(kanban.$('img[data-src*="/web/image/2"]').length, 1, "Image inserted after double click");
// varify write on both kanban record
assert.verifySteps([1,2]);
diff --git a/addons/project/views/project_portal_templates.xml b/addons/project/views/project_portal_templates.xml
index 391f46b38a7..006b9ec4a0e 100644
--- a/addons/project/views/project_portal_templates.xml
+++ b/addons/project/views/project_portal_templates.xml
@@ -101,8 +101,8 @@
</div>
<div class="col-md-6">
<div class="pull-right">
- <a t-attf-href="/my/tasks?filterby=#{project.id}" class="btn btn-default btn-lg">
- <span class="fa fa-tasks" />
+ <a role="button" t-attf-href="/my/tasks?filterby=#{project.id}" class="btn btn-default btn-lg">
+ <span class="fa fa-tasks" role="img" aria-label="Tasks" title="Tasks"/>
<span t-esc="project.task_count" />
<span t-field="project.label_tasks" />
</a>
@@ -170,7 +170,7 @@
<h4>
Task - <span t-field="task.name"/> (#<span t-esc="task.id"/>)
<span t-field="task.stage_id.name" class="pull-right label label-info" title="Current stage of this task"/>
- <a class="btn btn-info" t-att-href="'/web#return_label=Website&amp;model=project.task&amp;id=%s&amp;view_type=form' % (task.id)" groups="project.group_project_user">Edit Task</a>
+ <a role="button" class="btn btn-info" t-att-href="'/web#return_label=Website&amp;model=project.task&amp;id=%s&amp;view_type=form' % (task.id)" groups="project.group_project_user">Edit Task</a>
</h4>
</div>
</div>
diff --git a/addons/project/views/project_rating_templates.xml b/addons/project/views/project_rating_templates.xml
index 7ac2e9463ba..f1c0fa297a9 100644
--- a/addons/project/views/project_rating_templates.xml
+++ b/addons/project/views/project_rating_templates.xml
@@ -27,10 +27,10 @@
<span class="label label-default pull-right"><t t-esc="project.privacy_visibility"/></span>
<h4><t t-esc="project.name"/></h4>
<p t-if="project.date" class="text-muted">
- <i class="fa fa-calendar"/> End date : <t t-esc="project.date"/>
+ <span class="fa fa-calendar"> End date</span> : <t t-esc="project.date"/>
</p>
<p t-if="project.alias_name and project.alias_domain" class="text-muted">
- <i class="fa fa-envelope"/> Email : <t t-esc="project.alias_name"/>@<t t-esc="project.alias_domain"/>
+ <span class="fa fa-envelope"> Email</span> : <t t-esc="project.alias_name"/>@<t t-esc="project.alias_domain"/>
</p>
<div class="row">
<div class="col-md-6 text-center">
@@ -41,8 +41,8 @@
</div>
<div class="caption">
<p>
- <a t-att-href="'/project/rating/%s' % project.id" class="btn btn-primary btn-lg btn-block">
- <i class="fa fa-arrow-circle-right "/> See the feedbacks
+ <a role="button" t-att-href="'/project/rating/%s' % project.id" class="btn btn-primary btn-lg btn-block">
+ <span class="fa fa-arrow-circle-right "> See the feedbacks</span>
</a>
</p>
</div>
@@ -79,8 +79,8 @@
<t t-foreach="partner_stats.values()" t-as="partner_stat">
<div class="row mt8" t-if="partner_stat['total']" title="Top 5 partner ratings of last 15 days.">
<div class="col-sm-1">
- <img t-if="partner_stat['rated_partner'].image" class="o_top_partner_image" t-attf-src="data:image/png;base64,#{partner_stat['rated_partner'].image}"/>
- <img t-if="not partner_stat['rated_partner'].image" class="o_top_partner_image" src='/web/static/src/img/placeholder.png'/>
+ <img t-if="partner_stat['rated_partner'].image" class="o_top_partner_image" t-attf-src="data:image/png;base64,#{partner_stat['rated_partner'].image}" alt="Rated partner"/>
+ <img t-if="not partner_stat['rated_partner'].image" class="o_top_partner_image" src='/web/static/src/img/placeholder.png' alt="Placeholder"/>
</div>
<div class="col-sm-4 o_smiley_no_padding_right">
<span class="ml8" t-esc="partner_stat['rated_partner'].name"/>
@@ -88,31 +88,31 @@
<div class="col-sm-4 o_smiley_no_padding_right">
<div class="col-sm-4 o_smiley_no_padding_right o_smiley_no_padding_left">
<span t-if="partner_stat['rating_10']">
- <img title="happy" class="o_top_partner_rating_image" src='/rating/static/src/img/rating_10.png'/>
+ <img title="happy" alt="Happy face" class="o_top_partner_rating_image" src='/rating/static/src/img/rating_10.png'/>
<span class="o_rating_count" t-esc="partner_stat['rating_10']"/>
</span>
<span t-if="not partner_stat['rating_10']">
- <img title="happy" class="o_top_partner_rating_image o_lighter_smileys" src='/rating/static/src/img/rating_10.png'/>
+ <img title="happy" alt="Happy face" class="o_top_partner_rating_image o_lighter_smileys" src='/rating/static/src/img/rating_10.png'/>
<span class="o_rating_count o_lighter_smileys" t-esc="partner_stat['rating_10']"/>
</span>
</div>
<div class="col-sm-4 o_smiley_no_padding_right o_smiley_no_padding_left">
<span t-if="partner_stat['rating_5']">
- <img title="happy" class="o_top_partner_rating_image" src='/rating/static/src/img/rating_5.png'/>
+ <img title="average" alt="Neutral face" class="o_top_partner_rating_image" src='/rating/static/src/img/rating_5.png'/>
<span class="o_rating_count" t-esc="partner_stat['rating_5']"/>
</span>
<span t-if="not partner_stat['rating_5']">
- <img title="happy" class="o_top_partner_rating_image o_lighter_smileys" src='/rating/static/src/img/rating_5.png'/>
+ <img title="average" alt="Neutral face" class="o_top_partner_rating_image o_lighter_smileys" src='/rating/static/src/img/rating_5.png'/>
<span class="o_rating_count o_lighter_smileys" t-esc="partner_stat['rating_5']"/>
</span>
</div>
<div class="col-sm-4 o_smiley_no_padding_right o_smiley_no_padding_left">
<span t-if="partner_stat['rating_1']">
- <img title="happy" class="o_top_partner_rating_image" src='/rating/static/src/img/rating_1.png'/>
+ <img title="bad" alt="Sad face" class="o_top_partner_rating_image" src='/rating/static/src/img/rating_1.png'/>
<span class="o_rating_count" t-esc="partner_stat['rating_1']"/>
</span>
<span t-if="not partner_stat['rating_1']">
- <img title="happy" class="o_top_partner_rating_image o_lighter_smileys" src='/rating/static/src/img/rating_1.png'/>
+ <img title="bad" alt="Sad face" class="o_top_partner_rating_image o_lighter_smileys" src='/rating/static/src/img/rating_1.png'/>
<span class="o_rating_count o_lighter_smileys" t-esc="partner_stat['rating_1']"/>
</span>
</div>
@@ -128,8 +128,8 @@
<div t-attf-id="rating_#{rating.id}" class="container hidden">
<div class="row">
<div class="col-md-2">
- <img t-if="rating.partner_id.image" class="o_top_partner_image" t-attf-src="data:image/png;base64,#{rating.partner_id.image}"/>
- <img t-if="not rating.partner_id.image" class="o_top_partner_image" src='/web/static/src/img/placeholder.png'/>
+ <img t-if="rating.partner_id.image" class="o_top_partner_image" t-attf-src="data:image/png;base64,#{rating.partner_id.image}" alt="Rated partner"/>
+ <img t-if="not rating.partner_id.image" class="o_top_partner_image" src='/web/static/src/img/placeholder.png' alt="Placeholder"/>
</div>
<div class="col-md-10">
<div class="mt4">
@@ -160,6 +160,7 @@
<t t-foreach="ratings" t-as="rating">
<img t-attf-src='/rating/static/src/img/rating_#{int(rating.rating)}.png'
class="mt4 o_rating_image"
+ t-att-alt="rating.name"
t-att-data-id="rating.id"
t-att-data-rating-date="rating.write_date"/>
<t t-call="project.portal_project_rating_popover"/>
diff --git a/addons/project/views/project_views.xml b/addons/project/views/project_views.xml
index d0dfcfaa0ed..b5a54bd0dc5 100644
--- a/addons/project/views/project_views.xml
+++ b/addons/project/views/project_views.xml
@@ -236,7 +236,7 @@
</div>
<div class="col-xs-4">
<div class="oe_kanban_bottom_right">
- <img t-att-src="kanban_image('res.users', 'image_small', record.user_id.raw_value)" t-att-title="record.user_id.value" width="24" height="24" class="oe_kanban_avatar pull-right"/>
+ <img t-att-src="kanban_image('res.users', 'image_small', record.user_id.raw_value)" t-att-title="record.user_id.value" t-att-alt="record.user_id.value" width="24" height="24" class="oe_kanban_avatar pull-right"/>
</div>
</div>
</div>
@@ -309,36 +309,36 @@
</span>
</div>
<div t-if="record.alias_name.value and record.alias_domain.value">
- <span><i class="fa fa-envelope"></i> <t t-esc="record.alias_id.value"/></span>
+ <span><i class="fa fa-envelope" role="img" aria-label="Domain Alias" title="Domain Alias"></i> <t t-esc="record.alias_id.value"/></span>
</div>
<div t-if="record.rating_status.raw_value != 'no'" class="mt8 text-primary" title="Percentage of happy ratings over the past 30 days. Get rating details from the More menu." groups="project.group_project_rating">
<b>
<t t-if="record.percentage_satisfaction_project.value == -1">
- <i class="fa fa-smile-o"/> No rating yet
+ <span class="fa fa-smile-o"> No rating yet</span>
</t>
<t t-if="record.percentage_satisfaction_project.value != -1">
<a name="action_view_all_rating" type="object" context="{'search_default_rating_last_30_days':1}">
- <i class="fa fa-smile-o"/> <t t-esc="record.percentage_satisfaction_project.value"/>%
+ <i class="fa fa-smile-o" role="img" aria-label="Percentage of satisfaction" title="Percentage of satisfaction"/> <t t-esc="record.percentage_satisfaction_project.value"/>%
</a>
</t>
</b>
</div>
</div>
</div>
- <div class="o_kanban_card_manage_pane dropdown-menu" groups="project.group_project_manager">
+ <div class="o_kanban_card_manage_pane dropdown-menu" groups="project.group_project_manager" role="menu">
<div class="o_kanban_card_manage_section o_kanban_manage_reports">
- <div>
+ <div role="menuitem">
<a type="edit">Edit</a>
</div>
- <div t-if="record.rating_status.raw_value != 'no'">
+ <div role="menuitem" t-if="record.rating_status.raw_value != 'no'">
<a name="action_view_all_rating" type="object">Customer Ratings</a>
</div>
</div>
- <div class="o_no_padding_kanban_colorpicker">
- <ul class="oe_kanban_colorpicker" data-field="color"/>
+ <div role="menuitem" aria-haspopup="true" class="o_no_padding_kanban_colorpicker">
+ <ul class="oe_kanban_colorpicker" data-field="color" role="popup"/>
</div>
</div>
- <a class="o_kanban_manage_toggle_button o_left" href="#" groups="project.group_project_manager"><i class="fa fa-ellipsis-v" /></a>
+ <a class="o_kanban_manage_toggle_button o_left" href="#" groups="project.group_project_manager"><i class="fa fa-ellipsis-v" role="img" aria-label="Manage" title="Manage"/></a>
<span class="o_right"><field name="is_favorite" widget="boolean_favorite" nolabel="1" force_save="1" /></span>
</div>
@@ -552,17 +552,17 @@
</small>
</div>
<div class="o_dropdown_kanban dropdown" groups="base.group_user">
- <a class="dropdown-toggle btn" data-toggle="dropdown" href="#" >
- <span class="fa fa-ellipsis-v" aria-hidden="true"/>
+ <a role="button" class="dropdown-toggle btn" data-toggle="dropdown" href="#" aria-label="Dropdown menu" title="Dropdown menu">
+ <span class="fa fa-ellipsis-v"/>
</a>
- <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
- <li t-if="widget.editable"><a type="set_cover">Set Cover Image</a></li>
- <li t-if="widget.editable"><a type="edit">Edit Task</a></li>
- <li t-if="widget.deletable"><a type="delete">Delete</a></li>
- <li class="divider"></li>
- <li class="dropdown-header">Record's Colour</li>
- <li>
- <ul class="oe_kanban_colorpicker" data-field="color"/>
+ <ul class="dropdown-menu" role="menu">
+ <li t-if="widget.editable"><a role="menuitem" type="set_cover">Set Cover Image</a></li>
+ <li t-if="widget.editable"><a role="menuitem" type="edit">Edit Task</a></li>
+ <li t-if="widget.deletable"><a role="menuitem" type="delete">Delete</a></li>
+ <li role="separator" class="divider"></li>
+ <li role="separator" class="dropdown-header">Record's Colour</li>
+ <li role="menuitem" aria-haspopup="true">
+ <ul role="menu" class="oe_kanban_colorpicker" data-field="color"/>
</li>
</ul>
</div>
@@ -578,17 +578,17 @@
<field name="priority" widget="priority"/>
<field name="activity_ids" widget="kanban_activity"/>
<t t-if="record.message_needaction_counter.raw_value">
- <span class='oe_kanban_mail_new' title='Unread Messages'><i class='fa fa-comments'/><t t-raw="record.message_needaction_counter.raw_value"/></span>
+ <span role="alert" class='oe_kanban_mail_new' title='Unread Messages'><i class='fa fa-comments' role="img" aria-label="Unread Messages"/><t t-raw="record.message_needaction_counter.raw_value"/></span>
</t>
<b t-if="record.rating_ids.raw_value.length">
- <span style="font-weight:bold;" class="fa fa-fw mt4 fa-smile-o text-success" t-if="record.rating_last_value.value == 10" title="Latest Rating: Satisfied"/>
- <span style="font-weight:bold;" class="fa fa-fw mt4 fa-meh-o text-warning" t-if="record.rating_last_value.value == 5" title="Latest Rating: Not Satisfied"/>
- <span style="font-weight:bold;" class="fa fa-fw mt4 fa-frown-o text-danger" t-if="record.rating_last_value.value == 1" title="Latest Rating: Higly Dissatisfied"/>
+ <span style="font-weight:bold;" class="fa fa-fw mt4 fa-smile-o text-success" t-if="record.rating_last_value.value == 10" title="Latest Rating: Satisfied" role="img" aria-label="Happy face"/>
+ <span style="font-weight:bold;" class="fa fa-fw mt4 fa-meh-o text-warning" t-if="record.rating_last_value.value == 5" title="Latest Rating: Not Satisfied" role="img" aria-label="Neutral face"/>
+ <span style="font-weight:bold;" class="fa fa-fw mt4 fa-frown-o text-danger" t-if="record.rating_last_value.value == 1" title="Latest Rating: Higly Dissatisfied" role="img" aria-label="Sad face"/>
</b>
</div>
<div class="oe_kanban_bottom_right">
<field name="kanban_state" widget="state_selection" groups="base.group_user"/>
- <img t-att-src="kanban_image('res.users', 'image_small', record.user_id.raw_value)" t-att-title="record.user_id.value" width="24" height="24" class="oe_kanban_avatar"/>
+ <img t-att-src="kanban_image('res.users', 'image_small', record.user_id.raw_value)" t-att-title="record.user_id.value" t-att-alt="record.user_id.value" width="24" height="24" class="oe_kanban_avatar"/>
</div>
</div>
</div>
@@ -777,17 +777,23 @@
You can define here labels that will be displayed for the state instead
of the default labels.
</p>
- <label for="legend_normal" string=" " class="o_status oe_project_kanban_legend"/>
+ <label for="legend_normal" string=" " class="o_status oe_project_kanban_legend"
+ title="Task in progress. Click to block or set as done."
+ aria-label="Task in progress. Click to block or set as done." role="img"/>
<field name="legend_normal" nolabel="1"/>
- <label for="legend_blocked" string=" " class="o_status o_status_red oe_project_kanban_legend"/>
+ <label for="legend_blocked" string=" " class="o_status o_status_red oe_project_kanban_legend"
+ title="Task is blocked. Click to unblock or set as done."
+ aria-label="Task is blocked. Click to unblock or set as done." role="img"/>
<field name="legend_blocked" nolabel="1"/>
- <label for="legend_done" string=" " class="o_status o_status_green oe_project_kanban_legend"/>
+ <label for="legend_done" string=" " class="o_status o_status_green oe_project_kanban_legend"
+ title="This step is done. Click to block or set in progress."
+ aria-label="This step is done. Click to block or set in progress." role="img"/>
<field name="legend_done" nolabel="1"/>
<p class="text-muted" colspan="2">
You can also give a tooltip about the use of the stars available in the kanban and form views.
</p>
- <label for="legend_priority" string=" " class="fa fa-star fa-lg o_star_on oe_project_kanban_legend"/>
+ <label for="legend_priority" string=" " class="fa fa-star fa-lg o_star_on oe_project_kanban_legend" aria-label="Legend priority" title="Legend priority" role="img"/>
<field name="legend_priority" nolabel="1"/>
<p class="text-muted" colspan="2">