You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ju...@apache.org on 2023/10/13 14:06:03 UTC

[superset] branch sqllab-custom-result-table updated: pr 25569

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

justinpark pushed a commit to branch sqllab-custom-result-table
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/sqllab-custom-result-table by this push:
     new c19e152a41 pr 25569
c19e152a41 is described below

commit c19e152a41856effef4c95169c824532316c34ab
Author: justinpark <ju...@gmail.com>
AuthorDate: Fri Oct 13 10:05:46 2023 -0400

    pr 25569
---
 .../FiltersConfigForm/DatasetLabel.tsx             | 116 +++++++++++++++++++++
 .../FiltersConfigForm/DatasetSelect.tsx            |  37 +++++--
 .../FiltersConfigForm/FiltersConfigForm.tsx        |   3 +-
 .../FiltersConfigModal/FiltersConfigForm/utils.ts  |  12 ---
 .../src/pages/ChartCreation/index.tsx              |  86 +++++++++++----
 5 files changed, 213 insertions(+), 41 deletions(-)

diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/DatasetLabel.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/DatasetLabel.tsx
new file mode 100644
index 0000000000..fed323881d
--- /dev/null
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/DatasetLabel.tsx
@@ -0,0 +1,116 @@
+/**
+ * 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 { Tooltip } from 'src/components/Tooltip';
+import { styled } from '@superset-ui/core';
+
+type Database = {
+  database_name: string;
+};
+
+type Dataset = {
+  id: number;
+  table_name: string;
+  schema: string;
+  database: Database;
+};
+
+const TooltipContent = styled.div`
+  ${({ theme }) => `
+    .tooltip-header {
+      font-size: ${theme.typography.sizes.m}px;
+      font-weight: ${theme.typography.weights.bold};
+    }
+
+    .tooltip-description {
+      margin-top: ${theme.gridUnit * 2}px;
+      display: -webkit-box;
+      -webkit-line-clamp: 20;
+      -webkit-box-orient: vertical;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+  `}
+`;
+
+const StyledLabel = styled.span`
+  ${({ theme }) => `
+    left: ${theme.gridUnit * 3}px;
+    right: ${theme.gridUnit * 3}px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    display: block;
+  `}
+`;
+
+const StyledLabelDetail = styled.span`
+  ${({
+    theme: {
+      typography: { sizes, weights },
+    },
+  }) => `
+    overflow: hidden;
+    display: inline-block;
+    text-overflow: ellipsis;
+    font-size: ${sizes.s}px;
+    font-weight: ${weights.light};
+    max-width: 50%;
+    line-height: 1.4;
+  `}
+`;
+
+const StyledLabelContainer = styled.div`
+  ${({ theme }) => `
+    left: ${theme.gridUnit * 3}px;
+    right: ${theme.gridUnit * 3}px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    display: block;
+  `}
+`;
+
+export const newLabel = (item: Dataset | Record<string, any>) => (
+  <Tooltip
+    mouseEnterDelay={1}
+    placement="right"
+    title={
+      <TooltipContent>
+        <div className="tooltip-header">{item.table_name}</div>
+        <div className="tooltip-description">
+          <div>Database: {item.database.database_name}</div>
+          <div>
+            Schema:{' '}
+            {item.schema &&
+            !['null', 'none'].includes(item.schema.toLowerCase())
+              ? item.schema
+              : 'Not defined'}
+          </div>
+        </div>
+      </TooltipContent>
+    }
+  >
+    <StyledLabelContainer>
+      <StyledLabel>{item.table_name}</StyledLabel>
+      <StyledLabelDetail>{item.database.database_name}</StyledLabelDetail>
+      {item.schema && !['null', 'none'].includes(item.schema.toLowerCase()) && (
+        <StyledLabelDetail>&nbsp;- {item.schema}</StyledLabelDetail>
+      )}
+    </StyledLabelContainer>
+  </Tooltip>
+);
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/DatasetSelect.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/DatasetSelect.tsx
index e5e62b008e..c478b5eba5 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/DatasetSelect.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/DatasetSelect.tsx
@@ -16,16 +16,27 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React, { useCallback, useMemo } from 'react';
+import React, { useCallback, useMemo, ReactNode } from 'react';
 import rison from 'rison';
-import { t } from '@superset-ui/core';
+import { t, JsonResponse } from '@superset-ui/core';
 import { AsyncSelect } from 'src/components';
 import {
   ClientErrorObject,
   getClientErrorObject,
 } from 'src/utils/getClientErrorObject';
 import { cachedSupersetGet } from 'src/utils/cachedSupersetGet';
-import { datasetToSelectOption } from './utils';
+import { newLabel } from './DatasetLabel';
+
+type Database = {
+  database_name: string;
+};
+
+type Dataset = {
+  id: number;
+  table_name: string;
+  schema: string;
+  database: Database;
+};
 
 interface DatasetSelectProps {
   onChange: (value: { label: string; value: number }) => void;
@@ -49,24 +60,29 @@ const DatasetSelect = ({ onChange, value }: DatasetSelectProps) => {
     page: number,
     pageSize: number,
   ) => {
-    const searchColumn = 'table_name';
     const query = rison.encode({
-      filters: [{ col: searchColumn, opr: 'ct', value: search }],
+      columns: ['id', 'table_name', 'database.database_name', 'schema'],
+      filters: [{ col: 'table_name', opr: 'ct', value: search }],
       page,
       page_size: pageSize,
-      order_column: searchColumn,
+      order_column: 'table_name',
       order_direction: 'asc',
     });
     return cachedSupersetGet({
       endpoint: `/api/v1/dataset/?q=${query}`,
     })
-      .then(response => {
-        const data: {
+      .then((response: JsonResponse) => {
+        const list: {
+          customLabel: ReactNode;
           label: string;
           value: string | number;
-        }[] = response.json.result.map(datasetToSelectOption);
+        }[] = response.json.result.map((item: Dataset) => ({
+          customLabel: newLabel(item),
+          label: item.table_name,
+          value: item.id,
+        }));
         return {
-          data,
+          data: list,
           totalCount: response.json.count,
         };
       })
@@ -83,6 +99,7 @@ const DatasetSelect = ({ onChange, value }: DatasetSelectProps) => {
       options={loadDatasetOptions}
       onChange={onChange}
       notFoundContent={t('No compatible datasets found')}
+      placeholder={t('Select a dataset')}
     />
   );
 };
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
index 94b336af92..ac75896d69 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
@@ -103,6 +103,7 @@ import {
 } from './utils';
 import { FILTER_SUPPORTED_TYPES, INPUT_WIDTH } from './constants';
 import DependencyList from './DependencyList';
+import { newLabel } from './DatasetLabel';
 
 const TabPane = styled(Tabs.TabPane)`
   padding: ${({ theme }) => theme.gridUnit * 4}px 0px;
@@ -883,7 +884,7 @@ const FiltersConfigForm = (
                 initialValue={
                   datasetDetails
                     ? {
-                        label: datasetDetails.table_name,
+                        label: newLabel(datasetDetails),
                         value: datasetDetails.id,
                       }
                     : undefined
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/utils.ts b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/utils.ts
index 92185039d8..b7aa2cab39 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/utils.ts
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/utils.ts
@@ -66,18 +66,6 @@ export const getControlItems = (
     [],
   ) as CustomControlItem[]) ?? [];
 
-type DatasetSelectValue = {
-  value: number;
-  label: string;
-};
-
-export const datasetToSelectOption = (
-  item: Dataset & { table_name: string },
-): DatasetSelectValue => ({
-  value: item.id,
-  label: item.table_name,
-});
-
 // TODO: add column_types field to Dataset
 // We return true if column_types is undefined or empty as a precaution against backend failing to return column_types
 export const hasTemporalColumns = (
diff --git a/superset-frontend/src/pages/ChartCreation/index.tsx b/superset-frontend/src/pages/ChartCreation/index.tsx
index c09172e99d..9a7917c65c 100644
--- a/superset-frontend/src/pages/ChartCreation/index.tsx
+++ b/superset-frontend/src/pages/ChartCreation/index.tsx
@@ -43,11 +43,16 @@ import { findPermission } from 'src/utils/findPermission';
 import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
 import getBootstrapData from 'src/utils/getBootstrapData';
 
+type Database = {
+  database_name: string;
+};
+
 type Dataset = {
   id: number;
   table_name: string;
-  description: string;
   datasource_type: string;
+  schema: string;
+  database: Database;
 };
 
 export interface ChartCreationProps extends RouteComponentProps {
@@ -169,20 +174,18 @@ const StyledContainer = styled.div`
     &&&& .ant-select-selection-placeholder {
       padding-left: ${theme.gridUnit * 3}px;
     }
+
+    &&&& .ant-select-selection-item {
+      padding-left: ${theme.gridUnit * 3}px;
+    }
   `}
 `;
 
-const TooltipContent = styled.div<{ hasDescription: boolean }>`
-  ${({ theme, hasDescription }) => `
+const TooltipContent = styled.div`
+  ${({ theme }) => `
     .tooltip-header {
-      font-size: ${
-        hasDescription ? theme.typography.sizes.l : theme.typography.sizes.s
-      }px;
-      font-weight: ${
-        hasDescription
-          ? theme.typography.weights.bold
-          : theme.typography.weights.normal
-      };
+      font-size: ${theme.typography.sizes.m}px;
+      font-weight: ${theme.typography.weights.bold};
     }
 
     .tooltip-description {
@@ -198,7 +201,32 @@ const TooltipContent = styled.div<{ hasDescription: boolean }>`
 
 const StyledLabel = styled.span`
   ${({ theme }) => `
-    position: absolute;
+    display: block;
+    left: ${theme.gridUnit * 3}px;
+    right: ${theme.gridUnit * 3}px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    line-height: 1.4;
+  `}
+`;
+
+const StyledLabelDetail = styled.span`
+  ${({
+    theme: {
+      typography: { sizes, weights },
+    },
+  }) => `
+    overflow: hidden;
+    display: inline-block;
+    text-overflow: ellipsis;
+    font-size: ${sizes.s}px;
+    font-weight: ${weights.light};
+    max-width: 50%;
+  `}
+`;
+
+const StyledLabelContainer = styled.div`
+  ${({ theme }) => `
     left: ${theme.gridUnit * 3}px;
     right: ${theme.gridUnit * 3}px;
     overflow: hidden;
@@ -299,22 +327,44 @@ export class ChartCreation extends React.PureComponent<
         mouseEnterDelay={1}
         placement="right"
         title={
-          <TooltipContent hasDescription={!!item.description}>
+          <TooltipContent>
             <div className="tooltip-header">{item.table_name}</div>
-            {item.description && (
-              <div className="tooltip-description">{item.description}</div>
-            )}
+            <div className="tooltip-description">
+              <div>Database: {item.database.database_name}</div>
+              <div>
+                Schema:{' '}
+                {item.schema &&
+                !['null', 'none'].includes(item.schema.toLowerCase())
+                  ? item.schema
+                  : 'Not defined'}
+              </div>
+            </div>
           </TooltipContent>
         }
       >
-        <StyledLabel>{item.table_name}</StyledLabel>
+        <StyledLabelContainer>
+          <StyledLabel>{item.table_name}</StyledLabel>
+          <StyledLabel>
+            <StyledLabelDetail>{item.database.database_name}</StyledLabelDetail>
+            {item.schema &&
+              !['null', 'none'].includes(item.schema.toLowerCase()) && (
+                <StyledLabelDetail>&nbsp;- {item.schema}</StyledLabelDetail>
+              )}
+          </StyledLabel>
+        </StyledLabelContainer>
       </Tooltip>
     );
   }
 
   loadDatasources(search: string, page: number, pageSize: number) {
     const query = rison.encode({
-      columns: ['id', 'table_name', 'description', 'datasource_type'],
+      columns: [
+        'id',
+        'table_name',
+        'datasource_type',
+        'database.database_name',
+        'schema',
+      ],
       filters: [{ col: 'table_name', opr: 'ct', value: search }],
       page,
       page_size: pageSize,