diff --git a/addons/web/static/src/legacy/js/fields/relational_fields.js b/addons/web/static/src/legacy/js/fields/relational_fields.js index d5aaf782f63896e677d73200c5151f78e650cd12..0b71488e5b22c2f6ffb4e10c362b76a26a0a3d8c 100644 --- a/addons/web/static/src/legacy/js/fields/relational_fields.js +++ b/addons/web/static/src/legacy/js/fields/relational_fields.js @@ -317,6 +317,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'); } }; diff --git a/addons/web/static/tests/helpers/utils.js b/addons/web/static/tests/helpers/utils.js index 93d37e3b3f977647609b1f33caaa4bf2067a8cae..d1b27e907f4d3b1bded54c46725564984bc76d0c 100644 --- a/addons/web/static/tests/helpers/utils.js +++ b/addons/web/static/tests/helpers/utils.js @@ -225,6 +225,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 }); } diff --git a/addons/web/static/tests/legacy/fields/relational_fields/field_many2one_tests.js b/addons/web/static/tests/legacy/fields/relational_fields/field_many2one_tests.js index 25c1af550703ee7c75827728e4b839f720f62812..ca807d2944c99d2058f45687262749461757adea 100644 --- a/addons/web/static/tests/legacy/fields/relational_fields/field_many2one_tests.js +++ b/addons/web/static/tests/legacy/fields/relational_fields/field_many2one_tests.js @@ -10,7 +10,7 @@ var StandaloneFieldManagerMixin = require('web.StandaloneFieldManagerMixin'); var testUtils = require('web.test_utils'); var Widget = require('web.Widget'); -const { legacyExtraNextTick } = require("@web/../tests/helpers/utils"); +const { legacyExtraNextTick, triggerScroll } = require("@web/../tests/helpers/utils"); const { createWebClient, doAction } = require('@web/../tests/webclient/helpers'); const { browser } = require('@web/core/browser/browser'); const { patchWithCleanup } = require('@web/../tests/helpers/utils'); @@ -3297,7 +3297,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, @@ -3315,12 +3317,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(); });