You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by be...@apache.org on 2020/12/16 21:09:01 UTC

[incubator-superset] branch master updated: fix: improve import flow UI/UX (#12070)

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

beto 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 69185ee  fix: improve import flow UI/UX (#12070)
69185ee is described below

commit 69185eed6781b04bb72c6e90f09691f5f1840111
Author: Beto Dealmeida <ro...@dealmeida.net>
AuthorDate: Wed Dec 16 13:08:39 2020 -0800

    fix: improve import flow UI/UX (#12070)
    
    * fix: improve import flow UI/UX
    
    * Reset modal after import
    
    * Adjust margin before overwrite text
---
 .../src/common/components/Modal/Modal.tsx          |  2 +
 .../components/ImportModal/ImportModal.test.tsx    |  3 +-
 .../src/components/ImportModal/index.tsx           | 58 ++++++++++------------
 superset-frontend/src/components/Menu/SubMenu.tsx  |  4 ++
 .../src/views/CRUD/chart/ChartList.tsx             | 14 +++---
 .../src/views/CRUD/dashboard/DashboardList.tsx     | 14 +++---
 .../src/views/CRUD/data/database/DatabaseList.tsx  | 14 +++---
 .../src/views/CRUD/data/dataset/DatasetList.tsx    | 14 +++---
 8 files changed, 61 insertions(+), 62 deletions(-)

diff --git a/superset-frontend/src/common/components/Modal/Modal.tsx b/superset-frontend/src/common/components/Modal/Modal.tsx
index a11e042..fca500b 100644
--- a/superset-frontend/src/common/components/Modal/Modal.tsx
+++ b/superset-frontend/src/common/components/Modal/Modal.tsx
@@ -61,6 +61,8 @@ const StyledModal = styled(BaseModal)<StyledModalProps>`
     background-color: ${({ theme }) => theme.colors.grayscale.light4};
     border-radius: ${({ theme }) => theme.borderRadius}px
       ${({ theme }) => theme.borderRadius}px 0 0;
+    padding-left: ${({ theme }) => theme.gridUnit * 4}px;
+    padding-right: ${({ theme }) => theme.gridUnit * 4}px;
 
     .ant-modal-title h4 {
       display: flex;
diff --git a/superset-frontend/src/components/ImportModal/ImportModal.test.tsx b/superset-frontend/src/components/ImportModal/ImportModal.test.tsx
index ebae34c..a7a1cf5 100644
--- a/superset-frontend/src/components/ImportModal/ImportModal.test.tsx
+++ b/superset-frontend/src/components/ImportModal/ImportModal.test.tsx
@@ -23,7 +23,7 @@ import { styledMount as mount } from 'spec/helpers/theming';
 import { ReactWrapper } from 'enzyme';
 
 import { ImportResourceName } from 'src/views/CRUD/types';
-import ImportModelsModal, { StyledIcon } from 'src/components/ImportModal';
+import ImportModelsModal from 'src/components/ImportModal';
 import Modal from 'src/common/components/Modal';
 
 const mockStore = configureStore([thunk]);
@@ -32,7 +32,6 @@ const store = mockStore({});
 const requiredProps = {
   resourceName: 'database' as ImportResourceName,
   resourceLabel: 'database',
-  icon: <StyledIcon name="database" />,
   passwordsNeededMessage: 'Passwords are needed',
   confirmOverwriteMessage: 'Database exists',
   addDangerToast: () => {},
diff --git a/superset-frontend/src/components/ImportModal/index.tsx b/superset-frontend/src/components/ImportModal/index.tsx
index 1de3f75..a8d3db4 100644
--- a/superset-frontend/src/components/ImportModal/index.tsx
+++ b/superset-frontend/src/components/ImportModal/index.tsx
@@ -20,7 +20,7 @@ import React, { FunctionComponent, useEffect, useRef, useState } from 'react';
 import { styled, t } from '@superset-ui/core';
 
 import Icon from 'src//components/Icon';
-import Modal from 'src/common/components/Modal';
+import StyledModal from 'src/common/components/Modal';
 import { useImportResource } from 'src/views/CRUD/hooks';
 import { ImportResourceName } from 'src/views/CRUD/types';
 
@@ -28,24 +28,26 @@ export const StyledIcon = styled(Icon)`
   margin: auto ${({ theme }) => theme.gridUnit * 2}px auto 0;
 `;
 
+const HelperMessage = styled.div`
+  display: block;
+  color: ${({ theme }) => theme.colors.grayscale.base};
+  font-size: ${({ theme }) => theme.typography.sizes.s - 1}px;
+`;
+
 const StyledInputContainer = styled.div`
-  margin-bottom: ${({ theme }) => theme.gridUnit * 2}px;
+  padding-bottom: ${({ theme }) => theme.gridUnit * 2}px;
+  padding-top: ${({ theme }) => theme.gridUnit * 2}px;
+
+  & > div {
+    margin: ${({ theme }) => theme.gridUnit}px 0;
+  }
 
   &.extra-container {
     padding-top: 8px;
   }
 
-  .helper {
-    display: block;
-    padding: ${({ theme }) => theme.gridUnit}px 0;
-    color: ${({ theme }) => theme.colors.grayscale.base};
-    font-size: ${({ theme }) => theme.typography.sizes.s - 1}px;
-    text-align: left;
-
-    .required {
-      margin-left: ${({ theme }) => theme.gridUnit / 2}px;
-      color: ${({ theme }) => theme.colors.error.base};
-    }
+  .confirm-overwrite {
+    margin-bottom: ${({ theme }) => theme.gridUnit * 2}px;
   }
 
   .input-container {
@@ -100,7 +102,6 @@ const StyledInputContainer = styled.div`
 export interface ImportModelsModalProps {
   resourceName: ImportResourceName;
   resourceLabel: string;
-  icon: React.ReactNode;
   passwordsNeededMessage: string;
   confirmOverwriteMessage: string;
   addDangerToast: (msg: string) => void;
@@ -115,7 +116,6 @@ export interface ImportModelsModalProps {
 const ImportModelsModal: FunctionComponent<ImportModelsModalProps> = ({
   resourceName,
   resourceLabel,
-  icon,
   passwordsNeededMessage,
   confirmOverwriteMessage,
   addDangerToast,
@@ -140,6 +140,8 @@ const ImportModelsModal: FunctionComponent<ImportModelsModalProps> = ({
     setUploadFile(null);
     setPasswordFields([]);
     setPasswords({});
+    setNeedsOverwriteConfirm(false);
+    setConfirmedOverwrite(false);
     if (fileInputRef && fileInputRef.current) {
       fileInputRef.current.value = '';
     }
@@ -161,12 +163,13 @@ const ImportModelsModal: FunctionComponent<ImportModelsModalProps> = ({
 
   useEffect(() => {
     setNeedsOverwriteConfirm(alreadyExists.length > 0);
-  }, [alreadyExists]);
+  }, [alreadyExists, setNeedsOverwriteConfirm]);
 
   // Functions
   const hide = () => {
     setIsHidden(true);
     onHide();
+    clearModal();
   };
 
   const onUpload = () => {
@@ -201,9 +204,7 @@ const ImportModelsModal: FunctionComponent<ImportModelsModalProps> = ({
     return (
       <>
         <h5>Database passwords</h5>
-        <StyledInputContainer>
-          <div className="helper">{passwordsNeededMessage}</div>
-        </StyledInputContainer>
+        <HelperMessage>{passwordsNeededMessage}</HelperMessage>
         {passwordFields.map(fileName => (
           <StyledInputContainer key={`password-for-${fileName}`}>
             <div className="control-label">
@@ -226,18 +227,16 @@ const ImportModelsModal: FunctionComponent<ImportModelsModalProps> = ({
   };
 
   const renderOverwriteConfirmation = () => {
-    if (alreadyExists.length === 0) {
+    if (!needsOverwriteConfirm) {
       return null;
     }
 
     return (
       <>
         <StyledInputContainer>
-          <div>{confirmOverwriteMessage}</div>
+          <div className="confirm-overwrite">{confirmOverwriteMessage}</div>
           <div className="control-label">
-            <label htmlFor="overwrite">
-              {t('Type "%s" to confirm', t('OVERWRITE'))}
-            </label>
+            {t('Type "%s" to confirm', t('OVERWRITE'))}
           </div>
           <input
             data-test="overwrite-modal-input"
@@ -256,7 +255,7 @@ const ImportModelsModal: FunctionComponent<ImportModelsModalProps> = ({
   }
 
   return (
-    <Modal
+    <StyledModal
       name="model"
       className="import-model-modal"
       disablePrimaryButton={
@@ -268,12 +267,7 @@ const ImportModelsModal: FunctionComponent<ImportModelsModalProps> = ({
       primaryButtonType={needsOverwriteConfirm ? 'danger' : 'primary'}
       width="750px"
       show={show}
-      title={
-        <h4>
-          {icon}
-          {t('Import %s', resourceLabel)}
-        </h4>
-      }
+      title={<h4>{t('Import %s', resourceLabel)}</h4>}
     >
       <StyledInputContainer>
         <div className="control-label">
@@ -294,7 +288,7 @@ const ImportModelsModal: FunctionComponent<ImportModelsModalProps> = ({
       </StyledInputContainer>
       {renderPasswordFields()}
       {renderOverwriteConfirmation()}
-    </Modal>
+    </StyledModal>
   );
 };
 
diff --git a/superset-frontend/src/components/Menu/SubMenu.tsx b/superset-frontend/src/components/Menu/SubMenu.tsx
index 40d1140..e12c5c4 100644
--- a/superset-frontend/src/components/Menu/SubMenu.tsx
+++ b/superset-frontend/src/components/Menu/SubMenu.tsx
@@ -76,6 +76,10 @@ const StyledHeader = styled.header`
       }
     }
   }
+
+  .btn-link {
+    padding: 10px 0;
+  }
 `;
 
 type MenuChild = {
diff --git a/superset-frontend/src/views/CRUD/chart/ChartList.tsx b/superset-frontend/src/views/CRUD/chart/ChartList.tsx
index 79bb22c..13d2740 100644
--- a/superset-frontend/src/views/CRUD/chart/ChartList.tsx
+++ b/superset-frontend/src/views/CRUD/chart/ChartList.tsx
@@ -43,9 +43,7 @@ import ListView, {
 } from 'src/components/ListView';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
 import PropertiesModal from 'src/explore/components/PropertiesModal';
-import ImportModelsModal, {
-  StyledIcon,
-} from 'src/components/ImportModal/index';
+import ImportModelsModal from 'src/components/ImportModal/index';
 import Chart from 'src/types/Chart';
 import TooltipWrapper from 'src/components/TooltipWrapper';
 import ChartCard from './ChartCard';
@@ -58,6 +56,11 @@ const PASSWORDS_NEEDED_MESSAGE = t(
     'the database configuration are not present in export files, and ' +
     'should be added manually after the import if they are needed.',
 );
+const CONFIRM_OVERWRITE_MESSAGE = t(
+  'You are importing one or more charts that already exist. ' +
+    'Overwriting might cause you to lose some of your work. Are you ' +
+    'sure you want to overwrite?',
+);
 
 const createFetchDatasets = (handleError: (err: Response) => void) => async (
   filterValue = '',
@@ -580,11 +583,8 @@ function ChartList(props: ChartListProps) {
       <ImportModelsModal
         resourceName="chart"
         resourceLabel={t('chart')}
-        icon={<StyledIcon name="nav-charts" />}
         passwordsNeededMessage={PASSWORDS_NEEDED_MESSAGE}
-        confirmOverwriteMessage={t(
-          'One or more charts to be imported already exist.',
-        )}
+        confirmOverwriteMessage={CONFIRM_OVERWRITE_MESSAGE}
         addDangerToast={addDangerToast}
         addSuccessToast={addSuccessToast}
         onModelImport={handleChartImport}
diff --git a/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx b/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx
index 6254c0b..7e20f8e 100644
--- a/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx
+++ b/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx
@@ -37,9 +37,7 @@ import Icon from 'src/components/Icon';
 import FaveStar from 'src/components/FaveStar';
 import PropertiesModal from 'src/dashboard/components/PropertiesModal';
 import TooltipWrapper from 'src/components/TooltipWrapper';
-import ImportModelsModal, {
-  StyledIcon,
-} from 'src/components/ImportModal/index';
+import ImportModelsModal from 'src/components/ImportModal/index';
 
 import Dashboard from 'src/dashboard/containers/Dashboard';
 import DashboardCard from './DashboardCard';
@@ -52,6 +50,11 @@ const PASSWORDS_NEEDED_MESSAGE = t(
     'the database configuration are not present in export files, and ' +
     'should be added manually after the import if they are needed.',
 );
+const CONFIRM_OVERWRITE_MESSAGE = t(
+  'You are importing one or more dashboards that already exist. ' +
+    'Overwriting might cause you to lose some of your work. Are you ' +
+    'sure you want to overwrite?',
+);
 
 interface DashboardListProps {
   addDangerToast: (msg: string) => void;
@@ -541,11 +544,8 @@ function DashboardList(props: DashboardListProps) {
       <ImportModelsModal
         resourceName="dashboard"
         resourceLabel={t('dashboard')}
-        icon={<StyledIcon name="nav-dashboard" />}
         passwordsNeededMessage={PASSWORDS_NEEDED_MESSAGE}
-        confirmOverwriteMessage={t(
-          'One or more dashboards to be imported already exist.',
-        )}
+        confirmOverwriteMessage={CONFIRM_OVERWRITE_MESSAGE}
         addDangerToast={addDangerToast}
         addSuccessToast={addSuccessToast}
         onModelImport={handleDashboardImport}
diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx
index fea9ad4..13ff1bb 100644
--- a/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx
+++ b/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx
@@ -29,9 +29,7 @@ import TooltipWrapper from 'src/components/TooltipWrapper';
 import Icon from 'src/components/Icon';
 import ListView, { Filters } from 'src/components/ListView';
 import { commonMenuData } from 'src/views/CRUD/data/common';
-import ImportModelsModal, {
-  StyledIcon,
-} from 'src/components/ImportModal/index';
+import ImportModelsModal from 'src/components/ImportModal/index';
 import DatabaseModal from './DatabaseModal';
 import { DatabaseObject } from './types';
 
@@ -42,6 +40,11 @@ const PASSWORDS_NEEDED_MESSAGE = t(
     'sections of the database configuration are not present in export ' +
     'files, and should be added manually after the import if they are needed.',
 );
+const CONFIRM_OVERWRITE_MESSAGE = t(
+  'You are importing one or more databases that already exist. ' +
+    'Overwriting might cause you to lose some of your work. Are you ' +
+    'sure you want to overwrite?',
+);
 
 interface DatabaseDeleteObject extends DatabaseObject {
   chart_count: number;
@@ -436,11 +439,8 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) {
       <ImportModelsModal
         resourceName="database"
         resourceLabel={t('database')}
-        icon={<StyledIcon name="database" />}
         passwordsNeededMessage={PASSWORDS_NEEDED_MESSAGE}
-        confirmOverwriteMessage={t(
-          'One or more databases to be imported already exist.',
-        )}
+        confirmOverwriteMessage={CONFIRM_OVERWRITE_MESSAGE}
         addDangerToast={addDangerToast}
         addSuccessToast={addSuccessToast}
         onModelImport={handleDatabaseImport}
diff --git a/superset-frontend/src/views/CRUD/data/dataset/DatasetList.tsx b/superset-frontend/src/views/CRUD/data/dataset/DatasetList.tsx
index 752fb44..adb58d7 100644
--- a/superset-frontend/src/views/CRUD/data/dataset/DatasetList.tsx
+++ b/superset-frontend/src/views/CRUD/data/dataset/DatasetList.tsx
@@ -45,9 +45,7 @@ import TooltipWrapper from 'src/components/TooltipWrapper';
 import Icon from 'src/components/Icon';
 import FacePile from 'src/components/FacePile';
 import CertifiedIconWithTooltip from 'src/components/CertifiedIconWithTooltip';
-import ImportModelsModal, {
-  StyledIcon,
-} from 'src/components/ImportModal/index';
+import ImportModelsModal from 'src/components/ImportModal/index';
 import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
 import AddDatasetModal from './AddDatasetModal';
 
@@ -59,6 +57,11 @@ const PASSWORDS_NEEDED_MESSAGE = t(
     'the database configuration are not present in export files, and ' +
     'should be added manually after the import if they are needed.',
 );
+const CONFIRM_OVERWRITE_MESSAGE = t(
+  'You are importing one or more datasets that already exist. ' +
+    'Overwriting might cause you to lose some of your work. Are you ' +
+    'sure you want to overwrite?',
+);
 
 const FlexRowContainer = styled.div`
   align-items: center;
@@ -659,11 +662,8 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
       <ImportModelsModal
         resourceName="dataset"
         resourceLabel={t('dataset')}
-        icon={<StyledIcon name="table" />}
         passwordsNeededMessage={PASSWORDS_NEEDED_MESSAGE}
-        confirmOverwriteMessage={t(
-          'One or more datasets to be imported already exist.',
-        )}
+        confirmOverwriteMessage={CONFIRM_OVERWRITE_MESSAGE}
         addDangerToast={addDangerToast}
         addSuccessToast={addSuccessToast}
         onModelImport={handleDatasetImport}