From 7cdef81d1c78df4846d8f7258a90d5c97737cf0a Mon Sep 17 00:00:00 2001 From: "Adrien Minne (adrm)" Date: Mon, 10 Jun 2024 15:53:49 +0200 Subject: [PATCH] [FIX] data_validation: wrong popover position MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The popover to select the type of data validation was positioned at the top of the select element instead of the bottom. Also clicking again on the select element was not toggling the popover. closes odoo/o-spreadsheet#4429 Task: 3981399 X-original-commit: 9989ef57491b9ab6c57748cef1ea8dcfd0cbb79e Signed-off-by: Lucas Lefèvre (lul) Signed-off-by: Adrien Minne (adrm) --- .../side_panel/select_menu/select_menu.ts | 14 +++++++---- .../side_panel/select_menu/select_menu.xml | 1 + ...tion_generics_side_panel_component.test.ts | 24 +++++++++++++++++++ tests/test_helpers/mock_helpers.ts | 6 ++--- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/components/side_panel/select_menu/select_menu.ts b/src/components/side_panel/select_menu/select_menu.ts index ced05dddd..a67e7f0be 100644 --- a/src/components/side_panel/select_menu/select_menu.ts +++ b/src/components/side_panel/select_menu/select_menu.ts @@ -1,6 +1,7 @@ import { Component, useRef, useState } from "@odoo/owl"; import { Action } from "../../../actions/action"; -import { DOMCoordinates, SpreadsheetChildEnv } from "../../../types"; +import { UuidGenerator } from "../../../helpers"; +import { DOMCoordinates, MenuMouseEvent, SpreadsheetChildEnv } from "../../../types"; import { useAbsoluteBoundingRect } from "../../helpers/position_hook"; import { Menu } from "../../menu/menu"; @@ -24,6 +25,8 @@ export class SelectMenu extends Component }; static components = { Menu }; + menuId = new UuidGenerator().uuidv4(); + selectRef = useRef("select"); selectRect = useAbsoluteBoundingRect(this.selectRef); @@ -31,8 +34,11 @@ export class SelectMenu extends Component isMenuOpen: false, }); - onClick() { - this.state.isMenuOpen = true; + onClick(ev: MenuMouseEvent) { + if (ev.closedMenuId === this.menuId) { + return; + } + this.state.isMenuOpen = !this.state.isMenuOpen; } onMenuClosed() { @@ -42,7 +48,7 @@ export class SelectMenu extends Component get menuPosition(): DOMCoordinates { return { x: this.selectRect.x, - y: this.selectRect.y, + y: this.selectRect.y + this.selectRect.height, }; } } diff --git a/src/components/side_panel/select_menu/select_menu.xml b/src/components/side_panel/select_menu/select_menu.xml index 269c8f377..5dfc40fca 100644 --- a/src/components/side_panel/select_menu/select_menu.xml +++ b/src/components/side_panel/select_menu/select_menu.xml @@ -12,6 +12,7 @@ menuItems="props.menuItems" position="menuPosition" onClose.bind="onMenuClosed" + menuId="menuId" /> diff --git a/tests/data_validation/data_validation_generics_side_panel_component.test.ts b/tests/data_validation/data_validation_generics_side_panel_component.test.ts index 970220fac..f0e0394df 100644 --- a/tests/data_validation/data_validation_generics_side_panel_component.test.ts +++ b/tests/data_validation/data_validation_generics_side_panel_component.test.ts @@ -6,6 +6,7 @@ import { addDataValidation, updateLocale } from "../test_helpers/commands_helper import { FR_LOCALE } from "../test_helpers/constants"; import { click, setInputValueAndTrigger, simulateClick } from "../test_helpers/dom_helper"; import { getDataValidationRules, mountComponent, nextTick } from "../test_helpers/helpers"; +import { mockGetBoundingClientRect } from "../test_helpers/mock_helpers"; interface ParentProps { onCloseSidePanel: () => void; @@ -22,6 +23,12 @@ class Parent extends Component { } } +const dataValidationSelectBoundingRect = { x: 100, y: 100, width: 50, height: 50 }; +mockGetBoundingClientRect({ + "o-spreadsheet": () => ({ x: 0, y: 0, width: 1000, height: 1000 }), + "o-dv-type": () => dataValidationSelectBoundingRect, +}); + export async function mountDataValidationPanel(model?: Model) { return mountComponent(Parent, { model: model || new Model(), @@ -44,6 +51,23 @@ describe("data validation sidePanel component", () => { await click(fixture, `.o-menu-item[data-name="${type}"]`); } + test("Menu to select data validation type is correctly positioned", async () => { + await click(fixture, ".o-dv-add"); + await click(fixture, ".o-dv-type"); + const popover = document.querySelector(".o-popover")!; + const { x, y, height } = dataValidationSelectBoundingRect; + expect(popover.style.left).toEqual(x + "px"); + expect(popover.style.top).toEqual(y + height + "px"); + }); + + test("Clicking on the data validation type select element toggles the menu", async () => { + await click(fixture, ".o-dv-add"); + await click(fixture, ".o-dv-type"); + expect(fixture.querySelector(".o-menu")).toBeTruthy(); + await click(fixture, ".o-dv-type"); + expect(fixture.querySelector(".o-menu")).toBeFalsy(); + }); + test.each([ ["textContains", { values: ["str"] }, 'Text contains "str"'], ["textNotContains", { values: ["str"] }, 'Text does not contain "str"'], diff --git a/tests/test_helpers/mock_helpers.ts b/tests/test_helpers/mock_helpers.ts index 1838b08d6..9a9375f70 100644 --- a/tests/test_helpers/mock_helpers.ts +++ b/tests/test_helpers/mock_helpers.ts @@ -1,4 +1,4 @@ -const originalGetBoundingClientRect = HTMLDivElement.prototype.getBoundingClientRect; +const originalGetBoundingClientRect = HTMLElement.prototype.getBoundingClientRect; export function mockGetBoundingClientRect( classesWithMocks: Record Partial> @@ -6,8 +6,8 @@ export function mockGetBoundingClientRect( const mockedClasses = Object.keys(classesWithMocks); jest - .spyOn(HTMLDivElement.prototype, "getBoundingClientRect") - .mockImplementation(function (this: HTMLDivElement) { + .spyOn(HTMLElement.prototype, "getBoundingClientRect") + .mockImplementation(function (this: HTMLElement) { const mockedClass = mockedClasses.find((className) => this.classList.contains(className)); if (mockedClass) { const rect = populateDOMRect(classesWithMocks[mockedClass](this));