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/05 16:33:11 UTC

[superset] branch master updated: chore(sqllab): Typescript for SqlEditor component (#25228)

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 34f99708d4 chore(sqllab): Typescript for SqlEditor component (#25228)
34f99708d4 is described below

commit 34f99708d46e1b5b0f0211f493be9617da86dcbf
Author: JUST.in DO IT <ju...@airbnb.com>
AuthorDate: Thu Oct 5 12:33:01 2023 -0400

    chore(sqllab): Typescript for SqlEditor component (#25228)
---
 superset-frontend/package-lock.json                |  13 ++
 superset-frontend/package.json                     |   1 +
 .../SqlLab/components/AceEditorWrapper/index.tsx   |   3 +-
 .../components/RunQueryActionButton/index.tsx      |   3 +-
 .../src/SqlLab/components/SaveQuery/index.tsx      |   7 +-
 .../components/ScheduleQueryButton/index.tsx       |   2 +-
 .../{SqlEditor.test.jsx => SqlEditor.test.tsx}     |  41 +++--
 .../components/SqlEditor/{index.jsx => index.tsx}  | 198 ++++++++++++---------
 .../SqlLab/components/SqlEditorLeftBar/index.tsx   |  10 +-
 .../components/TemplateParamsEditor/index.tsx      |   2 +-
 superset-frontend/src/SqlLab/constants.ts          |   3 +-
 superset-frontend/src/SqlLab/types.ts              |   2 +
 .../src/components/DatabaseSelector/index.tsx      |   8 +-
 .../databases/DatabaseModal/index.test.tsx         |   2 +
 superset-frontend/src/features/databases/types.ts  |   5 +-
 15 files changed, 179 insertions(+), 121 deletions(-)

diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json
index 4c0fa255e1..b8780a78a0 100644
--- a/superset-frontend/package-lock.json
+++ b/superset-frontend/package-lock.json
@@ -191,6 +191,7 @@
         "@types/jquery": "^3.5.8",
         "@types/js-levenshtein": "^1.1.0",
         "@types/json-bigint": "^1.0.1",
+        "@types/mousetrap": "^1.6.11",
         "@types/react": "^16.9.43",
         "@types/react-dom": "^16.9.8",
         "@types/react-gravatar": "^2.6.8",
@@ -19514,6 +19515,12 @@
       "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==",
       "dev": true
     },
+    "node_modules/@types/mousetrap": {
+      "version": "1.6.11",
+      "resolved": "https://registry.npmjs.org/@types/mousetrap/-/mousetrap-1.6.11.tgz",
+      "integrity": "sha512-F0oAily9Q9QQpv9JKxKn0zMKfOo36KHCW7myYsmUyf2t0g+sBTbG3UleTPoguHdE1z3GLFr3p7/wiOio52QFjQ==",
+      "dev": true
+    },
     "node_modules/@types/ms": {
       "version": "0.7.31",
       "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
@@ -79317,6 +79324,12 @@
       "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==",
       "dev": true
     },
+    "@types/mousetrap": {
+      "version": "1.6.11",
+      "resolved": "https://registry.npmjs.org/@types/mousetrap/-/mousetrap-1.6.11.tgz",
+      "integrity": "sha512-F0oAily9Q9QQpv9JKxKn0zMKfOo36KHCW7myYsmUyf2t0g+sBTbG3UleTPoguHdE1z3GLFr3p7/wiOio52QFjQ==",
+      "dev": true
+    },
     "@types/ms": {
       "version": "0.7.31",
       "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
diff --git a/superset-frontend/package.json b/superset-frontend/package.json
index 299017b0d0..8cee75833c 100644
--- a/superset-frontend/package.json
+++ b/superset-frontend/package.json
@@ -256,6 +256,7 @@
     "@types/jquery": "^3.5.8",
     "@types/js-levenshtein": "^1.1.0",
     "@types/json-bigint": "^1.0.1",
+    "@types/mousetrap": "^1.6.11",
     "@types/react": "^16.9.43",
     "@types/react-dom": "^16.9.8",
     "@types/react-gravatar": "^2.6.8",
diff --git a/superset-frontend/src/SqlLab/components/AceEditorWrapper/index.tsx b/superset-frontend/src/SqlLab/components/AceEditorWrapper/index.tsx
index 0adf3f8e1f..17c84a3664 100644
--- a/superset-frontend/src/SqlLab/components/AceEditorWrapper/index.tsx
+++ b/superset-frontend/src/SqlLab/components/AceEditorWrapper/index.tsx
@@ -17,6 +17,7 @@
  * under the License.
  */
 import React, { useState, useEffect, useRef } from 'react';
+import type { IAceEditor } from 'react-ace/lib/types';
 import { useDispatch } from 'react-redux';
 import { css, styled, usePrevious } from '@superset-ui/core';
 
@@ -30,7 +31,7 @@ type HotKey = {
   key: string;
   descr: string;
   name: string;
-  func: () => void;
+  func: (aceEditor: IAceEditor) => void;
 };
 
 type AceEditorWrapperProps = {
diff --git a/superset-frontend/src/SqlLab/components/RunQueryActionButton/index.tsx b/superset-frontend/src/SqlLab/components/RunQueryActionButton/index.tsx
index d2da587091..30f8121132 100644
--- a/superset-frontend/src/SqlLab/components/RunQueryActionButton/index.tsx
+++ b/superset-frontend/src/SqlLab/components/RunQueryActionButton/index.tsx
@@ -19,7 +19,6 @@
 import React, { useMemo } from 'react';
 import { t, styled, useTheme } from '@superset-ui/core';
 
-import { Menu } from 'src/components/Menu';
 import Button from 'src/components/Button';
 import Icons from 'src/components/Icons';
 import { DropdownButton } from 'src/components/DropdownButton';
@@ -33,7 +32,7 @@ export interface RunQueryActionButtonProps {
   queryState?: string;
   runQuery: (c?: boolean) => void;
   stopQuery: () => void;
-  overlayCreateAsMenu: typeof Menu | null;
+  overlayCreateAsMenu: React.ReactElement | null;
 }
 
 const buildText = (
diff --git a/superset-frontend/src/SqlLab/components/SaveQuery/index.tsx b/superset-frontend/src/SqlLab/components/SaveQuery/index.tsx
index 6ed0f4c668..a7ac8b1b2a 100644
--- a/superset-frontend/src/SqlLab/components/SaveQuery/index.tsx
+++ b/superset-frontend/src/SqlLab/components/SaveQuery/index.tsx
@@ -17,6 +17,7 @@
  * under the License.
  */
 import React, { useState, useEffect, useMemo } from 'react';
+import type { DatabaseObject } from 'src/features/databases/types';
 import { Row, Col } from 'src/components';
 import { Input, TextArea } from 'src/components/Input';
 import { t, styled } from '@superset-ui/core';
@@ -39,10 +40,10 @@ interface SaveQueryProps {
   onSave: (arg0: QueryPayload, id: string) => void;
   onUpdate: (arg0: QueryPayload, id: string) => void;
   saveQueryWarning: string | null;
-  database: Record<string, any>;
+  database: Partial<DatabaseObject> | undefined;
 }
 
-type QueryPayload = {
+export type QueryPayload = {
   name: string;
   description?: string;
   id?: string;
@@ -65,7 +66,7 @@ const SaveQuery = ({
   queryEditorId,
   onSave = () => {},
   onUpdate,
-  saveQueryWarning = null,
+  saveQueryWarning,
   database,
   columns,
 }: SaveQueryProps) => {
diff --git a/superset-frontend/src/SqlLab/components/ScheduleQueryButton/index.tsx b/superset-frontend/src/SqlLab/components/ScheduleQueryButton/index.tsx
index d0a381638d..f2e3d33581 100644
--- a/superset-frontend/src/SqlLab/components/ScheduleQueryButton/index.tsx
+++ b/superset-frontend/src/SqlLab/components/ScheduleQueryButton/index.tsx
@@ -79,7 +79,7 @@ interface ScheduleQueryButtonProps {
   defaultLabel?: string;
   sql: string;
   schema?: string;
-  dbId: number;
+  dbId?: number;
   animation?: boolean;
   onSchedule?: Function;
   scheduleQueryWarning: string | null;
diff --git a/superset-frontend/src/SqlLab/components/SqlEditor/SqlEditor.test.jsx b/superset-frontend/src/SqlLab/components/SqlEditor/SqlEditor.test.tsx
similarity index 89%
rename from superset-frontend/src/SqlLab/components/SqlEditor/SqlEditor.test.jsx
rename to superset-frontend/src/SqlLab/components/SqlEditor/SqlEditor.test.tsx
index ff335e14ea..63f67170d0 100644
--- a/superset-frontend/src/SqlLab/components/SqlEditor/SqlEditor.test.jsx
+++ b/superset-frontend/src/SqlLab/components/SqlEditor/SqlEditor.test.tsx
@@ -21,7 +21,6 @@ import { act } from 'react-dom/test-utils';
 import { fireEvent, render, waitFor } from 'spec/helpers/testing-library';
 import fetchMock from 'fetch-mock';
 import reducers from 'spec/helpers/reducerIndex';
-import SqlEditor from 'src/SqlLab/components/SqlEditor';
 import { setupStore } from 'src/views/store';
 import {
   initialState,
@@ -34,10 +33,20 @@ import ResultSet from 'src/SqlLab/components/ResultSet';
 import { api } from 'src/hooks/apiResources/queryApi';
 import { getExtensionsRegistry } from '@superset-ui/core';
 import setupExtensions from 'src/setup/setupExtensions';
+import type { Action, Middleware, Store } from 'redux';
+import SqlEditor, { Props } from '.';
 
 jest.mock('src/components/AsyncAceEditor', () => ({
   ...jest.requireActual('src/components/AsyncAceEditor'),
-  FullSQLEditor: ({ onChange, onBlur, value }) => (
+  FullSQLEditor: ({
+    onChange,
+    onBlur,
+    value,
+  }: {
+    onChange: (value: string) => void;
+    onBlur: React.FocusEventHandler<HTMLTextAreaElement>;
+    value: string;
+  }) => (
     <textarea
       data-test="react-ace"
       onChange={evt => onChange(evt.target.value)}
@@ -56,8 +65,8 @@ fetchMock.get('glob:*/api/v1/database/*', { result: [] });
 fetchMock.get('glob:*/api/v1/database/*/tables/*', { options: [] });
 fetchMock.post('glob:*/sqllab/execute/*', { result: [] });
 
-let store;
-let actions;
+let store: Store;
+let actions: Action[];
 const latestQuery = {
   ...queries[0],
   sqlEditorId: defaultQueryEditor.id,
@@ -91,13 +100,13 @@ const mockInitialState = {
   },
 };
 
-const setup = (props = {}, store) =>
+const setup = (props: Props, store: Store) =>
   render(<SqlEditor {...props} />, {
     useRedux: true,
     ...(store && { store }),
   });
 
-const logAction = () => next => action => {
+const logAction: Middleware = () => next => action => {
   if (typeof action === 'function') {
     return next(action);
   }
@@ -105,9 +114,9 @@ const logAction = () => next => action => {
   return next(action);
 };
 
-const createStore = initState =>
+const createStore = (initState: object) =>
   setupStore({
-    disableDegugger: true,
+    disableDebugger: true,
     initialState: initState,
     rootReducers: reducers,
     middleware: getDefaultMiddleware =>
@@ -124,18 +133,22 @@ describe('SqlEditor', () => {
     defaultQueryLimit: 1000,
     maxRow: 100000,
     displayLimit: 100,
+    saveQueryWarning: '',
+    scheduleQueryWarning: '',
   };
 
   beforeEach(() => {
     store = createStore(mockInitialState);
     actions = [];
 
-    SqlEditorLeftBar.mockClear();
-    SqlEditorLeftBar.mockImplementation(() => (
+    (SqlEditorLeftBar as jest.Mock).mockClear();
+    (SqlEditorLeftBar as jest.Mock).mockImplementation(() => (
       <div data-test="mock-sql-editor-left-bar" />
     ));
-    ResultSet.mockClear();
-    ResultSet.mockImplementation(() => <div data-test="mock-result-set" />);
+    (ResultSet as jest.Mock).mockClear();
+    (ResultSet as jest.Mock).mockImplementation(() => (
+      <div data-test="mock-result-set" />
+    ));
   });
 
   afterEach(() => {
@@ -168,8 +181,8 @@ describe('SqlEditor', () => {
     const { findByTestId } = setup(mockedProps, store);
     const editor = await findByTestId('react-ace');
     const sql = 'select *';
-    const renderCount = SqlEditorLeftBar.mock.calls.length;
-    const renderCountForSouthPane = ResultSet.mock.calls.length;
+    const renderCount = (SqlEditorLeftBar as jest.Mock).mock.calls.length;
+    const renderCountForSouthPane = (ResultSet as jest.Mock).mock.calls.length;
     expect(SqlEditorLeftBar).toHaveBeenCalledTimes(renderCount);
     expect(ResultSet).toHaveBeenCalledTimes(renderCountForSouthPane);
     fireEvent.change(editor, { target: { value: sql } });
diff --git a/superset-frontend/src/SqlLab/components/SqlEditor/index.jsx b/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx
similarity index 87%
rename from superset-frontend/src/SqlLab/components/SqlEditor/index.jsx
rename to superset-frontend/src/SqlLab/components/SqlEditor/index.tsx
index 2626d70d73..08ddf2b0fb 100644
--- a/superset-frontend/src/SqlLab/components/SqlEditor/index.jsx
+++ b/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx
@@ -24,11 +24,12 @@ import React, {
   useMemo,
   useRef,
   useCallback,
+  ChangeEvent,
 } from 'react';
+import type AceEditor from 'react-ace';
 import useEffectEvent from 'src/hooks/useEffectEvent';
 import { CSSTransition } from 'react-transition-group';
 import { shallowEqual, useDispatch, useSelector } from 'react-redux';
-import PropTypes from 'prop-types';
 import Split from 'react-split';
 import {
   css,
@@ -38,7 +39,11 @@ import {
   t,
   useTheme,
   getExtensionsRegistry,
+  QueryResponse,
+  Query,
 } from '@superset-ui/core';
+import type { QueryEditor, SqlLabRootState } from 'src/SqlLab/types';
+import type { DatabaseObject } from 'src/features/databases/types';
 import debounce from 'lodash/debounce';
 import throttle from 'lodash/throttle';
 import Modal from 'src/components/Modal';
@@ -90,11 +95,11 @@ import getBootstrapData from 'src/utils/getBootstrapData';
 import { isEmpty } from 'lodash';
 import TemplateParamsEditor from '../TemplateParamsEditor';
 import SouthPane from '../SouthPane';
-import SaveQuery from '../SaveQuery';
+import SaveQuery, { QueryPayload } from '../SaveQuery';
 import ScheduleQueryButton from '../ScheduleQueryButton';
 import EstimateQueryCostButton from '../EstimateQueryCostButton';
 import ShareSqlLabQuery from '../ShareSqlLabQuery';
-import SqlEditorLeftBar from '../SqlEditorLeftBar';
+import SqlEditorLeftBar, { ExtendedTable } from '../SqlEditorLeftBar';
 import AceEditorWrapper from '../AceEditorWrapper';
 import RunQueryActionButton from '../RunQueryActionButton';
 import QueryLimitSelect from '../QueryLimitSelect';
@@ -133,7 +138,7 @@ const StyledToolbar = styled.div`
   }
 `;
 
-const StyledSidebar = styled.div`
+const StyledSidebar = styled.div<{ width: number; hide: boolean | undefined }>`
   flex: 0 0 ${({ width }) => width}px;
   width: ${({ width }) => width}px;
   padding: ${({ theme, hide }) => (hide ? 0 : theme.gridUnit * 2.5)}px;
@@ -198,46 +203,60 @@ const StyledSqlEditor = styled.div`
   `}
 `;
 
-const propTypes = {
-  tables: PropTypes.array.isRequired,
-  queryEditor: PropTypes.object.isRequired,
-  defaultQueryLimit: PropTypes.number.isRequired,
-  maxRow: PropTypes.number.isRequired,
-  displayLimit: PropTypes.number.isRequired,
-  saveQueryWarning: PropTypes.string,
-  scheduleQueryWarning: PropTypes.string,
-};
-
 const extensionsRegistry = getExtensionsRegistry();
 
-const SqlEditor = ({
+export type Props = {
+  tables: ExtendedTable[];
+  queryEditor: QueryEditor;
+  defaultQueryLimit: number;
+  maxRow: number;
+  displayLimit: number;
+  saveQueryWarning: string | null;
+  scheduleQueryWarning: string | null;
+};
+
+const elementStyle = (
+  dimension: string,
+  elementSize: number,
+  gutterSize: number,
+) => ({
+  [dimension]: `calc(${elementSize}% - ${
+    gutterSize + SQL_EDITOR_GUTTER_MARGIN
+  }px)`,
+});
+
+const SqlEditor: React.FC<Props> = ({
   tables,
   queryEditor,
   defaultQueryLimit,
   maxRow,
   displayLimit,
   saveQueryWarning,
-  scheduleQueryWarning = null,
+  scheduleQueryWarning,
 }) => {
   const theme = useTheme();
   const dispatch = useDispatch();
 
-  const { database, latestQuery, hideLeftBar } = useSelector(
-    ({ sqlLab: { unsavedQueryEditor, databases, queries } }) => {
-      let { dbId, latestQueryId, hideLeftBar } = queryEditor;
-      if (unsavedQueryEditor?.id === queryEditor.id) {
-        dbId = unsavedQueryEditor.dbId || dbId;
-        latestQueryId = unsavedQueryEditor.latestQueryId || latestQueryId;
-        hideLeftBar = unsavedQueryEditor.hideLeftBar || hideLeftBar;
-      }
-      return {
-        database: databases[dbId],
-        latestQuery: queries[latestQueryId],
-        hideLeftBar,
-      };
-    },
-    shallowEqual,
-  );
+  const { database, latestQuery, hideLeftBar } = useSelector<
+    SqlLabRootState,
+    {
+      database?: DatabaseObject;
+      latestQuery?: QueryResponse;
+      hideLeftBar?: boolean;
+    }
+  >(({ sqlLab: { unsavedQueryEditor, databases, queries } }) => {
+    let { dbId, latestQueryId, hideLeftBar } = queryEditor;
+    if (unsavedQueryEditor?.id === queryEditor.id) {
+      dbId = unsavedQueryEditor.dbId || dbId;
+      latestQueryId = unsavedQueryEditor.latestQueryId || latestQueryId;
+      hideLeftBar = unsavedQueryEditor.hideLeftBar || hideLeftBar;
+    }
+    return {
+      database: databases[dbId || ''],
+      latestQuery: queries[latestQueryId || ''],
+      hideLeftBar,
+    };
+  }, shallowEqual);
 
   const [height, setHeight] = useState(0);
   const [autorun, setAutorun] = useState(queryEditor.autorun);
@@ -255,8 +274,8 @@ const SqlEditor = ({
   const [createAs, setCreateAs] = useState('');
   const [showEmptyState, setShowEmptyState] = useState(false);
 
-  const sqlEditorRef = useRef(null);
-  const northPaneRef = useRef(null);
+  const sqlEditorRef = useRef<HTMLDivElement>(null);
+  const northPaneRef = useRef<HTMLDivElement>(null);
 
   const SqlFormExtension = extensionsRegistry.get('sqleditor.extension.form');
 
@@ -311,7 +330,7 @@ const SqlEditor = ({
   const getHotkeyConfig = useCallback(() => {
     // Get the user's OS
     const userOS = detectOS();
-    const base = [
+    return [
       {
         name: 'runQuery1',
         key: 'ctrl+r',
@@ -332,11 +351,34 @@ const SqlEditor = ({
           }
         },
       },
+      {
+        name: 'newTab',
+        key: userOS === 'Windows' ? 'ctrl+q' : 'ctrl+t',
+        descr: t('New tab'),
+        func: () => {
+          dispatch(addNewQueryEditor());
+        },
+      },
+      {
+        name: 'stopQuery',
+        key: userOS === 'MacOS' ? 'ctrl+x' : 'ctrl+e',
+        descr: t('Stop query'),
+        func: stopQuery,
+      },
+    ];
+  }, [dispatch, queryEditor.sql, startQuery, stopQuery]);
+
+  const hotkeys = useMemo(() => {
+    // Get all hotkeys including ace editor hotkeys
+    // Get the user's OS
+    const userOS = detectOS();
+    const base = [
+      ...getHotkeyConfig(),
       {
         name: 'runQuery3',
         key: 'ctrl+shift+enter',
         descr: t('Run current query'),
-        func: editor => {
+        func: (editor: AceEditor['editor']) => {
           if (!editor.getValue().trim()) {
             return;
           }
@@ -347,7 +389,6 @@ const SqlEditor = ({
           let end = editor.find(';', {
             backwards: false,
             skipCurrent: true,
-            start: cursorPosition,
           })?.end;
           if (!end || end.row < cursorPosition.row) {
             end = {
@@ -358,18 +399,13 @@ const SqlEditor = ({
           let start = editor.find(';', {
             backwards: true,
             skipCurrent: true,
-            start: cursorPosition,
           })?.end;
-          let currentLine = editor.find(';', {
-            backwards: true,
-            skipCurrent: true,
-            start: cursorPosition,
-          })?.end?.row;
+          let currentLine = start?.row;
           if (
             !currentLine ||
             currentLine > cursorPosition.row ||
             (currentLine === cursorPosition.row &&
-              start?.column > cursorPosition.column)
+              (start?.column || 0) > cursorPosition.column)
           ) {
             currentLine = 0;
           }
@@ -384,7 +420,7 @@ const SqlEditor = ({
           if (currentLine !== start?.row) {
             start = { row: currentLine, column: 0 };
           }
-          editor.selection.setRange({
+          editor.selection.setSelectionRange({
             start: start ?? { row: 0, column: 0 },
             end,
           });
@@ -394,35 +430,20 @@ const SqlEditor = ({
           editor.scrollToRow(currentRow);
         },
       },
-      {
-        name: 'newTab',
-        key: userOS === 'Windows' ? 'ctrl+q' : 'ctrl+t',
-        descr: t('New tab'),
-        func: () => {
-          dispatch(addNewQueryEditor());
-        },
-      },
-      {
-        name: 'stopQuery',
-        key: userOS === 'MacOS' ? 'ctrl+x' : 'ctrl+e',
-        descr: t('Stop query'),
-        func: stopQuery,
-      },
     ];
-
     if (userOS === 'MacOS') {
       base.push({
         name: 'previousLine',
         key: 'ctrl+p',
         descr: t('Previous Line'),
         func: editor => {
-          editor.navigateUp(1);
+          editor.navigateUp();
         },
       });
     }
 
     return base;
-  }, [dispatch, queryEditor.sql, startQuery, stopQuery]);
+  }, [getHotkeyConfig, startQuery]);
 
   const onBeforeUnload = useEffectEvent(event => {
     if (
@@ -461,20 +482,29 @@ const SqlEditor = ({
 
   useEffect(() => {
     // setup hotkeys
-    Mousetrap.reset();
     const hotkeys = getHotkeyConfig();
     hotkeys.forEach(keyConfig => {
       Mousetrap.bind([keyConfig.key], keyConfig.func);
     });
+    return () => {
+      hotkeys.forEach(keyConfig => {
+        Mousetrap.unbind(keyConfig.key);
+      });
+    };
   }, [getHotkeyConfig, latestQuery]);
 
   const onResizeStart = () => {
     // Set the heights on the ace editor and the ace content area after drag starts
     // to smooth out the visual transition to the new heights when drag ends
-    document.getElementsByClassName('ace_content')[0].style.height = '100%';
+    const editorEl = document.getElementsByClassName(
+      'ace_content',
+    )[0] as HTMLElement;
+    if (editorEl) {
+      editorEl.style.height = '100%';
+    }
   };
 
-  const onResizeEnd = ([northPercent, southPercent]) => {
+  const onResizeEnd = ([northPercent, southPercent]: number[]) => {
     setNorthPercent(northPercent);
     setSouthPercent(southPercent);
 
@@ -495,7 +525,7 @@ const SqlEditor = ({
     [setQueryEditorAndSaveSql],
   );
 
-  const onSqlChanged = sql => {
+  const onSqlChanged = (sql: string) => {
     dispatch(queryEditorSetSql(queryEditor, sql));
     setQueryEditorAndSaveSqlWithDebounce(sql);
   };
@@ -503,9 +533,9 @@ const SqlEditor = ({
   // Return the heights for the ace editor and the south pane as an object
   // given the height of the sql editor, north pane percent and south pane percent.
   const getAceEditorAndSouthPaneHeights = (
-    height,
-    northPercent,
-    southPercent,
+    height: number,
+    northPercent: number,
+    southPercent: number,
   ) => ({
     aceEditorHeight:
       (height * northPercent) / (theme.gridUnit * 25) -
@@ -530,12 +560,6 @@ const SqlEditor = ({
     setAutocompleteEnabled(!autocompleteEnabled);
   };
 
-  const elementStyle = (dimension, elementSize, gutterSize) => ({
-    [dimension]: `calc(${elementSize}% - ${
-      gutterSize + SQL_EDITOR_GUTTER_MARGIN
-    }px)`,
-  });
-
   const createTableAs = () => {
     startQuery(true, CtasEnum.TABLE);
     setShowCreateAsModal(false);
@@ -548,7 +572,7 @@ const SqlEditor = ({
     setCtas('');
   };
 
-  const ctasChanged = event => {
+  const ctasChanged = (event: ChangeEvent<HTMLInputElement>) => {
     setCtas(event.target.value);
   };
 
@@ -566,7 +590,6 @@ const SqlEditor = ({
           <AntdSwitch
             checked={autocompleteEnabled}
             onChange={handleToggleAutocompleteEnabled}
-            name="autocomplete-switch"
           />{' '}
         </Menu.Item>
         {isFeatureEnabled(FeatureFlag.ENABLE_TEMPLATE_PROCESSING) && (
@@ -585,7 +608,7 @@ const SqlEditor = ({
             <ScheduleQueryButton
               defaultLabel={qe.name}
               sql={qe.sql}
-              onSchedule={query => dispatch(scheduleQuery(query))}
+              onSchedule={(query: Query) => dispatch(scheduleQuery(query))}
               schema={qe.schema}
               dbId={qe.dbId}
               scheduleQueryWarning={scheduleQueryWarning}
@@ -598,7 +621,7 @@ const SqlEditor = ({
     );
   };
 
-  const onSaveQuery = async (query, clientId) => {
+  const onSaveQuery = async (query: QueryPayload, clientId: string) => {
     const savedQuery = await dispatch(saveQuery(query, clientId));
     dispatch(addSavedQueryToTabState(queryEditor, savedQuery));
   };
@@ -639,7 +662,7 @@ const SqlEditor = ({
         <div className="leftItems">
           <span>
             <RunQueryActionButton
-              allowAsync={database ? database.allow_run_async : false}
+              allowAsync={database?.allow_run_async === true}
               queryEditorId={queryEditor.id}
               queryState={latestQuery?.state}
               runQuery={runQuery}
@@ -668,7 +691,7 @@ const SqlEditor = ({
             <Timer
               startTime={latestQuery.startDttm}
               endTime={latestQuery.endDttm}
-              state={STATE_TYPE_MAP[latestQuery.state]}
+              status={STATE_TYPE_MAP[latestQuery.state]}
               isRunning={latestQuery.state === 'running'}
             />
           )}
@@ -679,8 +702,8 @@ const SqlEditor = ({
               queryEditorId={queryEditor.id}
               columns={latestQuery?.results?.columns || []}
               onSave={onSaveQuery}
-              onUpdate={(query, remoteId, id) =>
-                dispatch(updateSavedQuery(query, remoteId, id))
+              onUpdate={(query, remoteId) =>
+                dispatch(updateSavedQuery(query, remoteId))
               }
               saveQueryWarning={saveQueryWarning}
               database={database}
@@ -689,7 +712,7 @@ const SqlEditor = ({
           <span>
             <ShareSqlLabQuery queryEditorId={queryEditor.id} />
           </span>
-          <AntdDropdown overlay={renderDropdown()} trigger="click">
+          <AntdDropdown overlay={renderDropdown()} trigger={['click']}>
             <Icons.MoreHoriz iconColor={theme.colors.grayscale.base} />
           </AntdDropdown>
         </div>
@@ -698,7 +721,6 @@ const SqlEditor = ({
   };
 
   const queryPane = () => {
-    const hotkeys = getHotkeyConfig();
     const { aceEditorHeight, southPaneHeight } =
       getAceEditorAndSouthPaneHeights(height, northPercent, southPercent);
     return (
@@ -731,7 +753,7 @@ const SqlEditor = ({
             height={`${aceEditorHeight}px`}
             hotkeys={hotkeys}
           />
-          {renderEditorBottomBar(hotkeys)}
+          {renderEditorBottomBar()}
         </div>
         <SouthPane
           queryEditorId={queryEditor.id}
@@ -792,7 +814,7 @@ const SqlEditor = ({
         queryPane()
       )}
       <Modal
-        visible={showCreateAsModal}
+        show={showCreateAsModal}
         title={t(createViewModalTitle)}
         onHide={() => setShowCreateAsModal(false)}
         footer={
@@ -828,6 +850,4 @@ const SqlEditor = ({
   );
 };
 
-SqlEditor.propTypes = propTypes;
-
 export default SqlEditor;
diff --git a/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx b/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx
index ef55e30ec6..1a9cb17f97 100644
--- a/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx
+++ b/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx
@@ -46,7 +46,7 @@ import Icons from 'src/components/Icons';
 import { TableSelectorMultiple } from 'src/components/TableSelector';
 import { IconTooltip } from 'src/components/IconTooltip';
 import useQueryEditor from 'src/SqlLab/hooks/useQueryEditor';
-import { DatabaseObject } from 'src/components/DatabaseSelector';
+import type { DatabaseObject } from 'src/components/DatabaseSelector';
 import { emptyStateComponent } from 'src/components/EmptyState';
 import {
   getItem,
@@ -55,7 +55,7 @@ import {
 } from 'src/utils/localStorageHelpers';
 import TableElement from '../TableElement';
 
-interface ExtendedTable extends Table {
+export interface ExtendedTable extends Table {
   expanded: boolean;
 }
 
@@ -63,7 +63,7 @@ interface SqlEditorLeftBarProps {
   queryEditorId: string;
   height?: number;
   tables?: ExtendedTable[];
-  database: DatabaseObject;
+  database?: DatabaseObject;
   setEmptyState: Dispatch<SetStateAction<boolean>>;
 }
 
@@ -134,7 +134,9 @@ const SqlEditorLeftBar = ({
     if (bool && userSelected) {
       setUserSelected(userSelected);
       setItem(LocalStorageKeys.db, null);
-    } else setUserSelected(database);
+    } else if (database) {
+      setUserSelected(database);
+    }
   }, [database]);
 
   const onEmptyResults = (searchText?: string) => {
diff --git a/superset-frontend/src/SqlLab/components/TemplateParamsEditor/index.tsx b/superset-frontend/src/SqlLab/components/TemplateParamsEditor/index.tsx
index a862fd326a..0aa5503861 100644
--- a/superset-frontend/src/SqlLab/components/TemplateParamsEditor/index.tsx
+++ b/superset-frontend/src/SqlLab/components/TemplateParamsEditor/index.tsx
@@ -37,7 +37,7 @@ const StyledConfigEditor = styled(ConfigEditor)`
 export type TemplateParamsEditorProps = {
   queryEditorId: string;
   language: 'yaml' | 'json';
-  onChange: () => void;
+  onChange: (params: any) => void;
 };
 
 const TemplateParamsEditor = ({
diff --git a/superset-frontend/src/SqlLab/constants.ts b/superset-frontend/src/SqlLab/constants.ts
index 03f725c09d..5a944663b8 100644
--- a/superset-frontend/src/SqlLab/constants.ts
+++ b/superset-frontend/src/SqlLab/constants.ts
@@ -17,8 +17,9 @@
  * under the License.
  */
 import { t } from '@superset-ui/core';
+import type { Type } from 'src/components/Label';
 
-export const STATE_TYPE_MAP = {
+export const STATE_TYPE_MAP: Record<string, Type> = {
   offline: 'danger',
   failed: 'danger',
   pending: 'info',
diff --git a/superset-frontend/src/SqlLab/types.ts b/superset-frontend/src/SqlLab/types.ts
index d8a78a858d..5ecd69293c 100644
--- a/superset-frontend/src/SqlLab/types.ts
+++ b/superset-frontend/src/SqlLab/types.ts
@@ -46,6 +46,8 @@ export interface QueryEditor {
   description?: string;
   loaded?: boolean;
   inLocalStorage?: boolean;
+  northPercent?: number;
+  southPercent?: number;
 }
 
 export type toastState = {
diff --git a/superset-frontend/src/components/DatabaseSelector/index.tsx b/superset-frontend/src/components/DatabaseSelector/index.tsx
index 8516017b20..d17489a9c2 100644
--- a/superset-frontend/src/components/DatabaseSelector/index.tsx
+++ b/superset-frontend/src/components/DatabaseSelector/index.tsx
@@ -74,13 +74,13 @@ type DatabaseValue = {
   value: number;
   id: number;
   database_name: string;
-  backend: string;
+  backend?: string;
 };
 
 export type DatabaseObject = {
   id: number;
   database_name: string;
-  backend: string;
+  backend?: string;
 };
 
 export interface DatabaseSelectorProps {
@@ -102,11 +102,11 @@ const SelectLabel = ({
   backend,
   databaseName,
 }: {
-  backend: string;
+  backend?: string;
   databaseName: string;
 }) => (
   <LabelStyle>
-    <Label className="backend">{backend}</Label>
+    <Label className="backend">{backend || ''}</Label>
     <span className="name" title={databaseName}>
       {databaseName}
     </span>
diff --git a/superset-frontend/src/features/databases/DatabaseModal/index.test.tsx b/superset-frontend/src/features/databases/DatabaseModal/index.test.tsx
index 385b771efe..bcd9fbe694 100644
--- a/superset-frontend/src/features/databases/DatabaseModal/index.test.tsx
+++ b/superset-frontend/src/features/databases/DatabaseModal/index.test.tsx
@@ -302,6 +302,7 @@ fetchMock.post(VALIDATE_PARAMS_ENDPOINT, {
 });
 
 const databaseFixture: DatabaseObject = {
+  id: 123,
   backend: 'postgres',
   configuration_method: CONFIGURATION_METHOD.DYNAMIC_FORM,
   database_name: 'Postgres',
@@ -2012,6 +2013,7 @@ describe('dbReducer', () => {
     const currentState = dbReducer({}, action);
 
     expect(currentState).toEqual({
+      id: db.id,
       database_name: db.database_name,
       engine: backend,
       configuration_method: db.configuration_method,
diff --git a/superset-frontend/src/features/databases/types.ts b/superset-frontend/src/features/databases/types.ts
index e7089425bc..e138a91436 100644
--- a/superset-frontend/src/features/databases/types.ts
+++ b/superset-frontend/src/features/databases/types.ts
@@ -47,7 +47,7 @@ export type DatabaseObject = {
   driver: string;
   engine?: string;
   extra?: string;
-  id?: number;
+  id: number;
   uuid?: null | string;
   name: string; // synonym to database_name
   paramProperties?: Record<string, any>;
@@ -78,11 +78,14 @@ export type DatabaseObject = {
   allow_run_async?: boolean;
 
   // SQL Lab
+  allows_cost_estimate?: boolean;
   allow_ctas?: boolean;
   allow_cvas?: boolean;
   allow_dml?: boolean;
+  allows_virtual_table_explore?: boolean;
   expose_in_sqllab?: boolean;
   force_ctas_schema?: string;
+  extra_json?: ExtraJson;
 
   // Security
   allow_file_upload?: boolean;