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 2026/09/15 07:28:47 UTC

(myfaces-tobago) branch main updated: fix(dropdown): menu size

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

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


The following commit(s) were added to refs/heads/main by this push:
     new 682602e755 fix(dropdown): menu size
682602e755 is described below

commit 682602e7552b0199b89b8c8e1158d59312b27752
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Mon Sep 14 12:25:33 2026 +0200

    fix(dropdown): menu size
    
    add test
    
    Set display: inline-block; so the padding is respected. By default, the padding is part of the scroll area.
    
    Calculate the max-height first, instead of setting it to "initial". The value "initial" means that the whole browser window is used. But if later a max-height is set, a vertical scrollbar may appear. The vertical scrollbar must have some space, so it might cause a horizontal scrollbar.
    
    Issue: TOBAGO-2542
---
 .../content/900-test/button/dropdown/Dropdown.css  | 30 +++++++++++++
 .../content/900-test/button/dropdown/Dropdown.js   | 28 ++++++++++++
 .../900-test/button/dropdown/Dropdown.xhtml        | 26 +++++++++++
 .../src/test/typescript/button.test.ts             | 23 ++++++++++
 tobago-theme/src/main/scss/_tobago.scss            |  1 +
 .../src/main/ts/tobago-dropdown-menu.ts            | 52 ++++++++++------------
 6 files changed, 132 insertions(+), 28 deletions(-)

diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/button/dropdown/Dropdown.css b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/button/dropdown/Dropdown.css
new file mode 100644
index 0000000000..583eaad929
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/button/dropdown/Dropdown.css
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+.tobago2542-a-content {
+  background-color: #f2f542;
+  width: 380px;
+  height: 400px;
+}
+
+.tobago2542-b-content {
+  background-color: #f2f542;
+  width: 371px;
+  height: 700px;
+}
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/button/dropdown/Dropdown.js b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/button/dropdown/Dropdown.js
new file mode 100644
index 0000000000..3c93f74d12
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/button/dropdown/Dropdown.js
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+document.addEventListener("tobago.init", function () {
+  window.addEventListener("load", updateResolutionDisplay);
+  window.addEventListener("resize", updateResolutionDisplay);
+});
+
+function updateResolutionDisplay() {
+  const docRect = document.documentElement.getBoundingClientRect();
+  document.getElementById("resolution").textContent = docRect.width + "x" + window.innerHeight;
+}
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/button/dropdown/Dropdown.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/button/dropdown/Dropdown.xhtml
index eb02e9eb8d..522dad168b 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/button/dropdown/Dropdown.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/900-test/button/dropdown/Dropdown.xhtml
@@ -21,6 +21,10 @@
                 xmlns:tc="http://myfaces.apache.org/tobago/component"
                 xmlns:ui="jakarta.faces.facelets">
 
+  <tc:style file="Dropdown.css"/>
+  <tc:script file="Dropdown.js"/>
+  <div id="resolution"></div>
+
   <tc:in id="beforeComp" label="Component before"/>
 
   <tc:button id="dropdownForm" label="Form" omit="true">
@@ -171,4 +175,26 @@
     <tc:link id="fixedRight0Entry" label="Link" omit="true"/>
   </tc:button>
 
+  <p>TOBAGO-2542 dropdown content has scrollbars</p>
+  <tc:flowLayout textAlign="right">
+    <tc:button id="dropdownFormTobago2542a" label="TOBAGO-2542_a" omit="true">
+      <f:facet name="panel">
+        <tc:panel>
+          <tc:style customClass="tobago2542-a-content"/>
+          <p>Content</p>
+        </tc:panel>
+      </f:facet>
+    </tc:button>
+  </tc:flowLayout>
+  <tc:flowLayout textAlign="right">
+    <tc:button id="dropdownFormTobago2542b" label="TOBAGO-2542_b" omit="true">
+      <f:facet name="panel">
+        <tc:panel>
+          <tc:style customClass="tobago2542-b-content"/>
+          <p>Content</p>
+        </tc:panel>
+      </f:facet>
+    </tc:button>
+  </tc:flowLayout>
+
 </ui:composition>
diff --git a/tobago-example/tobago-example-demo/src/test/typescript/button.test.ts b/tobago-example/tobago-example-demo/src/test/typescript/button.test.ts
index abe215d0b3..4e6e8b900a 100644
--- a/tobago-example/tobago-example-demo/src/test/typescript/button.test.ts
+++ b/tobago-example/tobago-example-demo/src/test/typescript/button.test.ts
@@ -486,4 +486,27 @@ test.describe("900-test/button/dropdown/Dropdown.xhtml", () => {
     await expect(fixedRight0Entry).not.toBeVisible();
     await expect(fixedRight0ToggleButton).toBeFocused();
   });
+
+  test("TOBAGO-2542_a: dropdown must open to the left", async ({page, browserName}) => {
+    const toggleButton = page.locator("[id='page:mainForm:dropdownFormTobago2542a::command']");
+    const dropdownMenu = page.locator(".tobago-dropdown-menu[data-tobago-for='page:mainForm:dropdownFormTobago2542a']");
+
+    await expect(dropdownMenu).not.toBeVisible();
+    await toggleButton.scrollIntoViewIfNeeded();
+    await toggleButton.click();
+    await expect(dropdownMenu).toBeVisible();
+    await expect(dropdownMenu).toHaveCSS("right", "236px");
+  });
+
+  test("TOBAGO-2542_b: dropdown must open to the left", async ({page, browserName}) => {
+    const toggleButton = page.locator("[id='page:mainForm:dropdownFormTobago2542b::command']");
+    const dropdownMenu = page.locator(".tobago-dropdown-menu[data-tobago-for='page:mainForm:dropdownFormTobago2542b']");
+
+    await expect(dropdownMenu).not.toBeVisible();
+    await expect(dropdownMenu).not.toBeVisible();
+    await toggleButton.scrollIntoViewIfNeeded();
+    await toggleButton.click();
+    await expect(dropdownMenu).toBeVisible();
+    await expect(dropdownMenu).toHaveCSS("right", "236px");
+  });
 });
diff --git a/tobago-theme/src/main/scss/_tobago.scss b/tobago-theme/src/main/scss/_tobago.scss
index 5b9fd1a8cb..bf22da47b7 100644
--- a/tobago-theme/src/main/scss/_tobago.scss
+++ b/tobago-theme/src/main/scss/_tobago.scss
@@ -90,6 +90,7 @@ @mixin buttonDropdownMenu() {
     @include tobagoDropdownMenuValues();
 
     .tobago-panel-facet {
+      display: inline-block;
       padding: var(--#{$prefix}dropdown-item-padding-y) var(--#{$prefix}dropdown-item-padding-x);
     }
 
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 cd4f788acb..5853a04aef 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
@@ -154,9 +154,30 @@ export class DropdownMenu {
   private updatePosition(): void {
     const refElementRect = this.referenceElement.getBoundingClientRect();
 
-    /* After updating the position, max-height is set as an inline-style. Make sure no CSS class set max-height for this
-       calculation. see: TOBAGO-2541 */
-    this.dropdownMenuElement.style.maxHeight = "initial";
+    //calc vertical positioning and max-height
+    const upperBorder = this.stickyHeader ? this.stickyHeader.offsetHeight : 0;
+    const lowerBorder = this.fixedFooter ? this.fixedFooter.offsetTop : window.innerHeight;
+    const spaceAbove = refElementRect.top - upperBorder;
+    const spaceBelow = lowerBorder - refElementRect.bottom;
+    if (spaceBelow >= spaceAbove) {
+      this.dropdownMenuElement.style.marginTop = "0px";
+      this.dropdownMenuElement.style.top = "0px";
+      this.dropdownMenuElement.style.top = (refElementRect.bottom - this.dropdownRect.top) + "px";
+      this.dropdownMenuElement.style.bottom = null;
+      this.dropdownMenuElement.style.marginTop = "var(--tobago-dropdown-menu-component-offset)";
+      this.dropdownMenuElement.style.marginBottom = null;
+      this.dropdownMenuElement.style.maxHeight = spaceBelow
+          - parseFloat(getComputedStyle(this.dropdownMenuElement).marginBottom) + "px";
+    } else {
+      this.dropdownMenuElement.style.marginBottom = "0px";
+      this.dropdownMenuElement.style.top = null;
+      this.dropdownMenuElement.style.bottom = "0px";
+      this.dropdownMenuElement.style.bottom = (this.dropdownRect.bottom - refElementRect.top) + "px";
+      this.dropdownMenuElement.style.marginTop = null;
+      this.dropdownMenuElement.style.marginBottom = "var(--tobago-dropdown-menu-component-offset)";
+      this.dropdownMenuElement.style.maxHeight = spaceAbove
+          - parseFloat(getComputedStyle(this.dropdownMenuElement).marginTop) + "px";
+    }
 
     //calc horizontal positioning and max-width
     switch (this.alignment) {
@@ -182,31 +203,6 @@ export class DropdownMenu {
         }
         break;
     }
-
-    //calc vertical positioning and max-height
-    const upperBorder = this.stickyHeader ? this.stickyHeader.offsetHeight : 0;
-    const lowerBorder = this.fixedFooter ? this.fixedFooter.offsetTop : window.innerHeight;
-    const spaceAbove = refElementRect.top - upperBorder;
-    const spaceBelow = lowerBorder - refElementRect.bottom;
-    if (spaceBelow >= spaceAbove) {
-      this.dropdownMenuElement.style.marginTop = "0px";
-      this.dropdownMenuElement.style.top = "0px";
-      this.dropdownMenuElement.style.top = (refElementRect.bottom - this.dropdownRect.top) + "px";
-      this.dropdownMenuElement.style.bottom = null;
-      this.dropdownMenuElement.style.marginTop = "var(--tobago-dropdown-menu-component-offset)";
-      this.dropdownMenuElement.style.marginBottom = null;
-      this.dropdownMenuElement.style.maxHeight = spaceBelow
-          - parseFloat(getComputedStyle(this.dropdownMenuElement).marginBottom) + "px";
-    } else {
-      this.dropdownMenuElement.style.marginBottom = "0px";
-      this.dropdownMenuElement.style.top = null;
-      this.dropdownMenuElement.style.bottom = "0px";
-      this.dropdownMenuElement.style.bottom = (this.dropdownRect.bottom - refElementRect.top) + "px";
-      this.dropdownMenuElement.style.marginTop = null;
-      this.dropdownMenuElement.style.marginBottom = "var(--tobago-dropdown-menu-component-offset)";
-      this.dropdownMenuElement.style.maxHeight = spaceAbove
-          - parseFloat(getComputedStyle(this.dropdownMenuElement).marginTop) + "px";
-    }
   }
 
   private calcHorizontalPositioningAndMaxWidth(alignment: DropdownMenuAlignment, extend: boolean,