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

enzomartellucci pushed a commit to branch enxdev/style/custom-tabs
in repository https://gitbox.apache.org/repos/asf/superset.git

commit e24a1bfb977979d68b8db8afdf7cb4a71394fcad
Author: Enzo Martellucci <[email protected]>
AuthorDate: Mon Dec 29 13:33:29 2025 +0100

    fix(dashboard): resolve tab reorder state sync issues
    
    Fix multiple issues with tab drag-and-drop reordering in dashboard edit 
mode:
    
    - Add key prop to DndContext to force re-initialization when tab order 
changes,
      preventing stale state in subsequent drag operations
    - Use useRef to track current tabIds in callbacks, avoiding stale closure 
issues
    - Remove transition during drag for immediate visual feedback
    - Ensure tab labels maintain full opacity during drag operations
    - Add activeKey to handleTabsReorder dependencies for proper state sync
    - Fix LineEditableTabs TypeScript types to properly expose TabsProps
---
 .../superset-ui-core/src/components/Tabs/Tabs.tsx  |  7 +++++--
 .../components/gridComponents/Tabs/Tabs.jsx        |  2 +-
 .../gridComponents/TabsRenderer/TabsRenderer.tsx   | 23 +++++++++++++++++++---
 3 files changed, 26 insertions(+), 6 deletions(-)

diff --git 
a/superset-frontend/packages/superset-ui-core/src/components/Tabs/Tabs.tsx 
b/superset-frontend/packages/superset-ui-core/src/components/Tabs/Tabs.tsx
index 291b16efea..090d25da30 100644
--- a/superset-frontend/packages/superset-ui-core/src/components/Tabs/Tabs.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/components/Tabs/Tabs.tsx
@@ -16,6 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import type { FC } from 'react';
 import { css, styled, useTheme } from '@apache-superset/core/ui';
 
 // eslint-disable-next-line no-restricted-imports
@@ -161,8 +162,10 @@ export const StyledLineEditableTabs = styled(EditableTabs)`
   }
 `;
 
-export const LineEditableTabs = Object.assign(StyledLineEditableTabs, {
+export const LineEditableTabs: FC<TabsProps> & {
+  TabPane: typeof StyledTabPane;
+} = Object.assign(StyledLineEditableTabs, {
   TabPane: StyledTabPane,
-});
+}) as FC<TabsProps> & { TabPane: typeof StyledTabPane };
 
 export default Tabs;
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/Tabs/Tabs.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Tabs/Tabs.jsx
index b23f1da567..6beb76db72 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Tabs/Tabs.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Tabs/Tabs.jsx
@@ -362,7 +362,7 @@ const Tabs = props => {
         setActiveKey(currentActiveTabId);
       }
     },
-    [props.component, props.updateComponents, selectedTabIndex],
+    [props.component, props.updateComponents, selectedTabIndex, activeKey],
   );
 
   const {
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/TabsRenderer/TabsRenderer.tsx
 
b/superset-frontend/src/dashboard/components/gridComponents/TabsRenderer/TabsRenderer.tsx
index 445ddf1db3..bb92c016ad 100644
--- 
a/superset-frontend/src/dashboard/components/gridComponents/TabsRenderer/TabsRenderer.tsx
+++ 
b/superset-frontend/src/dashboard/components/gridComponents/TabsRenderer/TabsRenderer.tsx
@@ -22,6 +22,7 @@ import {
   ReactElement,
   RefObject,
   useCallback,
+  useRef,
   useState,
 } from 'react';
 import { styled } from '@apache-superset/core/ui';
@@ -61,6 +62,16 @@ const StyledTabsContainer = styled.div<{ isDragging?: 
boolean }>`
     height: calc(100% - 47px);
   }
 
+  /* Ensure tab labels maintain full opacity during drag */
+  .ant-tabs-tab {
+    .dragdroppable-tab,
+    .editable-title,
+    textarea {
+      opacity: 1;
+      color: inherit;
+    }
+  }
+
   /* Hide ink-bar during drag */
   ${({ isDragging }) =>
     isDragging &&
@@ -126,7 +137,7 @@ const DraggableTabNode: 
React.FC<Readonly<DraggableTabNodeProps>> = ({
     ...props.style,
     position: 'relative',
     transform: transform ? `translate3d(${transform.x}px, 0, 0)` : undefined,
-    transition,
+    transition: isDragging ? 'none' : transition,
     cursor: disabled ? 'default' : 'move',
     zIndex: isDragging ? 1000 : 'auto',
     opacity: 1,
@@ -163,6 +174,10 @@ const TabsRenderer = memo<TabsRendererProps>(
   }) => {
     const [activeId, setActiveId] = useState<string | null>(null);
 
+    // Use ref to always have access to the current tabIds in callbacks
+    const tabIdsRef = useRef(tabIds);
+    tabIdsRef.current = tabIds;
+
     const sensor = useSensor(PointerSensor, {
       activationConstraint: { distance: 10 },
     });
@@ -173,9 +188,10 @@ const TabsRenderer = memo<TabsRendererProps>(
 
     const onDragEnd = useCallback(
       ({ active, over }: DragEndEvent) => {
+        const currentTabIds = tabIdsRef.current;
         if (active.id !== over?.id && onTabsReorder) {
-          const activeIndex = tabIds.findIndex(id => id === active.id);
-          const overIndex = tabIds.findIndex(id => id === over?.id);
+          const activeIndex = currentTabIds.findIndex(id => id === active.id);
+          const overIndex = currentTabIds.findIndex(id => id === over?.id);
           onTabsReorder(activeIndex, overIndex);
         }
         setActiveId(null);
@@ -220,6 +236,7 @@ const TabsRenderer = memo<TabsRendererProps>(
           {...(editMode && {
             renderTabBar: (tabBarProps, DefaultTabBar) => (
               <DndContext
+                key={tabIds.join('-')}
                 sensors={[sensor]}
                 onDragStart={onDragStart}
                 onDragEnd={onDragEnd}

Reply via email to