From ef540eb006abac0d318f2a708384d125f6203662 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luis=20Gonz=C3=A1lez?= <lgonzalez@vauxoo.com>
Date: Thu, 22 Mar 2018 07:18:35 -0400
Subject: [PATCH] [FIX] web: Make accessible with keyboard the delete button on
 o2m fields (#23719)

* [FIX] web: Make accessible with keyboard the delete button on o2m fields

Currently, the delete button in one2many fields (trash icon) is not
reachable when using the keyboard, because it's neither a link, nor a
button or any other interactable HTML element, but a `<span>`.

This change causes that button to be an actual HTML button keeping the
same appearance, so it may be reached when using the keyboard.

* [FIX] web: Modify test to expect button instead of span as o2m trash icon

Since buttons to delete records in o2m fields (the trash icon) were
changed from `<span>` to `<button>` to be able to access them with the
keyboard, the test need to be modified so it expect the new element
type and doesn't break.
---
 .../js/views/list/list_editable_renderer.js   |  3 ++-
 addons/web/static/src/less/list_view.less     |  8 ++++++++
 .../tests/fields/relational_fields_tests.js   | 20 +++++++++----------
 .../test_new_api/static/tests/x2many.js       |  2 +-
 4 files changed, 21 insertions(+), 12 deletions(-)

diff --git a/addons/web/static/src/js/views/list/list_editable_renderer.js b/addons/web/static/src/js/views/list/list_editable_renderer.js
index 48078e2a22c1..64cfa8a1f301 100644
--- a/addons/web/static/src/js/views/list/list_editable_renderer.js
+++ b/addons/web/static/src/js/views/list/list_editable_renderer.js
@@ -498,7 +498,8 @@ ListRenderer.include({
     _renderRow: function (record, index) {
         var $row = this._super.apply(this, arguments);
         if (this.addTrashIcon) {
-            var $icon = $('<span>', {class: 'fa fa-trash-o', name: 'delete'});
+            var $icon = $('<button>', {class: 'fa fa-trash-o o_list_record_delete_btn', name: 'delete',
+                'aria-label': _t('Delete row ') + (index+1)});
             var $td = $('<td>', {class: 'o_list_record_delete'}).append($icon);
             $row.append($td);
         }
diff --git a/addons/web/static/src/less/list_view.less b/addons/web/static/src/less/list_view.less
index 395bf633e454..0948b566e621 100644
--- a/addons/web/static/src/less/list_view.less
+++ b/addons/web/static/src/less/list_view.less
@@ -70,6 +70,14 @@
         width: 1px;  // to prevent the column to expand
     }
 
+    .o_list_record_delete_btn {
+        padding: 0px;
+        background-style: none;
+        border-style: none;
+        height: 0px;
+        display: table-cell;
+    }
+
     // Decoration of the row
     .text-bf {
         font-weight: bold;
diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js
index 731de4341210..933a76097d8b 100644
--- a/addons/web/static/tests/fields/relational_fields_tests.js
+++ b/addons/web/static/tests/fields/relational_fields_tests.js
@@ -3890,12 +3890,12 @@ QUnit.module('relational_fields', {
         });
         form.$buttons.find('.o_form_button_edit').click();
 
-        assert.strictEqual(form.$('td.o_list_record_delete span').length, 2,
+        assert.strictEqual(form.$('td.o_list_record_delete button').length, 2,
             "should have 2 delete buttons");
 
-        form.$('td.o_list_record_delete span').first().click();
+        form.$('td.o_list_record_delete button').first().click();
 
-        assert.strictEqual(form.$('td.o_list_record_delete span').length, 1,
+        assert.strictEqual(form.$('td.o_list_record_delete button').length, 1,
             "should have 1 delete button (a record is supposed to have been unlinked)");
 
         // save and check that the correct command has been generated
@@ -3933,12 +3933,12 @@ QUnit.module('relational_fields', {
         });
         form.$buttons.find('.o_form_button_edit').click();
 
-        assert.strictEqual(form.$('td.o_list_record_delete span').length, 2,
+        assert.strictEqual(form.$('td.o_list_record_delete button').length, 2,
             "should have 2 delete buttons");
 
-        form.$('td.o_list_record_delete span').first().click();
+        form.$('td.o_list_record_delete button').first().click();
 
-        assert.strictEqual(form.$('td.o_list_record_delete span').length, 1,
+        assert.strictEqual(form.$('td.o_list_record_delete button').length, 1,
             "should have 1 delete button (a record is supposed to have been deleted)");
 
         // save and check that the correct command has been generated
@@ -6682,8 +6682,8 @@ QUnit.module('relational_fields', {
         form.$('input.o_field_integer[name="int_field"]').val('0').trigger('input');
 
         // delete and start over
-        form.$('.o_list_record_delete:first span').click();
-        form.$('.o_list_record_delete:first span').click();
+        form.$('.o_list_record_delete:first button').click();
+        form.$('.o_list_record_delete:first button').click();
 
         // enable the many2many onchange
         form.$('input.o_field_integer[name="int_field"]').val('10').trigger('input');
@@ -8416,7 +8416,7 @@ QUnit.module('relational_fields', {
                 mode: 'edit',
             },
         });
-        form.$('span[name="delete"]').first().click();
+        form.$('button[name="delete"]').first().click();
         assert.strictEqual(form.$('.o_data_row').text(), 'from onchange',
             'onchange has been properly applied');
         form.destroy();
@@ -8456,7 +8456,7 @@ QUnit.module('relational_fields', {
                 mode: 'edit',
             },
         });
-        form.$('span[name="delete"]').first().click();
+        form.$('button[name="delete"]').first().click();
         assert.strictEqual(form.$('.o_data_row').text(), 'from onchange id2from onchange id3',
             'onchange has been properly applied');
         form.destroy();
diff --git a/odoo/addons/test_new_api/static/tests/x2many.js b/odoo/addons/test_new_api/static/tests/x2many.js
index ea976330eb95..30f351c21f67 100644
--- a/odoo/addons/test_new_api/static/tests/x2many.js
+++ b/odoo/addons/test_new_api/static/tests/x2many.js
@@ -408,7 +408,7 @@ odoo.define('web.test.x2many', function (require) {
         trigger: '.ui-autocomplete a:first',
     }, { // remove record
         content: "delete the last item in the editable list",
-        trigger: '.o_list_view .o_data_row td.o_list_record_delete span:visible:last',
+        trigger: '.o_list_view .o_data_row td.o_list_record_delete button:visible:last',
     }, {
         content: "test one2many onchange after delete",
         trigger: '.o_content:not(:has(textarea[name="message_concat"]:propValueContains(Administrator:d)))',
-- 
GitLab