You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by vi...@apache.org on 2021/03/15 10:48:18 UTC

[superset] branch master updated: feat(explore): Drag and drop UX improvements (#13598)

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

villebro 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 ae66f5f  feat(explore): Drag and drop UX improvements (#13598)
ae66f5f is described below

commit ae66f5fa782155392c3b15f06ae5ba2c5cbee063
Author: Kamil Gabryjelski <ka...@gmail.com>
AuthorDate: Mon Mar 15 11:47:13 2021 +0100

    feat(explore): Drag and drop UX improvements (#13598)
    
    * Create a metric immediately when saved metric is dropped
    
    * Display borders around control boxes when metric or column is dragged
    
    * Fix imports
    
    * Display ghost button
    
    * Rename placeholder to ghostButton
---
 .../src/explore/components/OptionControls.tsx      |  2 +-
 .../DndColumnSelectControl/DndColumnSelect.tsx     |  1 -
 .../DndColumnSelectControl/DndFilterSelect.tsx     |  7 ++--
 .../DndColumnSelectControl/DndMetricSelect.tsx     | 46 ++++++++++------------
 .../DndColumnSelectControl/DndSelectLabel.tsx      | 15 +++----
 .../controls/DndColumnSelectControl/types.ts       |  7 ++--
 6 files changed, 36 insertions(+), 42 deletions(-)

diff --git a/superset-frontend/src/explore/components/OptionControls.tsx b/superset-frontend/src/explore/components/OptionControls.tsx
index 0e8f777..6a3b66e 100644
--- a/superset-frontend/src/explore/components/OptionControls.tsx
+++ b/superset-frontend/src/explore/components/OptionControls.tsx
@@ -99,7 +99,7 @@ export const DndLabelsContainer = styled.div<{
 }>`
   padding: ${({ theme }) => theme.gridUnit}px;
   border: ${({ canDrop, isOver, theme }) => {
-    if (isOver && canDrop) {
+    if (canDrop) {
       return `dashed 1px ${theme.colors.info.dark1}`;
     }
     if (isOver && !canDrop) {
diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelect.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelect.tsx
index 130f44b..67c8d61 100644
--- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelect.tsx
+++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndColumnSelect.tsx
@@ -72,7 +72,6 @@ export const DndColumnSelect = (props: LabelProps) => {
 
   return (
     <DndSelectLabel<string | string[], ColumnMeta[]>
-      values={values}
       onDrop={onDrop}
       canDrop={canDrop}
       valuesRenderer={valuesRenderer}
diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndFilterSelect.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndFilterSelect.tsx
index e835f99..9e94d0a 100644
--- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndFilterSelect.tsx
+++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndFilterSelect.tsx
@@ -17,8 +17,8 @@
  * under the License.
  */
 import React, { useEffect, useMemo, useState } from 'react';
-import { logging, SupersetClient, t } from '@superset-ui/core';
-import { ColumnMeta, Metric } from '@superset-ui/chart-controls';
+import { logging, SupersetClient, t, Metric } from '@superset-ui/core';
+import { ColumnMeta } from '@superset-ui/chart-controls';
 import { Tooltip } from 'src/common/components/Tooltip';
 import { OPERATORS } from 'src/explore/constants';
 import { OptionSortType } from 'src/explore/types';
@@ -300,7 +300,6 @@ export const DndFilterSelect = (props: DndFilterSelectProps) => {
   return (
     <>
       <DndSelectLabel<OptionValueType, OptionValueType[]>
-        values={values}
         onDrop={(item: DatasourcePanelDndItem) => {
           setDroppedItem(item.value);
           togglePopover(true);
@@ -313,7 +312,7 @@ export const DndFilterSelect = (props: DndFilterSelectProps) => {
           DndItemType.MetricOption,
           DndItemType.AdhocMetricOption,
         ]}
-        placeholderText={t('Drop columns or metrics')}
+        ghostButtonText={t('Drop columns or metrics')}
         {...props}
       />
       <AdhocFilterPopoverTrigger
diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndMetricSelect.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndMetricSelect.tsx
index 7fe1b4b..eb55faa 100644
--- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndMetricSelect.tsx
+++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndMetricSelect.tsx
@@ -241,41 +241,35 @@ export const DndMetricSelect = (props: any) => {
     togglePopover(false);
   };
 
-  const { savedMetric, adhocMetric } = useMemo(() => {
-    if (droppedItem?.type === 'column') {
-      const itemValue = droppedItem?.value as ColumnMeta;
-      return {
-        savedMetric: {} as savedMetricType,
-        adhocMetric: new AdhocMetric({
-          column: { column_name: itemValue?.column_name },
-        }),
-      };
+  const handleDrop = (item: DatasourcePanelDndItem) => {
+    if (item.type === DndItemType.Metric) {
+      onNewMetric(item.value as Metric);
+    }
+    if (item.type === DndItemType.Column) {
+      setDroppedItem(item);
+      togglePopover(true);
     }
-    if (droppedItem?.type === 'metric') {
-      const itemValue = droppedItem?.value as savedMetricType;
-      return {
-        savedMetric: itemValue,
-        adhocMetric: new AdhocMetric({ isNew: true }),
-      };
+  };
+
+  const adhocMetric = useMemo(() => {
+    if (droppedItem?.type === DndItemType.Column) {
+      const itemValue = droppedItem?.value as ColumnMeta;
+      return new AdhocMetric({
+        column: { column_name: itemValue?.column_name },
+      });
     }
-    return {
-      savedMetric: {} as savedMetricType,
-      adhocMetric: new AdhocMetric({ isNew: true }),
-    };
+    return new AdhocMetric({ isNew: true });
   }, [droppedItem?.type, droppedItem?.value]);
 
   return (
     <div className="metrics-select">
       <DndSelectLabel<OptionValueType, OptionValueType[]>
-        values={value}
-        onDrop={(item: DatasourcePanelDndItem) => {
-          setDroppedItem(item);
-          togglePopover(true);
-        }}
+        onDrop={handleDrop}
         canDrop={canDrop}
         valuesRenderer={valuesRenderer}
         accept={[DndItemType.Column, DndItemType.Metric]}
-        placeholderText={t('Drop columns or metrics')}
+        ghostButtonText={t('Drop columns or metrics')}
+        displayGhostButton={multi || value.length === 0}
         {...props}
       />
       <AdhocMetricPopoverTrigger
@@ -286,7 +280,7 @@ export const DndMetricSelect = (props: any) => {
           props.savedMetrics,
           props.value,
         )}
-        savedMetric={savedMetric}
+        savedMetric={{} as savedMetricType}
         datasourceType={props.datasourceType}
         isControlledComponent
         visible={newMetricPopoverVisible}
diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndSelectLabel.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndSelectLabel.tsx
index f0ee950..c316d5c 100644
--- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndSelectLabel.tsx
+++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndSelectLabel.tsx
@@ -18,7 +18,6 @@
  */
 import React from 'react';
 import { useDrop } from 'react-dnd';
-import { isEmpty } from 'lodash';
 import { t, useTheme } from '@superset-ui/core';
 import ControlHeader from 'src/explore/components/ControlHeader';
 import {
@@ -30,9 +29,10 @@ import { DatasourcePanelDndItem } from 'src/explore/components/DatasourcePanel/t
 import Icon from 'src/components/Icon';
 import { DndColumnSelectProps } from './types';
 
-export default function DndSelectLabel<T, O>(
-  props: DndColumnSelectProps<T, O>,
-) {
+export default function DndSelectLabel<T, O>({
+  displayGhostButton = true,
+  ...props
+}: DndColumnSelectProps<T, O>) {
   const theme = useTheme();
 
   const [{ isOver, canDrop }, datasourcePanelDrop] = useDrop({
@@ -51,11 +51,11 @@ export default function DndSelectLabel<T, O>(
     }),
   });
 
-  function renderPlaceHolder() {
+  function renderGhostButton() {
     return (
       <AddControlLabel cancelHover>
         <Icon name="plus-small" color={theme.colors.grayscale.light1} />
-        {t(props.placeholderText || 'Drop columns')}
+        {t(props.ghostButtonText || 'Drop columns')}
       </AddControlLabel>
     );
   }
@@ -66,7 +66,8 @@ export default function DndSelectLabel<T, O>(
         <ControlHeader {...props} />
       </HeaderContainer>
       <DndLabelsContainer canDrop={canDrop} isOver={isOver}>
-        {isEmpty(props.values) ? renderPlaceHolder() : props.valuesRenderer()}
+        {props.valuesRenderer()}
+        {displayGhostButton && renderGhostButton()}
       </DndLabelsContainer>
     </div>
   );
diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/types.ts b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/types.ts
index 68f830f..a34bda5 100644
--- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/types.ts
+++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/types.ts
@@ -17,7 +17,8 @@
  * under the License.
  */
 import { ReactNode } from 'react';
-import { ColumnMeta, Metric } from '@superset-ui/chart-controls';
+import { Metric } from '@superset-ui/core';
+import { ColumnMeta } from '@superset-ui/chart-controls';
 import { DatasourcePanelDndItem } from '../../DatasourcePanel/types';
 import { DndItemType } from '../../DndItemType';
 
@@ -45,12 +46,12 @@ export interface DndColumnSelectProps<
   T = string[] | string,
   O = string[] | string
 > extends LabelProps<T> {
-  values?: O;
   onDrop: (item: DatasourcePanelDndItem) => void;
   canDrop: (item: DatasourcePanelDndItem) => boolean;
   valuesRenderer: () => ReactNode;
   accept: DndItemType | DndItemType[];
-  placeholderText?: string;
+  ghostButtonText?: string;
+  displayGhostButton?: boolean;
 }
 
 export type OptionValueType = Record<string, any>;