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

EnxDev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 1ab6077e060 fix(filters): clamp filter bar row while remeasuring after 
items change (#43966)
1ab6077e060 is described below

commit 1ab6077e06066d789480fe9139d0da67a3dd94be
Author: Enzo Martellucci <[email protected]>
AuthorDate: Wed Sep 9 10:52:28 2026 +0200

    fix(filters): clamp filter bar row while remeasuring after items change 
(#43966)
    
    Co-authored-by: Claude Sonnet 5 <[email protected]>
---
 .../DropdownContainer/DropdownContainer.test.tsx   | 71 ++++++++++++++++++++++
 .../DropdownContainer/DropdownContainer.tsx        | 13 +++-
 2 files changed, 83 insertions(+), 1 deletion(-)

diff --git 
a/superset-frontend/packages/superset-ui-core/src/components/DropdownContainer/DropdownContainer.test.tsx
 
b/superset-frontend/packages/superset-ui-core/src/components/DropdownContainer/DropdownContainer.test.tsx
index 222a5b72dea..7bde7a1e173 100644
--- 
a/superset-frontend/packages/superset-ui-core/src/components/DropdownContainer/DropdownContainer.test.tsx
+++ 
b/superset-frontend/packages/superset-ui-core/src/components/DropdownContainer/DropdownContainer.test.tsx
@@ -16,6 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import type { CSSProperties } from 'react';
 import { screen, render } from '@superset-ui/core/spec';
 import { Button, DropdownContainer, Icons } from '..';
 
@@ -178,3 +179,73 @@ test('component renders and functions without throwing 
errors', () => {
   // Basic functionality test
   expect(screen.getByText('Element 1')).toBeInTheDocument();
 });
+
+const ITEM_WIDTH = 100;
+/* Width the flex layout leaves the row once the trigger button is laid out. */
+const ROW_WIDTH = 250;
+/* Item count of the transient row that holds every item while remeasuring. */
+const ALL_ITEMS = 4;
+
+/**
+ * Lays items out at ITEM_WIDTH each. In the steady state the row is reported 
at
+ * the flex-bounded ROW_WIDTH, so a three-item row overflows and the trigger
+ * shows. In the all-items frame the row is reported at its own content width
+ * instead, which is the frame Edge can paint before the flex layout bounds the
+ * row and its children spill out. `onRowMeasure` receives the row during that
+ * frame so a test can assert how it is styled.
+ */
+const mockBoundingRects = (onRowMeasure: (row: HTMLElement) => void) => {
+  const getBoundingClientRect: (this: HTMLElement) => DOMRect = function () {
+    let right: number;
+    if (this.dataset.test === 'container') {
+      const allItemsFrame = this.children.length === ALL_ITEMS;
+      right = allItemsFrame ? this.children.length * ITEM_WIDTH : ROW_WIDTH;
+      if (allItemsFrame) {
+        onRowMeasure(this);
+      }
+    } else {
+      const itemNumber = Number(this.textContent?.match(/Element (\d+)/)?.[1]);
+      right = itemNumber ? itemNumber * ITEM_WIDTH : ROW_WIDTH;
+    }
+    return {
+      bottom: 0,
+      height: 0,
+      left: right - ITEM_WIDTH,
+      right,
+      top: 0,
+      width: ITEM_WIDTH,
+      x: right - ITEM_WIDTH,
+      y: 0,
+      toJSON: () => ({}),
+    };
+  };
+  jest
+    .spyOn(HTMLElement.prototype, 'getBoundingClientRect')
+    .mockImplementation(getBoundingClientRect);
+};
+
+/* Grows an overflowing row by one item, which mounts every item for a frame
+ * while the new overflow index is calculated. */
+const remeasureWithExtraItem = (style?: CSSProperties) => {
+  const { rerender } = render(<DropdownContainer items={generateItems(3)} />);
+  rerender(<DropdownContainer items={generateItems(3)} />);
+  expect(screen.getByTestId('dropdown-container-btn')).toBeInTheDocument();
+  rerender(
+    <DropdownContainer items={generateItems(ALL_ITEMS)} style={style} />,
+  );
+};
+
+test('clips the item row while remeasuring, then restores it', () => {
+  const measured: string[] = [];
+  mockBoundingRects(row => {
+    measured.push(row.style.overflow);
+  });
+
+  remeasureWithExtraItem({ overflow: 'visible' });
+
+  /* While the row holds every item its children can spill past its own box, so
+   * clipping wins over the consumer's `overflow: visible` for that frame. Once
+   * the new overflow index is applied the consumer's value comes back. */
+  expect(measured[0]).toBe('hidden');
+  expect(measured.at(-1)).toBe('visible');
+});
diff --git 
a/superset-frontend/packages/superset-ui-core/src/components/DropdownContainer/DropdownContainer.tsx
 
b/superset-frontend/packages/superset-ui-core/src/components/DropdownContainer/DropdownContainer.tsx
index 23ab754d945..e0f9ac876f0 100644
--- 
a/superset-frontend/packages/superset-ui-core/src/components/DropdownContainer/DropdownContainer.tsx
+++ 
b/superset-frontend/packages/superset-ui-core/src/components/DropdownContainer/DropdownContainer.tsx
@@ -330,7 +330,18 @@ export const DropdownContainer = forwardRef(
             min-width: 0px;
           `}
           data-test="container"
-          style={style}
+          style={
+            recalculating
+              ? {
+                  ...style,
+                  /* The flex layout already bounds the row's own box, but its
+                   * children can spill past that box in the frame Edge paints
+                   * before the new overflow index is applied. Clipping keeps
+                   * them inside the filter bar. */
+                  overflow: 'hidden',
+                }
+              : style
+          }
         >
           {notOverflowedItems.map(item => item.element)}
         </div>

Reply via email to