[IMP] web,sms: limit the clickable area for email/phone/url widgets

The 'o_text_overflow' class whlie provided on any field, is used to
have classic ellipsis (...) for very long string. However, it is
implemented the way that it occupies the available width for the field
tag it even if the the string is small.

The same class is also utilized in some field widgets like  email, phone,
URL etc. But it has a small side-effect due to full width occupancy.

Because the above widgets rendered an anchor tag, the clickable area is on
the whole available width instead of simply on the content provided in anchor
tag. So even if user clicks on the empty area of that field, the click
action is performed (for example, in email field widget, default mail client
pops-up) which should not happen. It should behave like clickable m2o fields
where the action is performed only when clicking the content and not on the
empty area.

With this commit, we wrap the anchor tag, within a container div tag. Here,
the overflow class will be on the container div which will do it's job to
prevent the long strings from breaking the UI, and the anchor tag being its
child, will not be the full width, thus limiting the clickable area. This
commit also makes the related test cases more robust by checking proper
classes for particular widgets.

TaskId - 2345974

closes odoo/odoo#63735

Related: odoo/enterprise#15777
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
ijas ahammed
2021-01-15 13:05:54 +00:00
committed by Dharmraj Jhala
parent 6186d0e824
commit 2689915e3f
6 changed files with 51 additions and 47 deletions
@@ -33,7 +33,7 @@ Phone.include({
*/
getFocusableElement() {
if (this.enableSMS && this.mode === 'readonly') {
return this.$el.filter('.' + this.className);
return this.$el.filter('.' + this.className).find('a');
}
return this._super.apply(this, arguments);
},
+2 -2
View File
@@ -148,7 +148,7 @@ QUnit.module('fields', {
assert.strictEqual(list.$('tbody td:not(.o_list_record_selector)').first().text(), 'yopSMS',
"value should be displayed properly with a link to send SMS");
assert.containsN(list, 'a.o_field_widget.o_form_uri', 2,
assert.containsN(list, 'div.o_field_widget.o_form_uri.o_field_phone > a', 2,
"should have the correct classnames");
// Edit a line and check the result
@@ -165,7 +165,7 @@ QUnit.module('fields', {
assert.doesNotHaveClass($cell.parent(), 'o_selected_row', 'should not be in edit mode anymore');
assert.strictEqual(list.$('tbody td:not(.o_list_record_selector)').first().text(), 'newSMS',
"value should be properly updated");
assert.containsN(list, 'a.o_field_widget.o_form_uri', 2,
assert.containsN(list, 'div.o_field_widget.o_form_uri.o_field_phone > a', 2,
"should still have links with correct classes");
await testUtils.dom.click(list.$('tbody td:not(.o_list_record_selector) .o_field_phone_sms').first());
+17 -13
View File
@@ -1560,7 +1560,7 @@ var FieldEmail = InputField.extend({
*/
init: function () {
this._super.apply(this, arguments);
this.tagName = this.mode === 'readonly' ? 'a' : 'input';
this.tagName = this.mode === 'readonly' ? 'div' : 'input';
},
//--------------------------------------------------------------------------
@@ -1573,7 +1573,7 @@ var FieldEmail = InputField.extend({
* @override
*/
getFocusableElement: function () {
return this.mode === 'readonly' ? this.$el : this._super.apply(this, arguments);
return this.mode === 'readonly' ? this.$el.find('a') : this._super.apply(this, arguments);
},
//--------------------------------------------------------------------------
@@ -1588,11 +1588,12 @@ var FieldEmail = InputField.extend({
*/
_renderReadonly: function () {
if (this.value) {
this.$el.text(this.value)
.addClass('o_form_uri o_text_overflow')
.attr('href', this.prefix + ':' + this.value);
} else {
this.$el.text('');
this.el.classList.add("o_form_uri", "o_text_overflow");
const anchorEl = Object.assign(document.createElement('a'), {
text: this.value,
href: `${this.prefix}:${this.value}`,
});
this.el.appendChild(anchorEl);
}
},
/**
@@ -1664,7 +1665,7 @@ var UrlWidget = InputField.extend({
*/
init: function () {
this._super.apply(this, arguments);
this.tagName = this.mode === 'readonly' ? 'a' : 'input';
this.tagName = this.mode === 'readonly' ? 'div' : 'input';
this.websitePath = this.nodeOptions.website_path || false;
},
@@ -1678,7 +1679,7 @@ var UrlWidget = InputField.extend({
* @override
*/
getFocusableElement: function () {
return this.mode === 'readonly' ? this.$el : this._super.apply(this, arguments);
return this.mode === 'readonly' ? this.$el.find('a') : this._super.apply(this, arguments);
},
//--------------------------------------------------------------------------
@@ -1698,10 +1699,13 @@ var UrlWidget = InputField.extend({
const regex = /^(?:[fF]|[hH][tT])[tT][pP][sS]?:\/\//;
href = !regex.test(this.value) ? `http://${href}` : href;
}
this.$el.text(this.attrs.text || this.value)
.addClass('o_form_uri o_text_overflow')
.attr('target', '_blank')
.attr('href', href);
this.el.classList.add("o_form_uri", "o_text_overflow");
const anchorEl = Object.assign(document.createElement('a'), {
text: this.attrs.text || this.value,
href: href,
target: '_blank',
});
this.el.appendChild(anchorEl);
},
//--------------------------------------------------------------------------
@@ -70,7 +70,7 @@ QUnit.module('basic_fields', {
res_id: 1,
});
var $phoneLink = form.$('a.o_form_uri.o_field_widget');
var $phoneLink = form.$('div.o_form_uri.o_field_widget.o_field_phone > a');
assert.strictEqual($phoneLink.length, 1,
"should have a anchor with correct classes");
assert.strictEqual($phoneLink.text(), 'yop',
@@ -90,7 +90,7 @@ QUnit.module('basic_fields', {
// save
await testUtils.form.clickSave(form);
$phoneLink = form.$('a.o_form_uri.o_field_widget');
$phoneLink = form.$('div.o_form_uri.o_field_widget.o_field_phone > a');
assert.strictEqual($phoneLink.text(), 'new',
"new value should be displayed properly");
assert.hasAttrValue($phoneLink, 'href', 'tel:new',
@@ -114,7 +114,7 @@ QUnit.module('basic_fields', {
assert.strictEqual(list.$('tbody td:not(.o_list_record_selector) a').first().text(), 'yop',
"value should be displayed properly");
var $phoneLink = list.$('a.o_form_uri.o_field_widget');
var $phoneLink = list.$('div.o_form_uri.o_field_widget.o_field_phone > a');
assert.strictEqual($phoneLink.length, 3,
"should have anchors with correct classes");
assert.hasAttrValue($phoneLink.first(), 'href', 'tel:yop',
@@ -134,7 +134,7 @@ QUnit.module('basic_fields', {
assert.doesNotHaveClass($cell.parent(), 'o_selected_row', 'should not be in edit mode anymore');
assert.strictEqual(list.$('tbody td:not(.o_list_record_selector) a').first().text(), 'new',
"value should be properly updated");
$phoneLink = list.$('a.o_form_uri.o_field_widget');
$phoneLink = list.$('div.o_form_uri.o_field_widget.o_field_phone > a');
assert.strictEqual($phoneLink.length, 3,
"should still have anchors with correct classes");
assert.hasAttrValue($phoneLink.first(), 'href', 'tel:new',
@@ -1101,7 +1101,7 @@ QUnit.module('basic_fields', {
res_id: 1,
});
var $mailtoLink = form.$('a.o_form_uri.o_field_widget.o_text_overflow');
var $mailtoLink = form.$('div.o_form_uri.o_field_widget.o_text_overflow.o_field_email > a');
assert.strictEqual($mailtoLink.length, 1,
"should have a anchor with correct classes");
assert.strictEqual($mailtoLink.text(), 'yop',
@@ -1121,7 +1121,7 @@ QUnit.module('basic_fields', {
// save
await testUtils.form.clickSave(form);
$mailtoLink = form.$('a.o_form_uri.o_field_widget.o_text_overflow');
$mailtoLink = form.$('div.o_form_uri.o_field_widget.o_text_overflow.o_field_email > a');
assert.strictEqual($mailtoLink.text(), 'new',
"new value should be displayed properly");
assert.hasAttrValue($mailtoLink, 'href', 'mailto:new',
@@ -1145,7 +1145,7 @@ QUnit.module('basic_fields', {
assert.strictEqual(list.$('tbody td:not(.o_list_record_selector)').first().text(), 'yop',
"value should be displayed properly as text");
var $mailtoLink = list.$('a.o_form_uri.o_field_widget.o_text_overflow');
var $mailtoLink = list.$('div.o_form_uri.o_field_widget.o_text_overflow.o_field_email > a');
assert.strictEqual($mailtoLink.length, 5,
"should have anchors with correct classes");
assert.hasAttrValue($mailtoLink.first(), 'href', 'mailto:yop',
@@ -1165,7 +1165,7 @@ QUnit.module('basic_fields', {
assert.doesNotHaveClass($cell.parent(), 'o_selected_row', 'should not be in edit mode anymore');
assert.strictEqual(list.$('tbody td:not(.o_list_record_selector)').first().text(), 'new',
"value should be properly updated");
$mailtoLink = list.$('a.o_form_uri.o_field_widget.o_text_overflow');
$mailtoLink = list.$('div.o_form_uri.o_field_widget.o_text_overflow.o_field_email > a');
assert.strictEqual($mailtoLink.length, 5,
"should still have anchors with correct classes");
assert.hasAttrValue($mailtoLink.first(), 'href', 'mailto:new',
@@ -1191,7 +1191,7 @@ QUnit.module('basic_fields', {
res_id: 1,
});
var $mailtoLink = form.$('a.o_form_uri.o_field_widget.o_text_overflow');
var $mailtoLink = form.$('div.o_form_uri.o_field_widget.o_text_overflow.o_field_email > a');
assert.strictEqual($mailtoLink.text(), '',
"the value should be displayed properly");
@@ -1938,13 +1938,13 @@ QUnit.module('basic_fields', {
res_id: 1,
});
assert.containsOnce(form, 'a.o_form_uri.o_field_widget.o_text_overflow',
assert.containsOnce(form, 'div.o_form_uri.o_field_widget.o_text_overflow.o_field_url > a',
"should have a anchor with correct classes");
assert.hasAttrValue(form.$('a.o_form_uri.o_field_widget.o_text_overflow'), 'href', 'http://yop',
assert.hasAttrValue(form.$('div.o_form_uri.o_field_widget.o_text_overflow.o_field_url > a'), 'href', 'http://yop',
"should have proper href link");
assert.hasAttrValue(form.$('a.o_form_uri.o_field_widget.o_text_overflow'), 'target', '_blank',
assert.hasAttrValue(form.$('div.o_form_uri.o_field_widget.o_text_overflow.o_field_url > a'), 'target', '_blank',
"should have target attribute set to _blank");
assert.strictEqual(form.$('a.o_form_uri.o_field_widget.o_text_overflow').text(), 'yop',
assert.strictEqual(form.$('div.o_form_uri.o_field_widget.o_text_overflow.o_field_url > a').text(), 'yop',
"the value should be displayed properly");
// switch to edit mode and check the result
@@ -1959,11 +1959,11 @@ QUnit.module('basic_fields', {
// save
await testUtils.form.clickSave(form);
assert.containsOnce(form, 'a.o_form_uri.o_field_widget.o_text_overflow',
assert.containsOnce(form, 'div.o_form_uri.o_field_widget.o_text_overflow.o_field_url > a',
"should still have a anchor with correct classes");
assert.hasAttrValue(form.$('a.o_form_uri.o_field_widget.o_text_overflow'), 'href', 'http://limbo',
assert.hasAttrValue(form.$('div.o_form_uri.o_field_widget.o_text_overflow.o_field_url > a'), 'href', 'http://limbo',
"should have proper new href link");
assert.strictEqual(form.$('a.o_form_uri.o_field_widget.o_text_overflow').text(), 'limbo',
assert.strictEqual(form.$('div.o_form_uri.o_field_widget.o_text_overflow.o_field_url > a').text(), 'limbo',
'the new value should be displayed');
form.destroy();
@@ -1982,7 +1982,7 @@ QUnit.module('basic_fields', {
res_id: 1,
});
assert.strictEqual(form.$('a[name="foo"]').text(), 'kebeclibre',
assert.strictEqual(form.$('div[name="foo"].o_field_url > a').text(), 'kebeclibre',
"url text should come from the text attribute");
form.destroy();
});
@@ -2009,10 +2009,10 @@ QUnit.module('basic_fields', {
res_id: 1,
});
assert.strictEqual(form.$('a[name="url1"]').attr('href'), 'http://www.url1.com');
assert.strictEqual(form.$('a[name="url2"]').attr('href'), 'www.url2.com');
assert.strictEqual(form.$('a[name="url3"]').attr('href'), 'http://www.url3.com');
assert.strictEqual(form.$('a[name="url4"]').attr('href'), 'https://url4.com');
assert.strictEqual(form.$('div[name="url1"].o_field_url > a').attr('href'), 'http://www.url1.com');
assert.strictEqual(form.$('div[name="url2"].o_field_url > a').attr('href'), 'www.url2.com');
assert.strictEqual(form.$('div[name="url3"].o_field_url > a').attr('href'), 'http://www.url3.com');
assert.strictEqual(form.$('div[name="url4"].o_field_url > a').attr('href'), 'https://url4.com');
form.destroy();
});
@@ -2029,9 +2029,9 @@ QUnit.module('basic_fields', {
assert.strictEqual(list.$('tbody td:not(.o_list_record_selector)').length, 5,
"should have 5 cells");
assert.containsN(list, 'a.o_form_uri.o_field_widget.o_text_overflow', 5,
assert.containsN(list, 'div.o_form_uri.o_field_widget.o_text_overflow.o_field_url > a', 5,
"should have 5 anchors with correct classes");
assert.hasAttrValue(list.$('a.o_form_uri.o_field_widget.o_text_overflow').first(), 'href', 'http://yop',
assert.hasAttrValue(list.$('div.o_form_uri.o_field_widget.o_text_overflow.o_field_url > a').first(), 'href', 'http://yop',
"should have proper href link");
assert.strictEqual(list.$('tbody td:not(.o_list_record_selector)').first().text(), 'yop',
"value should be displayed properly as text");
@@ -2048,11 +2048,11 @@ QUnit.module('basic_fields', {
await testUtils.dom.click(list.$buttons.find('.o_list_button_save'));
$cell = list.$('tbody td:not(.o_list_record_selector)').first();
assert.doesNotHaveClass($cell.parent(), 'o_selected_row', 'should not be in edit mode anymore');
assert.containsN(list, 'a.o_form_uri.o_field_widget.o_text_overflow', 5,
assert.containsN(list, 'div.o_form_uri.o_field_widget.o_text_overflow.o_field_url > a', 5,
"should still have 5 anchors with correct classes");
assert.hasAttrValue(list.$('a.o_form_uri.o_field_widget.o_text_overflow').first(), 'href', 'http://brolo',
assert.hasAttrValue(list.$('div.o_form_uri.o_field_widget.o_text_overflow.o_field_url > a').first(), 'href', 'http://brolo',
"should have proper new href link");
assert.strictEqual(list.$('a.o_form_uri.o_field_widget.o_text_overflow').first().text(), 'brolo',
assert.strictEqual(list.$('div.o_form_uri.o_field_widget.o_text_overflow.o_field_url > a').first().text(), 'brolo',
"value should be properly updated");
list.destroy();
@@ -5735,7 +5735,7 @@ QUnit.module('basic_fields', {
},
});
var $phone = form.$('a.o_field_widget.o_form_uri');
var $phone = form.$('div.o_field_widget.o_form_uri.o_field_phone > a');
assert.strictEqual($phone.length, 1,
"should have rendered the phone number as a link with correct classes");
assert.strictEqual($phone.text(), 'yop',
@@ -5753,7 +5753,7 @@ QUnit.module('basic_fields', {
// save
await testUtils.form.clickSave(form);
assert.strictEqual(form.$('a.o_field_widget.o_form_uri').text(), 'new',
assert.strictEqual(form.$('div.o_field_widget.o_form_uri.o_field_phone > a').text(), 'new',
"new value should be displayed properly");
form.destroy();
@@ -5779,7 +5779,7 @@ QUnit.module('basic_fields', {
assert.strictEqual(list.$('tbody td:not(.o_list_record_selector) a').first().text(), 'yop',
"value should be displayed properly with a link to send SMS");
assert.containsN(list, 'a.o_field_widget.o_form_uri', 5,
assert.containsN(list, 'div.o_field_widget.o_form_uri.o_field_phone > a', 5,
"should have the correct classnames");
// Edit a line and check the result
@@ -5796,7 +5796,7 @@ QUnit.module('basic_fields', {
assert.doesNotHaveClass($cell.parent(), 'o_selected_row', 'should not be in edit mode anymore');
assert.strictEqual(list.$('tbody td:not(.o_list_record_selector) a').first().text(), 'new',
"value should be properly updated");
assert.containsN(list, 'a.o_field_widget.o_form_uri', 5,
assert.containsN(list, 'div.o_field_widget.o_form_uri.o_field_phone > a', 5,
"should still have links with correct classes");
list.destroy();
+1 -1
View File
@@ -4772,7 +4772,7 @@ QUnit.module('Views', {
"product_id should be focused");
form.$('[name="product_id"]').trigger($.Event('keydown', {which: $.ui.keyCode.TAB}));
form.$('[name="foo"]:eq(1)').trigger($.Event('keydown', {which: $.ui.keyCode.TAB}));
assert.strictEqual(form.$('[name="display_name"]')[0], document.activeElement,
assert.strictEqual(form.$('div[name="display_name"].o_field_url > a')[0], document.activeElement,
"display_name should be focused");
// simulate shift+tab on active element