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

hugh 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 1d5c58d  refactor: SouthPane into functional component (#13676)
1d5c58d is described below

commit 1d5c58d005646aa4495223bcd49701b14dfae99a
Author: AAfghahi <[email protected]>
AuthorDate: Wed Mar 24 23:00:12 2021 -0400

    refactor: SouthPane into functional component (#13676)
---
 .../spec/javascripts/sqllab/SouthPane_spec.jsx     | 100 +++++-----
 .../spec/javascripts/sqllab/SqlEditor_spec.jsx     |   2 +-
 .../src/SqlLab/components/ResultSet.tsx            |   1 +
 .../src/SqlLab/components/SouthPane.jsx            | 211 ---------------------
 .../src/SqlLab/components/SouthPane/SouthPane.tsx  | 187 ++++++++++++++++++
 .../src/SqlLab/components/SouthPane/state.ts       |  38 ++++
 .../src/SqlLab/components/SqlEditor.jsx            |   2 +-
 7 files changed, 279 insertions(+), 262 deletions(-)

diff --git a/superset-frontend/spec/javascripts/sqllab/SouthPane_spec.jsx 
b/superset-frontend/spec/javascripts/sqllab/SouthPane_spec.jsx
index b7c34c0..dbc7379 100644
--- a/superset-frontend/spec/javascripts/sqllab/SouthPane_spec.jsx
+++ b/superset-frontend/spec/javascripts/sqllab/SouthPane_spec.jsx
@@ -20,61 +20,62 @@ import React from 'react';
 import configureStore from 'redux-mock-store';
 import thunk from 'redux-thunk';
 import { styledShallow as shallow } from 'spec/helpers/theming';
-import SouthPaneContainer from 'src/SqlLab/components/SouthPane';
+import SouthPaneContainer from 'src/SqlLab/components/SouthPane/state';
 import ResultSet from 'src/SqlLab/components/ResultSet';
+import '@testing-library/jest-dom/extend-expect';
 import { STATUS_OPTIONS } from 'src/SqlLab/constants';
 import { initialState } from './fixtures';
 
-describe('SouthPane', () => {
-  const middlewares = [thunk];
-  const mockStore = configureStore(middlewares);
-  const store = mockStore(initialState);
+const mockedProps = {
+  editorQueries: [
+    {
+      cached: false,
+      changedOn: Date.now(),
+      db: 'main',
+      dbId: 1,
+      id: 'LCly_kkIN',
+      startDttm: Date.now(),
+    },
+    {
+      cached: false,
+      changedOn: 1559238500401,
+      db: 'main',
+      dbId: 1,
+      id: 'lXJa7F9_r',
+      startDttm: 1559238500401,
+    },
+    {
+      cached: false,
+      changedOn: 1559238506925,
+      db: 'main',
+      dbId: 1,
+      id: '2g2_iRFMl',
+      startDttm: 1559238506925,
+    },
+    {
+      cached: false,
+      changedOn: 1559238516395,
+      db: 'main',
+      dbId: 1,
+      id: 'erWdqEWPm',
+      startDttm: 1559238516395,
+    },
+  ],
+  latestQueryId: 'LCly_kkIN',
+  dataPreviewQueries: [],
+  actions: {},
+  activeSouthPaneTab: '',
+  height: 1,
+  displayLimit: 1,
+  databases: {},
+  offline: false,
+};
 
-  const mockedProps = {
-    editorQueries: [
-      {
-        cached: false,
-        changedOn: Date.now(),
-        db: 'main',
-        dbId: 1,
-        id: 'LCly_kkIN',
-        startDttm: Date.now(),
-      },
-      {
-        cached: false,
-        changedOn: 1559238500401,
-        db: 'main',
-        dbId: 1,
-        id: 'lXJa7F9_r',
-        startDttm: 1559238500401,
-      },
-      {
-        cached: false,
-        changedOn: 1559238506925,
-        db: 'main',
-        dbId: 1,
-        id: '2g2_iRFMl',
-        startDttm: 1559238506925,
-      },
-      {
-        cached: false,
-        changedOn: 1559238516395,
-        db: 'main',
-        dbId: 1,
-        id: 'erWdqEWPm',
-        startDttm: 1559238516395,
-      },
-    ],
-    latestQueryId: 'LCly_kkIN',
-    dataPreviewQueries: [],
-    actions: {},
-    activeSouthPaneTab: '',
-    height: 1,
-    displayLimit: 1,
-    databases: {},
-    offline: false,
-  };
+const middlewares = [thunk];
+const mockStore = configureStore(middlewares);
+const store = mockStore(initialState);
 
+describe('SouthPane', () => {
   const getWrapper = () =>
     shallow(<SouthPaneContainer store={store} {...mockedProps} />).dive();
 
@@ -85,6 +86,7 @@ describe('SouthPane', () => {
     wrapper.setProps({ offline: true });
     expect(wrapper.childAt(0).text()).toBe(STATUS_OPTIONS.offline);
   });
+
   it('should pass latest query down to ResultSet component', () => {
     wrapper = getWrapper().dive();
     expect(wrapper.find(ResultSet)).toExist();
diff --git a/superset-frontend/spec/javascripts/sqllab/SqlEditor_spec.jsx 
b/superset-frontend/spec/javascripts/sqllab/SqlEditor_spec.jsx
index cce5e1b..b634d2c 100644
--- a/superset-frontend/spec/javascripts/sqllab/SqlEditor_spec.jsx
+++ b/superset-frontend/spec/javascripts/sqllab/SqlEditor_spec.jsx
@@ -29,7 +29,7 @@ import {
   SQL_TOOLBAR_HEIGHT,
 } from 'src/SqlLab/constants';
 import AceEditorWrapper from 'src/SqlLab/components/AceEditorWrapper';
-import ConnectedSouthPane from 'src/SqlLab/components/SouthPane';
+import ConnectedSouthPane from 'src/SqlLab/components/SouthPane/state';
 import SqlEditor from 'src/SqlLab/components/SqlEditor';
 import SqlEditorLeftBar from 'src/SqlLab/components/SqlEditorLeftBar';
 import { Dropdown } from 'src/common/components';
diff --git a/superset-frontend/src/SqlLab/components/ResultSet.tsx 
b/superset-frontend/src/SqlLab/components/ResultSet.tsx
index a33fcd1..04a455c 100644
--- a/superset-frontend/src/SqlLab/components/ResultSet.tsx
+++ b/superset-frontend/src/SqlLab/components/ResultSet.tsx
@@ -65,6 +65,7 @@ interface DatasetOptionAutocomplete {
 }
 
 interface ResultSetProps {
+  showControls?: boolean;
   actions: Record<string, any>;
   cache?: boolean;
   csv?: boolean;
diff --git a/superset-frontend/src/SqlLab/components/SouthPane.jsx 
b/superset-frontend/src/SqlLab/components/SouthPane.jsx
deleted file mode 100644
index 3d99d17..0000000
--- a/superset-frontend/src/SqlLab/components/SouthPane.jsx
+++ /dev/null
@@ -1,211 +0,0 @@
-/**
- * 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 PropTypes from 'prop-types';
-import shortid from 'shortid';
-import Alert from 'src/components/Alert';
-import Tabs from 'src/common/components/Tabs';
-import { connect } from 'react-redux';
-import { bindActionCreators } from 'redux';
-import { t, styled } from '@superset-ui/core';
-
-import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
-
-import Label from 'src/components/Label';
-import * as Actions from '../actions/sqlLab';
-import QueryHistory from './QueryHistory';
-import ResultSet from './ResultSet';
-import {
-  STATUS_OPTIONS,
-  STATE_TYPE_MAP,
-  LOCALSTORAGE_MAX_QUERY_AGE_MS,
-} from '../constants';
-
-const TAB_HEIGHT = 64;
-
-/*
-    editorQueries are queries executed by users passed from SqlEditor component
-    dataPrebiewQueries are all queries executed for preview of table data 
(from SqlEditorLeft)
-*/
-const propTypes = {
-  editorQueries: PropTypes.array.isRequired,
-  latestQueryId: PropTypes.string,
-  dataPreviewQueries: PropTypes.array.isRequired,
-  actions: PropTypes.object.isRequired,
-  activeSouthPaneTab: PropTypes.string,
-  height: PropTypes.number,
-  databases: PropTypes.object.isRequired,
-  offline: PropTypes.bool,
-  displayLimit: PropTypes.number.isRequired,
-};
-
-const defaultProps = {
-  activeSouthPaneTab: 'Results',
-  offline: false,
-};
-
-const StyledPane = styled.div`
-  width: 100%;
-
-  .ant-tabs .ant-tabs-content-holder {
-    overflow: visible;
-  }
-  .SouthPaneTabs {
-    height: 100%;
-    display: flex;
-    flex-direction: column;
-  }
-  .tab-content {
-    overflow: hidden;
-    .alert {
-      margin-top: ${({ theme }) => theme.gridUnit * 2}px;
-    }
-
-    button.fetch {
-      margin-top: ${({ theme }) => theme.gridUnit * 2}px;
-    }
-  }
-`;
-
-export class SouthPane extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this.southPaneRef = React.createRef();
-    this.switchTab = this.switchTab.bind(this);
-  }
-
-  switchTab(id) {
-    this.props.actions.setActiveSouthPaneTab(id);
-  }
-
-  render() {
-    if (this.props.offline) {
-      return (
-        <Label className="m-r-3" type={STATE_TYPE_MAP[STATUS_OPTIONS.offline]}>
-          {STATUS_OPTIONS.offline}
-        </Label>
-      );
-    }
-    const innerTabContentHeight = this.props.height - TAB_HEIGHT;
-    let latestQuery;
-    const { props } = this;
-    if (props.editorQueries.length > 0) {
-      // get the latest query
-      latestQuery = props.editorQueries.find(
-        q => q.id === this.props.latestQueryId,
-      );
-    }
-    let results;
-    if (latestQuery) {
-      if (
-        isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) &&
-        latestQuery.state === 'success' &&
-        !latestQuery.resultsKey &&
-        !latestQuery.results
-      ) {
-        results = (
-          <Alert
-            type="warning"
-            message={t(
-              'No stored results found, you need to re-run your query',
-            )}
-          />
-        );
-      } else if (
-        Date.now() - latestQuery.startDttm <=
-        LOCALSTORAGE_MAX_QUERY_AGE_MS
-      ) {
-        results = (
-          <ResultSet
-            showControls
-            search
-            query={latestQuery}
-            actions={props.actions}
-            height={innerTabContentHeight}
-            database={this.props.databases[latestQuery.dbId]}
-            displayLimit={this.props.displayLimit}
-          />
-        );
-      }
-    } else {
-      results = (
-        <Alert type="info" message={t('Run a query to display results here')} 
/>
-      );
-    }
-    const dataPreviewTabs = props.dataPreviewQueries.map(query => (
-      <Tabs.TabPane
-        tab={t('Preview: `%s`', decodeURIComponent(query.tableName))}
-        key={query.id}
-      >
-        <ResultSet
-          query={query}
-          visualize={false}
-          csv={false}
-          actions={props.actions}
-          cache
-          height={innerTabContentHeight}
-          displayLimit={this.props.displayLimit}
-        />
-      </Tabs.TabPane>
-    ));
-
-    return (
-      <StyledPane className="SouthPane" ref={this.southPaneRef}>
-        <Tabs
-          activeKey={this.props.activeSouthPaneTab}
-          className="SouthPaneTabs"
-          onChange={this.switchTab}
-          id={shortid.generate()}
-          fullWidth={false}
-        >
-          <Tabs.TabPane tab={t('Results')} key="Results">
-            {results}
-          </Tabs.TabPane>
-          <Tabs.TabPane tab={t('Query history')} key="History">
-            <QueryHistory
-              queries={props.editorQueries}
-              actions={props.actions}
-              displayLimit={props.displayLimit}
-            />
-          </Tabs.TabPane>
-          {dataPreviewTabs}
-        </Tabs>
-      </StyledPane>
-    );
-  }
-}
-
-function mapStateToProps({ sqlLab }) {
-  return {
-    activeSouthPaneTab: sqlLab.activeSouthPaneTab,
-    databases: sqlLab.databases,
-    offline: sqlLab.offline,
-  };
-}
-
-function mapDispatchToProps(dispatch) {
-  return {
-    actions: bindActionCreators(Actions, dispatch),
-  };
-}
-
-SouthPane.propTypes = propTypes;
-SouthPane.defaultProps = defaultProps;
-
-export default connect(mapStateToProps, mapDispatchToProps)(SouthPane);
diff --git a/superset-frontend/src/SqlLab/components/SouthPane/SouthPane.tsx 
b/superset-frontend/src/SqlLab/components/SouthPane/SouthPane.tsx
new file mode 100644
index 0000000..10d9aca
--- /dev/null
+++ b/superset-frontend/src/SqlLab/components/SouthPane/SouthPane.tsx
@@ -0,0 +1,187 @@
+/**
+ * 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, { createRef } from 'react';
+import shortid from 'shortid';
+import Alert from 'src/components/Alert';
+import Tabs from 'src/common/components/Tabs';
+import { t, styled } from '@superset-ui/core';
+
+import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
+
+import Label from 'src/components/Label';
+import QueryHistory from '../QueryHistory';
+import ResultSet from '../ResultSet';
+import {
+  STATUS_OPTIONS,
+  STATE_TYPE_MAP,
+  LOCALSTORAGE_MAX_QUERY_AGE_MS,
+} from '../../constants';
+
+const TAB_HEIGHT = 64;
+
+/*
+    editorQueries are queries executed by users passed from SqlEditor component
+    dataPrebiewQueries are all queries executed for preview of table data 
(from SqlEditorLeft)
+*/
+interface SouthPanePropTypes {
+  editorQueries: any[];
+  latestQueryId?: string;
+  dataPreviewQueries: any[];
+  actions: Record<string, Function>;
+  activeSouthPaneTab?: string;
+  height: number;
+  databases: Record<string, any>;
+  offline?: boolean;
+  displayLimit: number;
+}
+
+const StyledPane = styled.div`
+  width: 100%;
+
+  .ant-tabs .ant-tabs-content-holder {
+    overflow: visible;
+  }
+  .SouthPaneTabs {
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+  }
+  .tab-content {
+    overflow: hidden;
+    .alert {
+      margin-top: ${({ theme }) => theme.gridUnit * 2}px;
+    }
+
+    button.fetch {
+      margin-top: ${({ theme }) => theme.gridUnit * 2}px;
+    }
+  }
+`;
+
+export default function SouthPane({
+  editorQueries,
+  latestQueryId,
+  dataPreviewQueries,
+  actions,
+  activeSouthPaneTab = 'Results',
+  height,
+  databases,
+  offline = false,
+  displayLimit,
+}: SouthPanePropTypes) {
+  const innerTabContentHeight = height - TAB_HEIGHT;
+  const southPaneRef = createRef<HTMLDivElement>();
+  const switchTab = (id: string) => {
+    actions.setActiveSouthPaneTab(id);
+  };
+
+  const renderOfflineStatus = () => (
+    <Label className="m-r-3" type={STATE_TYPE_MAP[STATUS_OPTIONS.offline]}>
+      {STATUS_OPTIONS.offline}
+    </Label>
+  );
+
+  const renderResults = () => {
+    let latestQuery;
+    if (editorQueries.length > 0) {
+      // get the latest query
+      latestQuery = editorQueries.find(({ id }) => id === latestQueryId);
+    }
+    let results;
+    if (latestQuery) {
+      if (
+        isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE) &&
+        latestQuery.state === 'success' &&
+        !latestQuery.resultsKey &&
+        !latestQuery.results
+      ) {
+        results = (
+          <Alert
+            type="warning"
+            message={t(
+              'No stored results found, you need to re-run your query',
+            )}
+          />
+        );
+        return results;
+      }
+      if (Date.now() - latestQuery.startDttm <= LOCALSTORAGE_MAX_QUERY_AGE_MS) 
{
+        results = (
+          <ResultSet
+            showControls
+            search
+            query={latestQuery}
+            actions={actions}
+            height={innerTabContentHeight}
+            database={databases[latestQuery.dbId]}
+            displayLimit={displayLimit}
+          />
+        );
+      }
+    } else {
+      results = (
+        <Alert type="info" message={t('Run a query to display results here')} 
/>
+      );
+    }
+    return results;
+  };
+
+  const renderDataPreviewTabs = () =>
+    dataPreviewQueries.map(query => (
+      <Tabs.TabPane
+        tab={t('Preview: `%s`', decodeURIComponent(query.tableName))}
+        key={query.id}
+      >
+        <ResultSet
+          query={query}
+          visualize={false}
+          csv={false}
+          actions={actions}
+          cache
+          height={innerTabContentHeight}
+          displayLimit={displayLimit}
+        />
+      </Tabs.TabPane>
+    ));
+  return offline ? (
+    renderOfflineStatus()
+  ) : (
+    <StyledPane className="SouthPane" ref={southPaneRef}>
+      <Tabs
+        activeKey={activeSouthPaneTab}
+        className="SouthPaneTabs"
+        onChange={switchTab}
+        id={shortid.generate()}
+        fullWidth={false}
+      >
+        <Tabs.TabPane tab={t('Results')} key="Results">
+          {renderResults()}
+        </Tabs.TabPane>
+        <Tabs.TabPane tab={t('Query history')} key="History">
+          <QueryHistory
+            queries={editorQueries}
+            actions={actions}
+            displayLimit={displayLimit}
+          />
+        </Tabs.TabPane>
+        {renderDataPreviewTabs()}
+      </Tabs>
+    </StyledPane>
+  );
+}
diff --git a/superset-frontend/src/SqlLab/components/SouthPane/state.ts 
b/superset-frontend/src/SqlLab/components/SouthPane/state.ts
new file mode 100644
index 0000000..075cd59
--- /dev/null
+++ b/superset-frontend/src/SqlLab/components/SouthPane/state.ts
@@ -0,0 +1,38 @@
+/**
+ * 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 { connect } from 'react-redux';
+import { bindActionCreators, Dispatch } from 'redux';
+import * as Actions from '../../actions/sqlLab';
+import SouthPane from './SouthPane';
+
+function mapStateToProps({ sqlLab }: Record<string, any>) {
+  return {
+    activeSouthPaneTab: sqlLab.activeSouthPaneTab,
+    databases: sqlLab.databases,
+    offline: sqlLab.offline,
+  };
+}
+
+function mapDispatchToProps(dispatch: Dispatch) {
+  return {
+    actions: bindActionCreators<any, any>(Actions, dispatch),
+  };
+}
+
+export default connect<any>(mapStateToProps, mapDispatchToProps)(SouthPane);
diff --git a/superset-frontend/src/SqlLab/components/SqlEditor.jsx 
b/superset-frontend/src/SqlLab/components/SqlEditor.jsx
index 79153f9..1db78b1 100644
--- a/superset-frontend/src/SqlLab/components/SqlEditor.jsx
+++ b/superset-frontend/src/SqlLab/components/SqlEditor.jsx
@@ -60,7 +60,7 @@ import {
 } from '../actions/sqlLab';
 
 import TemplateParamsEditor from './TemplateParamsEditor';
-import ConnectedSouthPane from './SouthPane';
+import ConnectedSouthPane from './SouthPane/state';
 import SaveQuery from './SaveQuery';
 import ScheduleQueryButton from './ScheduleQueryButton';
 import EstimateQueryCostButton from './EstimateQueryCostButton';

Reply via email to