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

justinpark 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 300ddaedf9 fix(dashboard): narrow empty drop area (#26313)
300ddaedf9 is described below

commit 300ddaedf97f582a8d73bc78e02ff3c434a4934f
Author: JUST.in DO IT <[email protected]>
AuthorDate: Thu Jan 4 09:44:23 2024 -0800

    fix(dashboard): narrow empty drop area (#26313)
---
 .../DashboardBuilder/DashboardBuilder.tsx          |  70 +----------
 .../DashboardBuilder/DashboardWrapper.test.tsx     |  75 ++++++++++++
 .../DashboardBuilder/DashboardWrapper.tsx          | 128 +++++++++++++++++++++
 .../src/dashboard/components/DashboardGrid.jsx     |  19 ++-
 .../src/dashboard/components/dnd/DragDroppable.jsx |  11 +-
 .../dashboard/components/gridComponents/Tab.jsx    |   8 +-
 6 files changed, 235 insertions(+), 76 deletions(-)

diff --git 
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
 
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
index a17b168374..51de15c7a1 100644
--- 
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
+++ 
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
@@ -86,72 +86,10 @@ import {
 import { getRootLevelTabsComponent, shouldFocusTabs } from './utils';
 import DashboardContainer from './DashboardContainer';
 import { useNativeFilters } from './state';
+import DashboardWrapper from './DashboardWrapper';
 
 type DashboardBuilderProps = {};
 
-const StyledDiv = styled.div`
-  ${({ theme }) => css`
-    display: grid;
-    grid-template-columns: auto 1fr;
-    grid-template-rows: auto 1fr;
-    flex: 1;
-    /* Special cases */
-
-    /* A row within a column has inset hover menu */
-    .dragdroppable-column .dragdroppable-row .hover-menu--left {
-      left: ${theme.gridUnit * -3}px;
-      background: ${theme.colors.grayscale.light5};
-      border: 1px solid ${theme.colors.grayscale.light2};
-    }
-
-    .dashboard-component-tabs {
-      position: relative;
-    }
-
-    /* A column within a column or tabs has inset hover menu */
-    .dragdroppable-column .dragdroppable-column .hover-menu--top,
-    .dashboard-component-tabs .dragdroppable-column .hover-menu--top {
-      top: ${theme.gridUnit * -3}px;
-      background: ${theme.colors.grayscale.light5};
-      border: 1px solid ${theme.colors.grayscale.light2};
-    }
-
-    /* move Tabs hover menu to top near actual Tabs */
-    .dashboard-component-tabs > .hover-menu-container > .hover-menu--left {
-      top: 0;
-      transform: unset;
-      background: transparent;
-    }
-
-    /* push Chart actions to upper right */
-    .dragdroppable-column .dashboard-component-chart-holder .hover-menu--top,
-    .dragdroppable .dashboard-component-header .hover-menu--top {
-      right: ${theme.gridUnit * 2}px;
-      top: ${theme.gridUnit * 2}px;
-      background: transparent;
-      border: none;
-      transform: unset;
-      left: unset;
-    }
-    div:hover > .hover-menu-container .hover-menu,
-    .hover-menu-container .hover-menu:hover {
-      opacity: 1;
-    }
-
-    p {
-      margin: 0 0 ${theme.gridUnit * 2}px 0;
-    }
-
-    i.danger {
-      color: ${theme.colors.error.base};
-    }
-
-    i.warning {
-      color: ${theme.colors.alert.base};
-    }
-  `}
-`;
-
 // @z-index-above-dashboard-charts + 1 = 11
 const FiltersPanel = styled.div<{ width: number; hidden: boolean }>`
   grid-column: 1;
@@ -317,7 +255,7 @@ const DashboardContentWrapper = styled.div`
         width: 100%;
       }
 
-      & > .empty-droptarget:first-child {
+      & > .empty-droptarget:first-child:not(.empty-droptarget--full) {
         height: ${theme.gridUnit * 4}px;
         top: -2px;
         z-index: 10;
@@ -640,7 +578,7 @@ const DashboardBuilder: FC<DashboardBuilderProps> = () => {
       : theme.gridUnit * 8;
 
   return (
-    <StyledDiv>
+    <DashboardWrapper>
       {showFilterBar && filterBarOrientation === FilterBarOrientation.VERTICAL 
&& (
         <>
           <ResizableSidebar
@@ -749,7 +687,7 @@ const DashboardBuilder: FC<DashboardBuilderProps> = () => {
           `}
         />
       )}
-    </StyledDiv>
+    </DashboardWrapper>
   );
 };
 
diff --git 
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.test.tsx
 
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.test.tsx
new file mode 100644
index 0000000000..fb913b4627
--- /dev/null
+++ 
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.test.tsx
@@ -0,0 +1,75 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React from 'react';
+import { fireEvent, render } from 'spec/helpers/testing-library';
+import { OptionControlLabel } from 
'src/explore/components/controls/OptionControls';
+
+import DashboardWrapper from './DashboardWrapper';
+
+test('should render children', () => {
+  const { getByTestId } = render(
+    <DashboardWrapper>
+      <div data-test="mock-children" />
+    </DashboardWrapper>,
+    { useRedux: true, useDnd: true },
+  );
+  expect(getByTestId('mock-children')).toBeInTheDocument();
+});
+
+test('should update the style on dragging state', () => {
+  const defaultProps = {
+    label: <span>Test label</span>,
+    tooltipTitle: 'This is a tooltip title',
+    onRemove: jest.fn(),
+    onMoveLabel: jest.fn(),
+    onDropLabel: jest.fn(),
+    type: 'test',
+    index: 0,
+  };
+  const { container, getByText } = render(
+    <DashboardWrapper>
+      <OptionControlLabel
+        {...defaultProps}
+        index={1}
+        label={<span>Label 1</span>}
+      />
+      <OptionControlLabel
+        {...defaultProps}
+        index={2}
+        label={<span>Label 2</span>}
+      />
+    </DashboardWrapper>,
+    {
+      useRedux: true,
+      useDnd: true,
+      initialState: {
+        dashboardState: {
+          editMode: true,
+        },
+      },
+    },
+  );
+  expect(
+    container.getElementsByClassName('dragdroppable--dragging'),
+  ).toHaveLength(0);
+  fireEvent.dragStart(getByText('Label 1'));
+  expect(
+    container.getElementsByClassName('dragdroppable--dragging'),
+  ).toHaveLength(1);
+});
diff --git 
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx
 
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx
new file mode 100644
index 0000000000..f39c7ed630
--- /dev/null
+++ 
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx
@@ -0,0 +1,128 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React, { useEffect } from 'react';
+import { css, styled } from '@superset-ui/core';
+import { RootState } from 'src/dashboard/types';
+import { useSelector } from 'react-redux';
+import { useDragDropManager } from 'react-dnd';
+import classNames from 'classnames';
+
+const StyledDiv = styled.div`
+  ${({ theme }) => css`
+    display: grid;
+    grid-template-columns: auto 1fr;
+    grid-template-rows: auto 1fr;
+    flex: 1;
+    /* Special cases */
+
+    &.dragdroppable--dragging
+      .dashboard-component-tabs-content
+      > .empty-droptarget.empty-droptarget--full {
+      height: 100%;
+    }
+
+    /* A row within a column has inset hover menu */
+    .dragdroppable-column .dragdroppable-row .hover-menu--left {
+      left: ${theme.gridUnit * -3}px;
+      background: ${theme.colors.grayscale.light5};
+      border: 1px solid ${theme.colors.grayscale.light2};
+    }
+
+    .dashboard-component-tabs {
+      position: relative;
+    }
+
+    /* A column within a column or tabs has inset hover menu */
+    .dragdroppable-column .dragdroppable-column .hover-menu--top,
+    .dashboard-component-tabs .dragdroppable-column .hover-menu--top {
+      top: ${theme.gridUnit * -3}px;
+      background: ${theme.colors.grayscale.light5};
+      border: 1px solid ${theme.colors.grayscale.light2};
+    }
+
+    /* move Tabs hover menu to top near actual Tabs */
+    .dashboard-component-tabs > .hover-menu-container > .hover-menu--left {
+      top: 0;
+      transform: unset;
+      background: transparent;
+    }
+
+    /* push Chart actions to upper right */
+    .dragdroppable-column .dashboard-component-chart-holder .hover-menu--top,
+    .dragdroppable .dashboard-component-header .hover-menu--top {
+      right: ${theme.gridUnit * 2}px;
+      top: ${theme.gridUnit * 2}px;
+      background: transparent;
+      border: none;
+      transform: unset;
+      left: unset;
+    }
+    div:hover > .hover-menu-container .hover-menu,
+    .hover-menu-container .hover-menu:hover {
+      opacity: 1;
+    }
+
+    p {
+      margin: 0 0 ${theme.gridUnit * 2}px 0;
+    }
+
+    i.danger {
+      color: ${theme.colors.error.base};
+    }
+
+    i.warning {
+      color: ${theme.colors.alert.base};
+    }
+  `}
+`;
+
+type Props = {};
+
+const DashboardWrapper: React.FC<Props> = ({ children }) => {
+  const editMode = useSelector<RootState, boolean>(
+    state => state.dashboardState.editMode,
+  );
+  const dragDropManager = useDragDropManager();
+  const [isDragged, setIsDragged] = React.useState(
+    dragDropManager.getMonitor().isDragging(),
+  );
+
+  useEffect(() => {
+    const monitor = dragDropManager.getMonitor();
+    const unsub = monitor.subscribeToStateChange(() => {
+      setIsDragged(monitor.isDragging());
+    });
+
+    return () => {
+      unsub();
+    };
+  }, [dragDropManager]);
+
+  return (
+    <StyledDiv
+      className={classNames({
+        'dragdroppable--dragging': editMode && isDragged,
+      })}
+    >
+      {children}
+    </StyledDiv>
+  );
+};
+
+export default DashboardWrapper;
diff --git a/superset-frontend/src/dashboard/components/DashboardGrid.jsx 
b/superset-frontend/src/dashboard/components/DashboardGrid.jsx
index 601dbac4a4..70cf65218f 100644
--- a/superset-frontend/src/dashboard/components/DashboardGrid.jsx
+++ b/superset-frontend/src/dashboard/components/DashboardGrid.jsx
@@ -18,6 +18,7 @@
  */
 import React from 'react';
 import PropTypes from 'prop-types';
+import classNames from 'classnames';
 import { addAlpha, css, styled, t } from '@superset-ui/core';
 import { EmptyStateBig } from 'src/components/EmptyState';
 import { componentShape } from '../util/propShapes';
@@ -76,10 +77,14 @@ const GridContent = styled.div`
     & > .empty-droptarget:first-child {
       height: ${theme.gridUnit * 12}px;
       margin-top: ${theme.gridUnit * -6}px;
-      margin-bottom: ${theme.gridUnit * -6}px;
     }
 
-    & > .empty-droptarget:only-child {
+    & > .empty-droptarget:last-child {
+      height: ${theme.gridUnit * 12}px;
+      margin-top: ${theme.gridUnit * -6}px;
+    }
+
+    & > .empty-droptarget.empty-droptarget--full:only-child {
       height: 80vh;
     }
   `}
@@ -270,10 +275,14 @@ class DashboardGrid extends React.PureComponent {
                 index={0}
                 orientation="column"
                 onDrop={this.handleTopDropTargetDrop}
-                className="empty-droptarget"
+                className={classNames({
+                  'empty-droptarget': true,
+                  'empty-droptarget--full':
+                    gridComponent?.children?.length === 0,
+                })}
                 editMode
               >
-                {renderDraggableContentBottom}
+                {renderDraggableContentTop}
               </DragDroppable>
             )}
             {gridComponent?.children?.map((id, index) => (
@@ -304,7 +313,7 @@ class DashboardGrid extends React.PureComponent {
                 className="empty-droptarget"
                 editMode
               >
-                {renderDraggableContentTop}
+                {renderDraggableContentBottom}
               </DragDroppable>
             )}
             {isResizing &&
diff --git a/superset-frontend/src/dashboard/components/dnd/DragDroppable.jsx 
b/superset-frontend/src/dashboard/components/dnd/DragDroppable.jsx
index 3bc9f4d299..6a49f98875 100644
--- a/superset-frontend/src/dashboard/components/dnd/DragDroppable.jsx
+++ b/superset-frontend/src/dashboard/components/dnd/DragDroppable.jsx
@@ -90,6 +90,11 @@ const DragDroppableStyles = styled.div`
       z-index: 10;
     }
 
+    &.empty-droptarget--full > .drop-indicator--top {
+      height: 100%;
+      opacity: 0.3;
+    }
+
     & {
       .drop-indicator {
         display: block;
@@ -99,7 +104,7 @@ const DragDroppableStyles = styled.div`
       }
 
       .drop-indicator--top {
-        top: 0;
+        top: ${-theme.gridUnit - 2}px;
         left: 0;
         height: ${theme.gridUnit}px;
         width: 100%;
@@ -107,7 +112,7 @@ const DragDroppableStyles = styled.div`
       }
 
       .drop-indicator--bottom {
-        top: 100%;
+        bottom: ${-theme.gridUnit - 2}px;
         left: 0;
         height: ${theme.gridUnit}px;
         width: 100%;
@@ -116,7 +121,7 @@ const DragDroppableStyles = styled.div`
 
       .drop-indicator--right {
         top: 0;
-        left: 100%;
+        left: calc(100% - ${theme.gridUnit}px);
         height: 100%;
         width: ${theme.gridUnit}px;
         min-height: ${theme.gridUnit * 4}px;
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
index 32ac77936c..d1d08176ba 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
@@ -18,6 +18,7 @@
  */
 import React from 'react';
 import PropTypes from 'prop-types';
+import classNames from 'classnames';
 import { bindActionCreators } from 'redux';
 import { connect } from 'react-redux';
 import { styled, t } from '@superset-ui/core';
@@ -173,7 +174,10 @@ class Tab extends React.PureComponent {
             depth={depth}
             onDrop={this.handleTopDropTargetDrop}
             editMode
-            className="empty-droptarget"
+            className={classNames({
+              'empty-droptarget': true,
+              'empty-droptarget--full': tabComponent.children.length === 0,
+            })}
           >
             {renderDraggableContentTop}
           </DragDroppable>
@@ -234,7 +238,7 @@ class Tab extends React.PureComponent {
           />
         ))}
         {/* Make bottom of tab droppable */}
-        {editMode && (
+        {editMode && tabComponent.children.length > 0 && (
           <DragDroppable
             component={tabComponent}
             parentComponent={tabParentComponent}

Reply via email to