[FIX] web: allow user to scroll horizontally

Steps to reproduce:
- create a contact with a very long name
- create a quotation
- in customer, type the first letter of the very long customer's name
- try to use the horizontal scroll bar

Issue:
close window on horizontal scroll

Solution:
Discriminate an horizontal scroll from a vertical scroll
A test has been added as well as the triggerScroll utility function
to allow proper tests when scrolling on elements

opw-2777443

closes odoo/odoo#86744

X-original-commit: e0b26496196bd4ee39fa06e1a3274982d2c7fb12
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
Co-authored-by: Iucapad  <luvi@odoo.com>
This commit is contained in:
Yolann Sabaux
2022-03-22 19:39:40 +01:00
co-authored by Iucapad
parent 5b078f2a77
commit 701293dbd8
3 changed files with 65 additions and 3 deletions
@@ -312,6 +312,10 @@ var FieldMany2One = AbstractField.extend({
open: function (event) {
self._onScroll = function (ev) {
if (ev.target !== self.$input.get(0) && self.$input.hasClass('ui-autocomplete-input')) {
if (ev.target.id === self.$input.autocomplete('widget').get(0).id) {
ev.stopPropagation();
return;
}
self.$input.autocomplete('close');
}
};
+50
View File
@@ -248,6 +248,56 @@ export async function triggerEvents(el, querySelector, events) {
}
}
/**
* Triggers a scroll event on the given target
*
* If the target cannot be scrolled or an axis has reached
* the end of the scrollable area, the event can be transmitted
* to its nearest parent until it can be triggered
*
* @param {HTMLElement} target target of the scroll event
* @param {Object} coordinates
* @param {Number} coordinates[left] coordinates to scroll horizontally
* @param {Number} coordinates[top] coordinates to scroll vertically
* @param {Boolean} canPropagate states if the scroll can propagate to a scrollable parent
*/
export async function triggerScroll(
target,
coordinates = { left: null, top: null },
canPropagate = true
) {
const isScrollable =
(target.scrollHeight > target.clientHeight && target.clientHeight > 0) ||
(target.scrollWidth > target.clientWidth && target.clientWidth > 0);
if (!isScrollable && !canPropagate) return;
if (isScrollable) {
const canScrollFrom = {
left:
coordinates.left > target.scrollLeft
? target.scrollLeft + target.clientWidth < target.scrollWidth
: target.scrollLeft > 0,
top:
coordinates.top > target.scrollTop
? target.scrollTop + target.clientHeight < target.scrollHeight
: target.scrollTop > 0,
};
const scrollCoordinates = {};
Object.entries(coordinates).forEach(([key, value]) => {
if (value !== null && canScrollFrom[key]) {
scrollCoordinates[key] = value;
delete coordinates[key];
}
});
target.scrollTo(scrollCoordinates);
target.dispatchEvent(new UIEvent("scroll"));
if (!canPropagate || !Object.entries(coordinates).length) return;
}
target.parentElement
? triggerScroll(target.parentElement, coordinates)
: window.dispatchEvent(new UIEvent("scroll"));
await nextTick();
}
export function click(el, selector) {
return triggerEvent(el, selector, "click", { bubbles: true, cancelable: true });
}
@@ -9,6 +9,7 @@ var StandaloneFieldManagerMixin = require('web.StandaloneFieldManagerMixin');
var testUtils = require('web.test_utils');
var Widget = require('web.Widget');
const { triggerScroll } = require("@web/../tests/helpers/utils");
const { browser } = require('@web/core/browser/browser');
const { patchWithCleanup } = require('@web/../tests/helpers/utils');
const cpHelpers = require('@web/../tests/search/helpers');
@@ -3348,7 +3349,9 @@ QUnit.module('fields', {}, function () {
});
QUnit.test('many2one dropdown disappears on scroll', async function (assert) {
assert.expect(2);
assert.expect(4);
this.data.partner.records[0].display_name = "Veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeery Loooooooooooooooooooooooooooooooooooooooooooong Naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaame";
var form = await createView({
View: FormView,
@@ -3366,12 +3369,17 @@ QUnit.module('fields', {}, function () {
await testUtils.form.clickEdit(form);
var $input = form.$('.o_field_many2one input');
var dropdown = document.querySelector(".dropdown-menu.ui-front");
await testUtils.dom.click($input);
assert.isVisible($input.autocomplete('widget'), "dropdown should be opened");
form.el.dispatchEvent(new Event('scroll'));
assert.isNotVisible($input.autocomplete('widget'), "dropdown should be closed");
await triggerScroll(dropdown, { left: 50 }, false);
assert.strictEqual(dropdown.scrollLeft, 50, "a scroll happened");
assert.isVisible($input.autocomplete('widget'), "dropdown stays open if the scroll is inside the dropdown");
await triggerScroll(window, { top: 50 });
assert.isNotVisible($input.autocomplete('widget'), "dropdown closes if the scroll is outside the dropdown");
form.destroy();
});