From cda339654610dfc1e2c8f6d6f5474d547d1e1ab1 Mon Sep 17 00:00:00 2001 From: Julien Mougenot Date: Mon, 7 Nov 2022 10:46:54 +0000 Subject: [PATCH] [FIX] web,project: Restore kanban drag&drop in Firefox Before this commit, the drag & drop feature for the Project "Tasks" kanban view did not work if the drag sequence was initiated from the title of the card. This was due to the "o_kanban_record_headings" class having the CSS rule `overflow: hidden`, because of a well known 6 year-long-and-counting bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1352061 Namely: mouse enter|leave events won't fire on anything underneath elements having `overflow: hidden`. This commit solves this issue by introducing a dedicated class name 'o_dragged' to all elements dragged using the `draggable_hook_builder`, and defining on it the CSS attributes that were assigned in JS before, as well as `pointer-events: none` on itself and all its children to avoid the issue mentioned above. Also in this commit: fixed a syntax error in a CSS rule in project's "task_name_with_subtask_count_char_field" widget. closes odoo/odoo#105353 Related: odoo/enterprise#33774 Signed-off-by: Aaron Bohy (aab) --- ...ect_task_name_with_subtask_count_char_field.xml | 2 +- .../src/core/utils/draggable_hook_builder.js | 7 ++++--- .../src/core/utils/draggable_hook_builder.scss | 14 ++++++++++++++ addons/web/static/src/core/utils/sortable.js | 4 ++-- .../web/static/src/views/kanban/kanban_renderer.js | 4 ++-- addons/web/static/src/views/list/list_renderer.js | 8 ++------ addons/web/static/tests/core/utils/ui_tests.js | 12 +++++++++--- 7 files changed, 34 insertions(+), 17 deletions(-) create mode 100644 addons/web/static/src/core/utils/draggable_hook_builder.scss diff --git a/addons/project/static/src/components/project_task_name_with_subtask_count_char_field/project_task_name_with_subtask_count_char_field.xml b/addons/project/static/src/components/project_task_name_with_subtask_count_char_field/project_task_name_with_subtask_count_char_field.xml index 89ded871ce4..1bb18e7dc37 100644 --- a/addons/project/static/src/components/project_task_name_with_subtask_count_char_field/project_task_name_with_subtask_count_char_field.xml +++ b/addons/project/static/src/components/project_task_name_with_subtask_count_char_field/project_task_name_with_subtask_count_char_field.xml @@ -6,7 +6,7 @@ diff --git a/addons/web/static/src/core/utils/draggable_hook_builder.js b/addons/web/static/src/core/utils/draggable_hook_builder.js index 0575c513d09..92f057b1a05 100644 --- a/addons/web/static/src/core/utils/draggable_hook_builder.js +++ b/addons/web/static/src/core/utils/draggable_hook_builder.js @@ -81,6 +81,7 @@ const DEFAULT_DEFAULT_PARAMS = { threshold: 30, }, }; +const DRAGGED_CLASS = "o_dragged"; const LEFT_CLICK = 0; const MANDATORY_PARAMS = ["ref", "elements"]; @@ -247,9 +248,6 @@ export function makeDraggableHook(hookParams = {}) { ctx.offset.y -= eRect.y; addStyle(ctx.currentElement, { - position: "fixed", - "pointer-events": "none", - "z-index": 1000, width: `${eRect.width}px`, height: `${eRect.height}px`, left: `${eRect.x}px`, @@ -271,6 +269,9 @@ export function makeDraggableHook(hookParams = {}) { cleanups.push(cleanupFn); } + ctx.currentElement.classList.add(DRAGGED_CLASS); + cleanups.push(() => ctx.currentElement.classList.remove(DRAGGED_CLASS)); + execBuildHandler("onDragStart"); }; diff --git a/addons/web/static/src/core/utils/draggable_hook_builder.scss b/addons/web/static/src/core/utils/draggable_hook_builder.scss new file mode 100644 index 00000000000..657abf3138e --- /dev/null +++ b/addons/web/static/src/core/utils/draggable_hook_builder.scss @@ -0,0 +1,14 @@ +.o_dragged { + position: fixed !important; + z-index: 1000 !important; + + // In FireFox: elements with `overflow: hidden` will prevent mouseenter and mouseleave + // events from firing on elements underneath them. This is the case when dragging a card + // by the `.o_kanban_record_headings` element. In such cases, the pointer events are + // disabled to allow mouse events to fire properly. + // https://bugzilla.mozilla.org/show_bug.cgi?id=1352061 + &, + * { + pointer-events: none; + } +} diff --git a/addons/web/static/src/core/utils/sortable.js b/addons/web/static/src/core/utils/sortable.js index d8a0eb60aa4..6abab741b95 100644 --- a/addons/web/static/src/core/utils/sortable.js +++ b/addons/web/static/src/core/utils/sortable.js @@ -139,8 +139,7 @@ export const useSortable = makeDraggableHook({ const { width, height } = ctx.currentElementRect; - // Prepares the ghost element - ctx.ghostElement = ctx.currentElement.cloneNode(false); + // Ghost element is hidden and its size is frozen ctx.ghostElement.style = `visibility: hidden; display: block; width: ${width}px; height:${height}px;`; // Binds handlers on eligible groups, if the elements are not confined to @@ -193,6 +192,7 @@ export const useSortable = makeDraggableHook({ ctx.currentContainer = ctx.currentGroup; } } + ctx.ghostElement = ctx.currentElement.cloneNode(false); }, onCleanup({ ctx }) { if (ctx.ghostElement) { diff --git a/addons/web/static/src/views/kanban/kanban_renderer.js b/addons/web/static/src/views/kanban/kanban_renderer.js index 726af40193c..b7da556def6 100644 --- a/addons/web/static/src/views/kanban/kanban_renderer.js +++ b/addons/web/static/src/views/kanban/kanban_renderer.js @@ -530,7 +530,7 @@ export class KanbanRenderer extends Component { * @param {HTMLElement} [params.group] */ sortStart({ element }) { - element.classList.add("o_dragged", "shadow"); + element.classList.add("shadow"); } /** @@ -539,7 +539,7 @@ export class KanbanRenderer extends Component { * @param {HTMLElement} [params.group] */ sortStop({ element, group }) { - element.classList.remove("o_dragged", "shadow"); + element.classList.remove("shadow"); if (group) { group.classList.remove("o_kanban_hover"); } diff --git a/addons/web/static/src/views/list/list_renderer.js b/addons/web/static/src/views/list/list_renderer.js index 7601c5d5270..b33506e0cb9 100644 --- a/addons/web/static/src/views/list/list_renderer.js +++ b/addons/web/static/src/views/list/list_renderer.js @@ -1745,18 +1745,14 @@ export class ListRenderer extends Component { * @param {HTMLElement} params.element * @param {HTMLElement} [params.group] */ - sortStart({ element }) { - element.classList.add("o_dragged"); - } + sortStart() {} /** * @param {Object} params * @param {HTMLElement} params.element * @param {HTMLElement} [params.group] */ - sortStop({ element }) { - element.classList.remove("o_dragged"); - } + sortStop() {} } ListRenderer.template = "web.ListRenderer"; diff --git a/addons/web/static/tests/core/utils/ui_tests.js b/addons/web/static/tests/core/utils/ui_tests.js index 96c9b358033..e903755a6e2 100644 --- a/addons/web/static/tests/core/utils/ui_tests.js +++ b/addons/web/static/tests/core/utils/ui_tests.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { dragAndDrop, getFixture, mount, nextTick } from "@web/../tests/helpers/utils"; +import { drag, dragAndDrop, getFixture, mount, nextTick } from "@web/../tests/helpers/utils"; import { useSortable } from "@web/core/utils/sortable"; import { Component, reactive, useRef, useState, xml } from "@odoo/owl"; @@ -88,7 +88,7 @@ QUnit.module("UI", ({ beforeEach }) => { }); QUnit.test("Simple sorting in single group", async (assert) => { - assert.expect(19); + assert.expect(22); class List extends Component { setup() { @@ -132,12 +132,18 @@ QUnit.module("UI", ({ beforeEach }) => { await mount(List, target); assert.containsN(target, ".item", 3); + assert.containsNone(target, ".o_dragged"); assert.verifySteps([]); // First item after 2nd item - await dragAndDrop(".item:first-child", ".item:nth-child(2)"); + const drop = await drag(".item:first-child", ".item:nth-child(2)"); + + assert.hasClass(target.querySelector(".item"), "o_dragged"); + + await drop(); assert.containsN(target, ".item", 3); + assert.containsNone(target, ".o_dragged"); assert.verifySteps(["start", "elemententer", "stop", "drop"]); });