You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2020/11/30 19:54:06 UTC

[incubator-superset] branch master updated: feat(welcome): add SQL snippets to saved queries card (#11678)

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

rusackas 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 0e9898c  feat(welcome): add SQL snippets to saved queries card (#11678)
0e9898c is described below

commit 0e9898cb96d1b16ac666e0f83433e02ba01dda18
Author: Phillip Kelley-Dotson <pk...@yahoo.com>
AuthorDate: Mon Nov 30 11:53:12 2020 -0800

    feat(welcome): add SQL snippets to saved queries card (#11678)
    
    * update savedqueries card to new layout
    
    * update card
    
    * update card to latest mock
    
    * update empty state
    
    * remove fallback
    
    * fix query statement
    
    * update card styles
    
    * remove double import
    
    * use fallbackurl prop for emptystate
    
    * update line lenth
    
    * Update superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
    
    Co-authored-by: Evan Rusackas <ev...@preset.io>
    
    * update styles and svg
    
    Co-authored-by: Evan Rusackas <ev...@preset.io>
---
 superset-frontend/images/empty-query.svg           |  25 +++++
 .../src/components/ListViewCard/index.tsx          |   4 +-
 .../src/views/CRUD/data/query/QueryList.tsx        |  15 +--
 superset-frontend/src/views/CRUD/utils.tsx         |  17 ++-
 .../src/views/CRUD/welcome/SavedQueries.tsx        | 120 +++++++++++++++------
 5 files changed, 131 insertions(+), 50 deletions(-)

diff --git a/superset-frontend/images/empty-query.svg b/superset-frontend/images/empty-query.svg
new file mode 100644
index 0000000..be72857
--- /dev/null
+++ b/superset-frontend/images/empty-query.svg
@@ -0,0 +1,25 @@
+<!--
+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.
+-->
+<svg width="458" height="146" viewBox="0 0 458 146" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M17 21C17 18.7909 18.7909 17 21 17H125C127.209 17 129 18.7909 129 21V29C129 31.2091 127.209 33 125 33H21C18.7909 33 17 31.2091 17 29V21Z" fill="#ECEEF2"/>
+<path d="M17 45C17 42.7909 18.7909 41 21 41H223C225.209 41 227 42.7909 227 45V53C227 55.2091 225.209 57 223 57H21C18.7909 57 17 55.2091 17 53V45Z" fill="#ECEEF2"/>
+<path d="M21 65C18.7909 65 17 66.7909 17 69V77C17 79.2091 18.7909 81 21 81H172C174.209 81 176 79.2091 176 77V69C176 66.7909 174.209 65 172 65H21Z" fill="#ECEEF2"/>
+<path d="M17 93C17 90.7909 18.7909 89 21 89H286C288.209 89 290 90.7909 290 93V101C290 103.209 288.209 105 286 105H21C18.7909 105 17 103.209 17 101V93Z" fill="#ECEEF2"/>
+<path d="M21 113C18.7909 113 17 114.791 17 117V125C17 127.209 18.7909 129 21 129H73C75.2091 129 77 127.209 77 125V117C77 114.791 75.2091 113 73 113H21Z" fill="#ECEEF2"/>
+</svg>
diff --git a/superset-frontend/src/components/ListViewCard/index.tsx b/superset-frontend/src/components/ListViewCard/index.tsx
index 26030e0..94f14a8 100644
--- a/superset-frontend/src/components/ListViewCard/index.tsx
+++ b/superset-frontend/src/components/ListViewCard/index.tsx
@@ -145,7 +145,7 @@ const SkeletonActions = styled(Skeleton.Button)`
 
 const paragraphConfig = { rows: 1, width: 150 };
 interface CardProps {
-  title: React.ReactNode;
+  title?: React.ReactNode;
   url?: string;
   imgURL?: string;
   imgFallbackURL?: string;
@@ -155,7 +155,7 @@ interface CardProps {
   titleRight?: React.ReactNode;
   coverLeft?: React.ReactNode;
   coverRight?: React.ReactNode;
-  actions: React.ReactNode | null;
+  actions?: React.ReactNode | null;
   rows?: number | string;
   avatar?: string;
   cover?: React.ReactNode | null;
diff --git a/superset-frontend/src/views/CRUD/data/query/QueryList.tsx b/superset-frontend/src/views/CRUD/data/query/QueryList.tsx
index 4ed20de..ed9329fd 100644
--- a/superset-frontend/src/views/CRUD/data/query/QueryList.tsx
+++ b/superset-frontend/src/views/CRUD/data/query/QueryList.tsx
@@ -20,7 +20,7 @@ import React, { useMemo, useState, useCallback } from 'react';
 import { SupersetClient, t, styled } from '@superset-ui/core';
 import moment from 'moment';
 
-import { createErrorHandler } from 'src/views/CRUD/utils';
+import { createErrorHandler, shortenSQL } from 'src/views/CRUD/utils';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
 import { useListViewResource } from 'src/views/CRUD/hooks';
 import SubMenu, { SubMenuProps } from 'src/components/Menu/SubMenu';
@@ -38,6 +38,7 @@ import { QueryObject } from 'src/views/CRUD/types';
 import QueryPreviewModal from './QueryPreviewModal';
 
 const PAGE_SIZE = 25;
+const SQL_PREVIEW_MAX_LINES = 4;
 
 const TopAlignedListView = styled(ListView)<ListViewProps<QueryObject>>`
   table .table-cell {
@@ -52,15 +53,7 @@ const StyledSyntaxHighlighter = styled(SyntaxHighlighter)`
   text-overflow: ellipsis;
   white-space: nowrap;
 `;
-const SQL_PREVIEW_MAX_LINES = 4;
-function shortenSQL(sql: string) {
-  let lines: string[] = sql.split('\n');
-  if (lines.length >= SQL_PREVIEW_MAX_LINES) {
-    lines = lines.slice(0, SQL_PREVIEW_MAX_LINES);
-    lines.push('...');
-  }
-  return lines.join('\n');
-}
+
 interface QueryListProps {
   addDangerToast: (msg: string, config?: any) => any;
   addSuccessToast: (msg: string, config?: any) => any;
@@ -298,7 +291,7 @@ function QueryList({ addDangerToast, addSuccessToast }: QueryListProps) {
               onClick={() => setQueryCurrentlyPreviewing(original)}
             >
               <StyledSyntaxHighlighter language="sql" style={github}>
-                {shortenSQL(original.sql)}
+                {shortenSQL(original.sql, SQL_PREVIEW_MAX_LINES)}
               </StyledSyntaxHighlighter>
             </div>
           );
diff --git a/superset-frontend/src/views/CRUD/utils.tsx b/superset-frontend/src/views/CRUD/utils.tsx
index 2cc9999..e22310d 100644
--- a/superset-frontend/src/views/CRUD/utils.tsx
+++ b/superset-frontend/src/views/CRUD/utils.tsx
@@ -264,22 +264,31 @@ export function handleDashboardDelete(
   );
 }
 
+export function shortenSQL(sql: string, maxLines: number) {
+  let lines: string[] = sql.split('\n');
+  if (lines.length >= maxLines) {
+    lines = lines.slice(0, maxLines);
+    lines.push('...');
+  }
+  return lines.join('\n');
+}
+
 const breakpoints = [576, 768, 992, 1200];
 export const mq = breakpoints.map(bp => `@media (max-width: ${bp}px)`);
 
 export const CardContainer = styled.div`
   display: grid;
-  grid-template-columns: repeat(auto-fit, minmax(31%, max-content));
+  grid-template-columns: repeat(auto-fit, minmax(31%, 31%));
   ${[mq[3]]} {
-    grid-template-columns: repeat(auto-fit, minmax(31%, max-content));
+    grid-template-columns: repeat(auto-fit, minmax(31%, 31%));
   }
 
   ${[mq[2]]} {
-    grid-template-columns: repeat(auto-fit, minmax(48%, max-content));
+    grid-template-columns: repeat(auto-fit, minmax(48%, 48%));
   }
 
   ${[mq[1]]} {
-    grid-template-columns: repeat(auto-fit, minmax(50%, max-content));
+    grid-template-columns: repeat(auto-fit, minmax(50%, 80%));
   }
   grid-gap: ${({ theme }) => theme.gridUnit * 8}px;
   justify-content: left;
diff --git a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
index 6de2f3c..6232ec1 100644
--- a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
@@ -18,6 +18,9 @@
  */
 import React, { useState } from 'react';
 import { t, SupersetClient, styled } from '@superset-ui/core';
+import SyntaxHighlighter from 'react-syntax-highlighter/dist/cjs/light';
+import sql from 'react-syntax-highlighter/dist/cjs/languages/hljs/sql';
+import github from 'react-syntax-highlighter/dist/cjs/styles/hljs/github';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
 import { Dropdown, Menu } from 'src/common/components';
 import { useListViewResource, copyQueryLink } from 'src/views/CRUD/hooks';
@@ -30,9 +33,11 @@ import {
   IconContainer,
   CardContainer,
   createErrorHandler,
-  CardStyles,
+  shortenSQL,
 } from '../utils';
 
+SyntaxHighlighter.registerLanguage('sql', sql);
+
 const PAGE_SIZE = 3;
 
 interface Query {
@@ -45,6 +50,8 @@ interface Query {
   description?: string;
   end_time?: string;
   label?: string;
+  changed_on_delta_humanized?: string;
+  sql?: string | null;
 }
 
 interface SavedQueriesProps {
@@ -57,19 +64,51 @@ interface SavedQueriesProps {
   mine: Array<Query>;
 }
 
+export const CardStyles = styled.div`
+  cursor: pointer;
+  a {
+    text-decoration: none;
+  }
+  .ant-card-cover {
+    border-bottom: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
+    & > div {
+      height: 171px;
+    }
+  }
+  .gradient-container > div {
+    background-size: contain;
+    background-repeat: no-repeat;
+    background-position: center;
+    background-color: ${({ theme }) => theme.colors.secondary.light3};
+    display: inline-block;
+    width: 100%;
+    height: 179px;
+    background-repeat: no-repeat;
+    vertical-align: middle;
+  }
+`;
+
 const QueryData = styled.div`
-  display: flex;
-  flex-direction: row;
-  justify-content: flex-start;
-  border-bottom: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
-  .title {
-    font-weight: ${({ theme }) => theme.typography.weights.normal};
-    color: ${({ theme }) => theme.colors.grayscale.light1};
+  svg {
+    margin-left: ${({ theme }) => theme.gridUnit * 10}px;
   }
-  .holder {
-    margin: ${({ theme }) => theme.gridUnit * 2}px;
+  .query-title {
+    padding: ${({ theme }) => theme.gridUnit * 2 + 2}px;
+    font-size: ${({ theme }) => theme.typography.sizes.l}px;
   }
 `;
+
+const QueryContainer = styled.div`
+  pre {
+    height: ${({ theme }) => theme.gridUnit * 40}px;
+    border: none !important;
+    background-color: ${({ theme }) =>
+      theme.colors.grayscale.light5} !important;
+    overflow: hidden;
+    padding: ${({ theme }) => theme.gridUnit * 4}px !important;
+  }
+`;
+
 const SavedQueries = ({
   user,
   addDangerToast,
@@ -259,35 +298,50 @@ const SavedQueries = ({
               key={q.id}
             >
               <ListViewCard
-                imgFallbackURL=""
                 imgURL=""
                 url={`/superset/sqllab?savedQueryId=${q.id}`}
                 title={q.label}
-                rows={q.rows}
-                description={t('Last run ', q.end_time)}
+                imgFallbackURL="/static/assets/images/empty-query.svg"
+                description={t('Last run %s', q.changed_on_delta_humanized)}
                 cover={
-                  <QueryData>
-                    <div className="holder">
-                      <div className="title">{t('Tables')}</div>
-                      <div>{q?.sql_tables?.length}</div>
-                    </div>
-                    <div className="holder">
-                      <div className="title">{t('Datasource Name')}</div>
-                      <div>{q?.sql_tables && q.sql_tables[0]?.table}</div>
-                    </div>
-                  </QueryData>
+                  q?.sql?.length ? (
+                    <QueryContainer>
+                      <SyntaxHighlighter
+                        language="sql"
+                        lineProps={{
+                          style: {
+                            color: 'black',
+                            wordBreak: 'break-all',
+                            whiteSpace: 'pre-wrap',
+                          },
+                        }}
+                        style={github}
+                        wrapLines
+                        lineNumberStyle={{
+                          display: 'none',
+                        }}
+                        showLineNumbers={false}
+                      >
+                        {shortenSQL(q.sql, 25)}
+                      </SyntaxHighlighter>
+                    </QueryContainer>
+                  ) : (
+                    false
+                  )
                 }
                 actions={
-                  <ListViewCard.Actions
-                    onClick={e => {
-                      e.stopPropagation();
-                      e.preventDefault();
-                    }}
-                  >
-                    <Dropdown overlay={renderMenu(q)}>
-                      <Icon name="more-horiz" />
-                    </Dropdown>
-                  </ListViewCard.Actions>
+                  <QueryData>
+                    <ListViewCard.Actions
+                      onClick={e => {
+                        e.stopPropagation();
+                        e.preventDefault();
+                      }}
+                    >
+                      <Dropdown overlay={renderMenu(q)}>
+                        <Icon name="more-horiz" />
+                      </Dropdown>
+                    </ListViewCard.Actions>
+                  </QueryData>
                 }
               />
             </CardStyles>