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 ba22f2c73789c9ba0ce91a6fceffe2c94151af6c..e16e66a3774e9d45ca763a940a10cec5d5a5fc13 100644 --- a/addons/web/static/src/legacy/js/fields/relational_fields.js +++ b/addons/web/static/src/legacy/js/fields/relational_fields.js @@ -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'); } }; diff --git a/addons/web/static/tests/helpers/utils.js b/addons/web/static/tests/helpers/utils.js index 0770f8a721709138db400fb619955a1f60e4c0b2..30e090427cd9f6ad80f952e9b54e8038dff77a57 100644 --- a/addons/web/static/tests/helpers/utils.js +++ b/addons/web/static/tests/helpers/utils.js @@ -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 }); } 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 9adb4e9b548af3824140a5324091dcdc371f72c9..03bfa5ee4210d6a2cbb53067f54a7df995e5bf4a 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 @@ -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(); });