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

diegopucci 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 aba3b81e13 fix: DropdownContainer resize algorithm (#22318)
aba3b81e13 is described below

commit aba3b81e132cf2c92a79e5d381f01550481def9b
Author: Michael S. Molina <[email protected]>
AuthorDate: Sun Dec 4 09:52:27 2022 -0500

    fix: DropdownContainer resize algorithm (#22318)
---
 .../DropdownContainer.stories.tsx                  |  21 ++--
 .../src/components/DropdownContainer/index.tsx     | 113 ++++++++++++---------
 2 files changed, 78 insertions(+), 56 deletions(-)

diff --git 
a/superset-frontend/src/components/DropdownContainer/DropdownContainer.stories.tsx
 
b/superset-frontend/src/components/DropdownContainer/DropdownContainer.stories.tsx
index 4e6f5fedfd..4aeaa03a16 100644
--- 
a/superset-frontend/src/components/DropdownContainer/DropdownContainer.stories.tsx
+++ 
b/superset-frontend/src/components/DropdownContainer/DropdownContainer.stories.tsx
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React, { useEffect, useState } from 'react';
+import React, { useCallback, useState } from 'react';
 import { isEqual } from 'lodash';
 import { css } from '@superset-ui/core';
 import Select from '../Select/Select';
@@ -63,10 +63,15 @@ export const Component = (props: DropdownContainerProps) => 
{
   const [items, setItems] = useState<ItemsType>([]);
   const [overflowingState, setOverflowingState] = useState<OverflowingState>();
   const containerRef = React.useRef<Ref>(null);
-
-  useEffect(() => {
-    setItems(generateItems(overflowingState));
-  }, [overflowingState]);
+  const onOverflowingStateChange = useCallback(
+    value => {
+      if (!isEqual(overflowingState, value)) {
+        setItems(generateItems(value));
+        setOverflowingState(value);
+      }
+    },
+    [overflowingState],
+  );
 
   return (
     <div>
@@ -86,11 +91,7 @@ export const Component = (props: DropdownContainerProps) => {
         <DropdownContainer
           {...props}
           items={items}
-          onOverflowingStateChange={value => {
-            if (!isEqual(overflowingState, value)) {
-              setOverflowingState(value);
-            }
-          }}
+          onOverflowingStateChange={onOverflowingStateChange}
           ref={containerRef}
         />
       </div>
diff --git a/superset-frontend/src/components/DropdownContainer/index.tsx 
b/superset-frontend/src/components/DropdownContainer/index.tsx
index 92a2ff82e7..29380d4a93 100644
--- a/superset-frontend/src/components/DropdownContainer/index.tsx
+++ b/superset-frontend/src/components/DropdownContainer/index.tsx
@@ -135,50 +135,6 @@ const DropdownContainer = forwardRef(
 
     const [showOverflow, setShowOverflow] = useState(false);
 
-    useLayoutEffect(() => {
-      const container = current?.children.item(0);
-      if (container) {
-        const { children } = container;
-        const childrenArray = Array.from(children);
-
-        // Stores items width once
-        if (itemsWidth.length === 0) {
-          setItemsWidth(
-            childrenArray.map(child => child.getBoundingClientRect().width),
-          );
-        }
-
-        // Calculates the index of the first overflowed element
-        const index = childrenArray.findIndex(
-          child =>
-            child.getBoundingClientRect().right >
-            container.getBoundingClientRect().right,
-        );
-        setOverflowingIndex(index === -1 ? children.length : index);
-
-        if (width > previousWidth && overflowingIndex !== -1) {
-          // Calculates remaining space in the container
-          const button = current?.children.item(1);
-          const buttonRight = button?.getBoundingClientRect().right || 0;
-          const containerRight = current?.getBoundingClientRect().right || 0;
-          const remainingSpace = containerRight - buttonRight;
-          // Checks if the first element in the dropdown fits in the remaining 
space
-          const fitsInRemainingSpace = remainingSpace >= itemsWidth[0];
-          if (fitsInRemainingSpace && overflowingIndex < items.length) {
-            // Moves element from dropdown to container
-            setOverflowingIndex(overflowingIndex + 1);
-          }
-        }
-      }
-    }, [
-      current,
-      items.length,
-      itemsWidth,
-      overflowingIndex,
-      previousWidth,
-      width,
-    ]);
-
     const reduceItems = (items: Item[]): [Item[], string[]] =>
       items.reduce(
         ([items, ids], item) => {
@@ -206,11 +162,76 @@ const DropdownContainer = forwardRef(
     const [overflowedItems, overflowedIds] = useMemo(
       () =>
         overflowingIndex !== -1
-          ? reduceItems(items.slice(overflowingIndex, items.length))
+          ? reduceItems(items.slice(overflowingIndex))
           : [[], []],
       [items, overflowingIndex],
     );
 
+    useEffect(() => {
+      if (itemsWidth.length !== items.length) {
+        const container = current?.children.item(0);
+        if (container) {
+          const { children } = container;
+          const childrenArray = Array.from(children);
+          setItemsWidth(
+            childrenArray.map(child => child.getBoundingClientRect().width),
+          );
+        }
+      }
+    }, [current?.children, items.length, itemsWidth.length]);
+
+    useLayoutEffect(() => {
+      const container = current?.children.item(0);
+      if (container) {
+        const { children } = container;
+        const childrenArray = Array.from(children);
+
+        // Calculates the index of the first overflowed element
+        // +1 is to give at least one pixel of difference and avoid flakiness
+        const index = childrenArray.findIndex(
+          child =>
+            child.getBoundingClientRect().right >
+            container.getBoundingClientRect().right + 1,
+        );
+
+        // If elements fit (-1) and there's overflowed items
+        // then preserve the overflow index. We can't use overflowIndex
+        // directly because the items may have been modified
+        let newOverflowingIndex =
+          index === -1 && overflowedItems.length > 0
+            ? items.length - overflowedItems.length
+            : index;
+
+        if (width > previousWidth) {
+          // Calculates remaining space in the container
+          const button = current?.children.item(1);
+          const buttonRight = button?.getBoundingClientRect().right || 0;
+          const containerRight = current?.getBoundingClientRect().right || 0;
+          const remainingSpace = containerRight - buttonRight;
+
+          // Checks if some elements in the dropdown fits in the remaining 
space
+          let sum = 0;
+          for (let i = childrenArray.length; i < items.length; i += 1) {
+            sum += itemsWidth[i];
+            if (sum <= remainingSpace) {
+              newOverflowingIndex = i + 1;
+            } else {
+              break;
+            }
+          }
+        }
+
+        setOverflowingIndex(newOverflowingIndex);
+      }
+    }, [
+      current,
+      items.length,
+      itemsWidth,
+      overflowedItems.length,
+      previousWidth,
+      width,
+    ]);
+
     useEffect(() => {
       if (onOverflowingStateChange) {
         onOverflowingStateChange({
@@ -296,7 +317,7 @@ const DropdownContainer = forwardRef(
             align-items: center;
             gap: ${theme.gridUnit * 4}px;
             margin-right: ${theme.gridUnit * 3}px;
-            min-width: 100px;
+            min-width: 0px;
           `}
           data-test="container"
           style={style}

Reply via email to