You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by hn...@apache.org on 2025/05/22 11:48:08 UTC

(myfaces-tobago) 01/02: fix(dropdown): position

This is an automated email from the ASF dual-hosted git repository.

hnoeth pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit 9ca59c6b45ac39bdc54089b4f3d7e056938dd4cb
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Tue May 20 09:36:08 2025 +0200

    fix(dropdown): position
    
    * add test
    * fix position if on the right
    
    Issue: TOBAGO-2422
---
 .../DropdownMenuPosition.test.js                   | 15 ++++++++
 .../DropdownMenuPosition.xhtml                     | 17 +++++++++
 .../src/main/ts/tobago-dropdown-menu.ts            | 43 +++++++++++++---------
 3 files changed, 58 insertions(+), 17 deletions(-)

diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/4200-dropdown/dropdown-menu-position/DropdownMenuPosition.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/4200-dropdown/dropdown-menu-position/DropdownMenuPosition.test.js
index 8636dffade..088ccc16d5 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/4200-dropdown/dropdown-menu-position/DropdownMenuPosition.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/4200-dropdown/dropdown-menu-position/DropdownMenuPosition.test.js
@@ -18,6 +18,21 @@
 import {elementByIdFn, innerHeight, querySelectorFn, scrollTo} from "/script/tobago-test.js";
 import {JasmineTestTool} from "/tobago/test/tobago-test-tool.js";
 
+it("Button dropdown on the right", function (done) {
+  const button = elementByIdFn("page:mainForm:buttonDropdownRight::command");
+  const dropdownMenu = querySelectorFn(".tobago-dropdown-menu[name='page:mainForm:buttonDropdownRight']");
+
+  const test = new JasmineTestTool(done);
+  test.event("click", button, () => dropdownMenu().classList.contains("show"));
+  test.waitMs(100);
+  test.do(() => expect(dropdownMenu().classList).toContain("show"));
+  test.do(() => expect(dropdownMenu().getBoundingClientRect().left).toBeGreaterThan(0));
+  test.do(() => expect(dropdownMenu().getBoundingClientRect().left).toBeLessThan(window.innerWidth));
+  test.do(() => expect(dropdownMenu().getBoundingClientRect().right).toBeGreaterThan(0));
+  test.do(() => expect(dropdownMenu().getBoundingClientRect().right).toBeLessThan(window.innerWidth));
+  test.start();
+});
+
 it("default - SelectOneList: upper half", function (done) {
   const tobagoHeader = querySelectorFn("tobago-header.sticky-top");
   const selectOneList = elementByIdFn("page:mainForm:defaultSelectOneList");
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/4200-dropdown/dropdown-menu-position/DropdownMenuPosition.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/4200-dropdown/dropdown-menu-position/DropdownMenuPosition.xhtml
index cd96042d05..a37ae1f303 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/4200-dropdown/dropdown-menu-position/DropdownMenuPosition.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/4200-dropdown/dropdown-menu-position/DropdownMenuPosition.xhtml
@@ -23,6 +23,23 @@
                 xmlns:tc="http://myfaces.apache.org/tobago/component"
                 xmlns:ui="jakarta.faces.facelets">
 
+  <tc:header>
+    <tc:style position="fixed" top="60px" left="0" width="100%" customClass="blue"/>
+    <tc:bar>
+      <f:facet name="brand">
+        BRAND
+      </f:facet>
+      CONTENT OF TC:BAR
+      <f:facet name="after">
+        <tc:button id="buttonDropdownRight" label="X" omit="true">
+          <tc:link
+              label="Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua."
+              omit="true"/>
+        </tc:button>
+      </f:facet>
+    </tc:bar>
+  </tc:header>
+
   <tc:panel>
     <tc:style height="1000px"/>
     This is a 1000px height panel to make sure the components for testing can be at the top of the browser window.
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-dropdown-menu.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-dropdown-menu.ts
index 905726ef8c..c799a661dc 100644
--- a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-dropdown-menu.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-dropdown-menu.ts
@@ -140,23 +140,11 @@ export class DropdownMenu {
     const refElementRect = this.referenceElement.getBoundingClientRect();
 
     //calc horizontal positioning and max-width
-    const rightBorder = this.body.offsetWidth;
-    switch (this.alignment) {
-      case DropdownMenuAlignment.start:
-      case DropdownMenuAlignment.centerFullWidth:
-        this.dropdownMenuElement.style.left = "0px";
-        this.dropdownMenuElement.style.left = (refElementRect.left - this.dropdownRect.left) + "px";
-        this.dropdownMenuElement.style.right = null;
-        this.dropdownMenuElement.style.maxWidth = rightBorder - refElementRect.left
-            - parseFloat(getComputedStyle(this.dropdownMenuElement).marginRight) + "px";
-        break;
-      case DropdownMenuAlignment.end:
-        this.dropdownMenuElement.style.left = null;
-        this.dropdownMenuElement.style.right = "0px";
-        this.dropdownMenuElement.style.right = (this.dropdownRect.right - refElementRect.right) + "px";
-        this.dropdownMenuElement.style.maxWidth = refElementRect.right
-            - parseFloat(getComputedStyle(this.dropdownMenuElement).marginLeft) + "px";
-        break;
+    this.calcHorizontalPositioningAndMaxWidth(refElementRect, this.alignment);
+    if (this.dropdownMenuElement.offsetWidth - 1 > parseInt(this.dropdownMenuElement.style.maxWidth)
+        && (window.innerWidth / 2) <= refElementRect.left + (refElementRect.right - refElementRect.left)) {
+      // Content of dropdown is too big and reference element is on the right side; do DropdownMenuAlignment.end
+      this.calcHorizontalPositioningAndMaxWidth(refElementRect, DropdownMenuAlignment.end);
     }
 
     //calc width
@@ -196,6 +184,27 @@ export class DropdownMenu {
     }
   }
 
+  private calcHorizontalPositioningAndMaxWidth(refElementRect: DOMRect, alignment: DropdownMenuAlignment): void {
+    const rightBorder = this.body.offsetWidth;
+    switch (alignment) {
+      case DropdownMenuAlignment.start:
+      case DropdownMenuAlignment.centerFullWidth:
+        this.dropdownMenuElement.style.left = "0px";
+        this.dropdownMenuElement.style.left = (refElementRect.left - this.dropdownRect.left) + "px";
+        this.dropdownMenuElement.style.right = null;
+        this.dropdownMenuElement.style.maxWidth = rightBorder - refElementRect.left
+            - parseFloat(getComputedStyle(this.dropdownMenuElement).marginRight) + "px";
+        break;
+      case DropdownMenuAlignment.end:
+        this.dropdownMenuElement.style.left = null;
+        this.dropdownMenuElement.style.right = "0px";
+        this.dropdownMenuElement.style.right = (this.dropdownRect.right - refElementRect.right) + "px";
+        this.dropdownMenuElement.style.maxWidth = refElementRect.right
+            - parseFloat(getComputedStyle(this.dropdownMenuElement).marginLeft) + "px";
+        break;
+    }
+  }
+
   private get body(): HTMLElement {
     const root = document.getRootNode() as ShadowRoot | Document;
     return root.querySelector("body");