From commits-return-2624-archive-asf-public=cust-asf.ponee.io@superset.incubator.apache.org Tue Apr 30 07:40:53 2019 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [207.244.88.153]) by mx-eu-01.ponee.io (Postfix) with SMTP id B7B0B180651 for ; Tue, 30 Apr 2019 09:40:52 +0200 (CEST) Received: (qmail 49605 invoked by uid 500); 30 Apr 2019 07:40:52 -0000 Mailing-List: contact commits-help@superset.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@superset.incubator.apache.org Delivered-To: mailing list commits@superset.incubator.apache.org Received: (qmail 49595 invoked by uid 99); 30 Apr 2019 07:40:52 -0000 Received: from ec2-52-202-80-70.compute-1.amazonaws.com (HELO gitbox.apache.org) (52.202.80.70) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 30 Apr 2019 07:40:52 +0000 Received: by gitbox.apache.org (ASF Mail Server at gitbox.apache.org, from userid 33) id 0342582F00; Tue, 30 Apr 2019 07:40:51 +0000 (UTC) Date: Tue, 30 Apr 2019 07:40:51 +0000 To: "commits@superset.apache.org" Subject: [incubator-superset] branch master updated: [dashboard] allow user re-order top-level tabs (#7390) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Message-ID: <155661005129.22219.12262246115513031768@gitbox.apache.org> From: graceguo@apache.org X-Git-Host: gitbox.apache.org X-Git-Repo: incubator-superset X-Git-Refname: refs/heads/master X-Git-Reftype: branch X-Git-Oldrev: ca2996c78f679260eb79c6008e276733df5fb653 X-Git-Newrev: 9e703f399b78af1a198ba5d69353385b77dd701e X-Git-Rev: 9e703f399b78af1a198ba5d69353385b77dd701e X-Git-NotificationType: ref_changed_plus_diff X-Git-Multimail-Version: 1.5.dev Auto-Submitted: auto-generated This is an automated email from the ASF dual-hosted git repository. graceguo pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-superset.git The following commit(s) were added to refs/heads/master by this push: new 9e703f3 [dashboard] allow user re-order top-level tabs (#7390) 9e703f3 is described below commit 9e703f399b78af1a198ba5d69353385b77dd701e Author: Grace Guo AuthorDate: Tue Apr 30 00:40:35 2019 -0700 [dashboard] allow user re-order top-level tabs (#7390) --- .../dashboard/actions/dashboardLayout_spec.js | 67 +++++++++++++++++++++- .../src/dashboard/actions/dashboardLayout.js | 29 +++++++++- .../src/dashboard/components/DashboardBuilder.jsx | 7 ++- .../dashboard/components/gridComponents/Tab.jsx | 5 -- 4 files changed, 95 insertions(+), 13 deletions(-) diff --git a/superset/assets/spec/javascripts/dashboard/actions/dashboardLayout_spec.js b/superset/assets/spec/javascripts/dashboard/actions/dashboardLayout_spec.js index d57207d..0dfca9c 100644 --- a/superset/assets/spec/javascripts/dashboard/actions/dashboardLayout_spec.js +++ b/superset/assets/spec/javascripts/dashboard/actions/dashboardLayout_spec.js @@ -39,7 +39,10 @@ import { } from '../../../../src/dashboard/actions/dashboardLayout'; import { setUnsavedChanges } from '../../../../src/dashboard/actions/dashboardState'; -import { addInfoToast } from '../../../../src/messageToasts/actions'; +import { + addWarningToast, + ADD_TOAST, +} from '../../../../src/messageToasts/actions'; import { DASHBOARD_GRID_TYPE, @@ -334,7 +337,9 @@ describe('dashboardLayout actions', () => { const thunk = handleComponentDrop(dropResult); thunk(dispatch, getState); - expect(dispatch.getCall(0).args[0].type).toEqual(addInfoToast('').type); + expect(dispatch.getCall(0).args[0].type).toEqual( + addWarningToast('').type, + ); expect(dispatch.callCount).toBe(1); }); @@ -402,6 +407,64 @@ describe('dashboardLayout actions', () => { expect(dispatch.callCount).toBe(2); }); + + it('should dispatch a toast if drop top-level tab into nested tab', () => { + const { getState, dispatch } = setup({ + dashboardLayout: { + present: { + [DASHBOARD_ROOT_ID]: { + children: ['TABS-ROOT_TABS'], + id: DASHBOARD_ROOT_ID, + type: 'ROOT', + }, + 'TABS-ROOT_TABS': { + children: ['TAB-iMppmTOQy', 'TAB-rt1y8cQ6K9', 'TAB-X_pnCIwPN'], + id: 'TABS-ROOT_TABS', + meta: {}, + parents: ['ROOT_ID'], + type: TABS_TYPE, + }, + 'TABS-ROW_TABS': { + children: [ + 'TAB-dKIDBT03bQ', + 'TAB-PtxY5bbTe', + 'TAB-Wc2P-yGMz', + 'TAB-U-xe_si7i', + ], + id: 'TABS-ROW_TABS', + meta: {}, + parents: ['ROOT_ID', 'TABS-ROOT_TABS', 'TAB-X_pnCIwPN'], + type: TABS_TYPE, + }, + }, + }, + }); + const dropResult = { + source: { + id: 'TABS-ROOT_TABS', + index: 1, + type: TABS_TYPE, + }, + destination: { + id: 'TABS-ROW_TABS', + index: 1, + type: TABS_TYPE, + }, + dragging: { + id: 'TAB-rt1y8cQ6K9', + meta: { text: 'New Tab' }, + type: 'TAB', + }, + }; + + const thunk1 = handleComponentDrop(dropResult); + thunk1(dispatch, getState); + + const thunk2 = dispatch.getCall(0).args[0]; + thunk2(dispatch, getState); + + expect(dispatch.getCall(1).args[0].type).toEqual(ADD_TOAST); + }); }); describe('undoLayoutAction', () => { diff --git a/superset/assets/src/dashboard/actions/dashboardLayout.js b/superset/assets/src/dashboard/actions/dashboardLayout.js index b76966c..b276e37 100644 --- a/superset/assets/src/dashboard/actions/dashboardLayout.js +++ b/superset/assets/src/dashboard/actions/dashboardLayout.js @@ -17,8 +17,9 @@ * under the License. */ import { ActionCreators as UndoActionCreators } from 'redux-undo'; +import { t } from '@superset-ui/translation'; -import { addInfoToast } from '../../messageToasts/actions'; +import { addWarningToast } from '../../messageToasts/actions'; import { setUnsavedChanges } from './dashboardState'; import { TABS_TYPE, ROW_TYPE } from '../util/componentTypes'; import { @@ -153,8 +154,10 @@ export function handleComponentDrop(dropResult) { if (overflowsParent) { return dispatch( - addInfoToast( - `There is not enough space for this component. Try decreasing its width, or increasing the destination width.`, + addWarningToast( + t( + `There is not enough space for this component. Try decreasing its width, or increasing the destination width.`, + ), ), ); } @@ -162,12 +165,30 @@ export function handleComponentDrop(dropResult) { const { source, destination } = dropResult; const droppedOnRoot = destination && destination.id === DASHBOARD_ROOT_ID; const isNewComponent = source.id === NEW_COMPONENTS_SOURCE_ID; + const dashboardRoot = getState().dashboardLayout.present[DASHBOARD_ROOT_ID]; + const rootChildId = + dashboardRoot && dashboardRoot.children ? dashboardRoot.children[0] : ''; if (droppedOnRoot) { dispatch(createTopLevelTabs(dropResult)); } else if (destination && isNewComponent) { dispatch(createComponent(dropResult)); } else if ( + // Add additional allow-to-drop logic for tag/tags source. + // We only allow + // - top-level tab => top-level tab: rearrange top-level tab order + // - nested tab => top-level tab: allow row tab become top-level tab + // Dashboard does not allow top-level tab become nested tab, to avoid + // nested tab inside nested tab. + source.type === TABS_TYPE && + destination.type === TABS_TYPE && + source.id === rootChildId && + destination.id !== rootChildId + ) { + return dispatch( + addWarningToast(t(`Can not move top level tab into nested tabs`)), + ); + } else if ( destination && source && !// ensure it has moved @@ -176,6 +197,8 @@ export function handleComponentDrop(dropResult) { dispatch(moveComponent(dropResult)); } + // call getState() again down here in case redux state is stale after + // previous dispatch(es) const { dashboardLayout: undoableLayout } = getState(); // if we moved a child from a Tab or Row parent and it was the only child, delete the parent. diff --git a/superset/assets/src/dashboard/components/DashboardBuilder.jsx b/superset/assets/src/dashboard/components/DashboardBuilder.jsx index 12c8ff3..4a39955 100644 --- a/superset/assets/src/dashboard/components/DashboardBuilder.jsx +++ b/superset/assets/src/dashboard/components/DashboardBuilder.jsx @@ -84,10 +84,11 @@ class DashboardBuilder extends React.Component { const { dashboardLayout, directPathToChild } = props; const dashboardRoot = dashboardLayout[DASHBOARD_ROOT_ID]; const rootChildId = dashboardRoot.children[0]; - const topLevelTabs = - rootChildId !== DASHBOARD_GRID_ID && dashboardLayout[rootChildId]; const tabIndex = findTabIndexByComponentId({ - currentComponent: topLevelTabs || dashboardLayout[DASHBOARD_ROOT_ID], + currentComponent: + rootChildId === DASHBOARD_GRID_ID + ? dashboardLayout[DASHBOARD_ROOT_ID] + : dashboardLayout[rootChildId], directPathToChild, }); diff --git a/superset/assets/src/dashboard/components/gridComponents/Tab.jsx b/superset/assets/src/dashboard/components/gridComponents/Tab.jsx index 49a0f18..d441c8e 100644 --- a/superset/assets/src/dashboard/components/gridComponents/Tab.jsx +++ b/superset/assets/src/dashboard/components/gridComponents/Tab.jsx @@ -26,7 +26,6 @@ import AnchorLink from '../../../components/AnchorLink'; import DeleteComponentModal from '../DeleteComponentModal'; import WithPopoverMenu from '../menu/WithPopoverMenu'; import { componentShape } from '../../util/propShapes'; -import { DASHBOARD_ROOT_DEPTH } from '../../util/constants'; export const RENDER_TAB = 'RENDER_TAB'; export const RENDER_TAB_CONTENT = 'RENDER_TAB_CONTENT'; @@ -219,10 +218,6 @@ export default class Tab extends React.PureComponent { index={index} depth={depth} onDrop={this.handleDrop} - // disable drag drop of top-level Tab's to prevent invalid nesting of a child in - // itself, e.g. if a top-level Tab has a Tabs child, dragging the Tab into the Tabs would - // reusult in circular children - disableDragDrop={depth <= DASHBOARD_ROOT_DEPTH + 1} editMode={editMode} > {({ dropIndicatorProps, dragSourceRef }) => (