diff options
| author | kujiu <kujiu@users.noreply.github.com> | 2018-06-22 21:22:21 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-06-22 21:22:21 +0200 |
| commit | 9de1bc0eef6f5bfaa2a8d745431caa361ae91548 (patch) | |
| tree | 342ddbf7fb2b7303606f864ff9f68cbf1bd8814e /addons/project | |
| parent | 864357203fc6222cc989b99a5e9f428cd53f413f (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.xml | 2 | ||||
| -rw-r--r-- | addons/project/static/src/js/tour.js | 2 | ||||
| -rw-r--r-- | addons/project/static/src/xml/project.xml | 2 | ||||
| -rw-r--r-- | addons/project/static/tests/project_tests.js | 6 | ||||
| -rw-r--r-- | addons/project/views/project_portal_templates.xml | 6 | ||||
| -rw-r--r-- | addons/project/views/project_rating_templates.xml | 29 | ||||
| -rw-r--r-- | addons/project/views/project_views.xml | 64 |
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&model=project.task&id=%s&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&model=project.task&id=%s&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"> |
