[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:
@@ -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');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user