mirror of https://github.com/apache/superset.git
fix: dashboard top level tabs edit (#19722)
This commit is contained in:
parent
e061955fd0
commit
1c5d3b73df
|
@ -18,7 +18,14 @@
|
||||||
*/
|
*/
|
||||||
/* eslint-env browser */
|
/* eslint-env browser */
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
import React, { FC, useCallback, useEffect, useState, useMemo } from 'react';
|
import React, {
|
||||||
|
FC,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
} from 'react';
|
||||||
import { JsonObject, styled, css, t } from '@superset-ui/core';
|
import { JsonObject, styled, css, t } from '@superset-ui/core';
|
||||||
import { Global } from '@emotion/react';
|
import { Global } from '@emotion/react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
@ -319,6 +326,27 @@ const DashboardBuilder: FC<DashboardBuilderProps> = () => {
|
||||||
[dashboardFiltersOpen, editMode, nativeFiltersEnabled],
|
[dashboardFiltersOpen, editMode, nativeFiltersEnabled],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// If a new tab was added, update the directPathToChild to reflect it
|
||||||
|
const currentTopLevelTabs = useRef(topLevelTabs);
|
||||||
|
useEffect(() => {
|
||||||
|
const currentTabsLength = currentTopLevelTabs.current?.children?.length;
|
||||||
|
const newTabsLength = topLevelTabs?.children?.length;
|
||||||
|
|
||||||
|
if (
|
||||||
|
currentTabsLength !== undefined &&
|
||||||
|
newTabsLength !== undefined &&
|
||||||
|
newTabsLength > currentTabsLength
|
||||||
|
) {
|
||||||
|
const lastTab = getDirectPathToTabIndex(
|
||||||
|
getRootLevelTabsComponent(dashboardLayout),
|
||||||
|
newTabsLength - 1,
|
||||||
|
);
|
||||||
|
dispatch(setDirectPathToChild(lastTab));
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTopLevelTabs.current = topLevelTabs;
|
||||||
|
}, [topLevelTabs]);
|
||||||
|
|
||||||
const renderDraggableContent = useCallback(
|
const renderDraggableContent = useCallback(
|
||||||
({ dropIndicatorProps }: { dropIndicatorProps: JsonObject }) => (
|
({ dropIndicatorProps }: { dropIndicatorProps: JsonObject }) => (
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
// ParentSize uses resize observer so the dashboard will update size
|
// ParentSize uses resize observer so the dashboard will update size
|
||||||
// when its container size changes, due to e.g., builder side panel opening
|
// when its container size changes, due to e.g., builder side panel opening
|
||||||
import React, { FC, useEffect, useMemo, useState } from 'react';
|
import React, { FC, useEffect, useMemo } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
FeatureFlag,
|
FeatureFlag,
|
||||||
|
@ -36,7 +36,6 @@ import {
|
||||||
LayoutItem,
|
LayoutItem,
|
||||||
RootState,
|
RootState,
|
||||||
} from 'src/dashboard/types';
|
} from 'src/dashboard/types';
|
||||||
import getLeafComponentIdFromPath from 'src/dashboard/util/getLeafComponentIdFromPath';
|
|
||||||
import {
|
import {
|
||||||
DASHBOARD_GRID_ID,
|
DASHBOARD_GRID_ID,
|
||||||
DASHBOARD_ROOT_DEPTH,
|
DASHBOARD_ROOT_DEPTH,
|
||||||
|
@ -68,29 +67,27 @@ const useNativeFilterScopes = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const DashboardContainer: FC<DashboardContainerProps> = ({ topLevelTabs }) => {
|
const DashboardContainer: FC<DashboardContainerProps> = ({ topLevelTabs }) => {
|
||||||
|
const nativeFilterScopes = useNativeFilterScopes();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const dashboardLayout = useSelector<RootState, DashboardLayout>(
|
const dashboardLayout = useSelector<RootState, DashboardLayout>(
|
||||||
state => state.dashboardLayout.present,
|
state => state.dashboardLayout.present,
|
||||||
);
|
);
|
||||||
const nativeFilterScopes = useNativeFilterScopes();
|
|
||||||
const directPathToChild = useSelector<RootState, string[]>(
|
const directPathToChild = useSelector<RootState, string[]>(
|
||||||
state => state.dashboardState.directPathToChild,
|
state => state.dashboardState.directPathToChild,
|
||||||
);
|
);
|
||||||
const charts = useSelector<RootState, ChartsState>(state => state.charts);
|
const charts = useSelector<RootState, ChartsState>(state => state.charts);
|
||||||
const [tabIndex, setTabIndex] = useState(
|
|
||||||
getRootLevelTabIndex(dashboardLayout, directPathToChild),
|
|
||||||
);
|
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const tabIndex = useMemo(() => {
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const nextTabIndex = findTabIndexByComponentId({
|
const nextTabIndex = findTabIndexByComponentId({
|
||||||
currentComponent: getRootLevelTabsComponent(dashboardLayout),
|
currentComponent: getRootLevelTabsComponent(dashboardLayout),
|
||||||
directPathToChild,
|
directPathToChild,
|
||||||
});
|
});
|
||||||
if (nextTabIndex > -1) {
|
|
||||||
setTabIndex(nextTabIndex);
|
return nextTabIndex > -1
|
||||||
}
|
? nextTabIndex
|
||||||
}, [getLeafComponentIdFromPath(directPathToChild)]);
|
: getRootLevelTabIndex(dashboardLayout, directPathToChild);
|
||||||
|
}, [dashboardLayout, directPathToChild]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -155,22 +155,6 @@ export class Tabs extends React.PureComponent {
|
||||||
this.setState(() => ({ tabIndex: maxIndex }));
|
this.setState(() => ({ tabIndex: maxIndex }));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextTabsIds.length) {
|
|
||||||
const lastTabId = nextTabsIds[nextTabsIds.length - 1];
|
|
||||||
// if a new tab is added focus on it immediately
|
|
||||||
if (nextTabsIds.length > currTabsIds.length) {
|
|
||||||
// a new tab's path may be empty, here also need to set tabIndex
|
|
||||||
this.setState(() => ({
|
|
||||||
activeKey: lastTabId,
|
|
||||||
tabIndex: maxIndex,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
// if a tab is removed focus on the first
|
|
||||||
if (nextTabsIds.length < currTabsIds.length) {
|
|
||||||
this.setState(() => ({ activeKey: nextTabsIds[0] }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextProps.isComponentVisible) {
|
if (nextProps.isComponentVisible) {
|
||||||
const nextFocusComponent = getLeafComponentIdFromPath(
|
const nextFocusComponent = getLeafComponentIdFromPath(
|
||||||
nextProps.directPathToChild,
|
nextProps.directPathToChild,
|
||||||
|
@ -179,7 +163,14 @@ export class Tabs extends React.PureComponent {
|
||||||
this.props.directPathToChild,
|
this.props.directPathToChild,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (nextFocusComponent !== currentFocusComponent) {
|
// If the currently selected component is different than the new one,
|
||||||
|
// or the tab length/order changed, calculate the new tab index and
|
||||||
|
// replace it if it's different than the current one
|
||||||
|
if (
|
||||||
|
nextFocusComponent !== currentFocusComponent ||
|
||||||
|
(nextFocusComponent === currentFocusComponent &&
|
||||||
|
currTabsIds !== nextTabsIds)
|
||||||
|
) {
|
||||||
const nextTabIndex = findTabIndexByComponentId({
|
const nextTabIndex = findTabIndexByComponentId({
|
||||||
currentComponent: nextProps.component,
|
currentComponent: nextProps.component,
|
||||||
directPathToChild: nextProps.directPathToChild,
|
directPathToChild: nextProps.directPathToChild,
|
||||||
|
@ -219,9 +210,12 @@ export class Tabs extends React.PureComponent {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
handleEdit = (key, action) => {
|
handleEdit = (event, action) => {
|
||||||
const { component, createComponent } = this.props;
|
const { component, createComponent } = this.props;
|
||||||
if (action === 'add') {
|
if (action === 'add') {
|
||||||
|
// Prevent the tab container to be selected
|
||||||
|
event?.stopPropagation?.();
|
||||||
|
|
||||||
createComponent({
|
createComponent({
|
||||||
destination: {
|
destination: {
|
||||||
id: component.id,
|
id: component.id,
|
||||||
|
@ -234,7 +228,7 @@ export class Tabs extends React.PureComponent {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else if (action === 'remove') {
|
} else if (action === 'remove') {
|
||||||
this.showDeleteConfirmModal(key);
|
this.showDeleteConfirmModal(event);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -261,8 +255,12 @@ export class Tabs extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDeleteTab(tabIndex) {
|
handleDeleteTab(tabIndex) {
|
||||||
|
// If we're removing the currently selected tab,
|
||||||
|
// select the previous one (if any)
|
||||||
|
if (this.state.tabIndex === tabIndex) {
|
||||||
this.handleClickTab(Math.max(0, tabIndex - 1));
|
this.handleClickTab(Math.max(0, tabIndex - 1));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleDropOnTab(dropResult) {
|
handleDropOnTab(dropResult) {
|
||||||
const { component } = this.props;
|
const { component } = this.props;
|
||||||
|
|
Loading…
Reference in New Issue