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 ff665fa  feat: restyle database modal (#14092)
ff665fa is described below

commit ff665fa5a7552d0e00df213adaf5f039f0ab6b23
Author: Elizabeth Thompson <[email protected]>
AuthorDate: Tue Apr 20 15:21:07 2021 -0700

    feat: restyle database modal (#14092)
    
    * restyle database modal
    
    * change name of tab to Basic
    
    * update test with RTL better RTL render statement
    
    * change color and position of required asterisk
    
    * refactor db logic
---
 .../CRUD/data/database/DatabaseModal_spec.jsx      | 112 +--
 .../src/views/CRUD/data/database/DatabaseModal.tsx | 801 ++++++++++-----------
 .../src/views/CRUD/data/database/styles.ts         | 203 ++++++
 3 files changed, 625 insertions(+), 491 deletions(-)

diff --git 
a/superset-frontend/spec/javascripts/views/CRUD/data/database/DatabaseModal_spec.jsx
 
b/superset-frontend/spec/javascripts/views/CRUD/data/database/DatabaseModal_spec.jsx
index 9e200a5..89116a5 100644
--- 
a/superset-frontend/spec/javascripts/views/CRUD/data/database/DatabaseModal_spec.jsx
+++ 
b/superset-frontend/spec/javascripts/views/CRUD/data/database/DatabaseModal_spec.jsx
@@ -23,7 +23,6 @@ import * as redux from 'react-redux';
 import { styledMount as mount } from 'spec/helpers/theming';
 import { render, screen } from 'spec/helpers/testing-library';
 import userEvent from '@testing-library/user-event';
-import { supersetTheme, ThemeProvider } from '@superset-ui/core';
 import { Provider } from 'react-redux';
 import DatabaseModal from 'src/views/CRUD/data/database/DatabaseModal';
 import Modal from 'src/components/Modal';
@@ -87,9 +86,9 @@ describe('DatabaseModal', () => {
     it('renders a Tabs menu', () => {
       expect(wrapper.find(Tabs)).toExist();
     });
-    it('renders five TabPanes', () => {
-      expect(wrapper.find(Tabs.TabPane)).toExist();
-      expect(wrapper.find(Tabs.TabPane)).toHaveLength(5);
+    it('renders two TabPanes', () => {
+      expect(wrapper.find('.ant-tabs-tab')).toExist();
+      expect(wrapper.find('.ant-tabs-tab')).toHaveLength(2);
     });
     it('renders input elements for Connection section', () => {
       expect(wrapper.find('input[name="database_name"]')).toExist();
@@ -101,22 +100,24 @@ describe('DatabaseModal', () => {
     describe('initial load', () => {
       it('hides the forms from the db when not selected', () => {
         render(
-          <ThemeProvider theme={supersetTheme}>
-            <Provider store={store}>
-              <DatabaseModal
-                show
-                database={{
-                  expose_in_sqllab: false,
-                  allow_ctas: false,
-                  allow_cvas: false,
-                }}
-              />
-            </Provider>
-          </ThemeProvider>,
+          <DatabaseModal
+            show
+            database={{
+              expose_in_sqllab: false,
+              allow_ctas: false,
+              allow_cvas: false,
+            }}
+          />,
+          { useRedux: true },
         );
-        // Select SQL Lab settings tab
+        // Select Advanced tab
+        const advancedTab = screen.getByRole('tab', {
+          name: /advanced/i,
+        });
+        userEvent.click(advancedTab);
+        // Select SQL Lab tab
         const sqlLabSettingsTab = screen.getByRole('tab', {
-          name: /sql lab settings/i,
+          name: /sql lab/i,
         });
         userEvent.click(sqlLabSettingsTab);
 
@@ -129,21 +130,22 @@ describe('DatabaseModal', () => {
       });
     });
     it('renders all settings when "Expose in SQL Lab" is checked', () => {
-      render(
-        <ThemeProvider theme={supersetTheme}>
-          <Provider store={store}>
-            <DatabaseModal {...dbProps} />
-          </Provider>
-        </ThemeProvider>,
-      );
+      render(<DatabaseModal {...dbProps} />, { useRedux: true });
+
+      // Select Advanced tab
+      const advancedTab = screen.getByRole('tab', {
+        name: /advanced/i,
+      });
+      userEvent.click(advancedTab);
 
-      // Select SQL Lab settings tab
+      // Select SQL Lab tab
       const sqlLabSettingsTab = screen.getByRole('tab', {
-        name: /sql lab settings/i,
+        name: /sql lab/i,
       });
 
       userEvent.click(sqlLabSettingsTab);
-      // Grab all SQL Lab Settings by their labels
+
+      // Grab all SQL Lab settings by their labels
       // const exposeInSqlLab = screen.getByText('Expose in SQL Lab');
       const exposeInSqlLab = screen.getByRole('checkbox', {
         name: /expose in sql lab/i,
@@ -165,17 +167,17 @@ describe('DatabaseModal', () => {
     });
 
     it('renders the schema field when allowCTAS is checked', () => {
-      render(
-        <ThemeProvider theme={supersetTheme}>
-          <Provider store={store}>
-            <DatabaseModal {...dbProps} />
-          </Provider>
-        </ThemeProvider>,
-      );
+      render(<DatabaseModal {...dbProps} />, { useRedux: true });
+
+      // Select Advanced tab
+      const advancedTab = screen.getByRole('tab', {
+        name: /advanced/i,
+      });
+      userEvent.click(advancedTab);
 
-      // Select SQL Lab settings tab
+      // Select SQL Lab tab
       const sqlLabSettingsTab = screen.getByRole('tab', {
-        name: /sql lab settings/i,
+        name: /sql lab/i,
       });
       userEvent.click(sqlLabSettingsTab);
       // Grab CTAS & schema field by their labels
@@ -195,17 +197,17 @@ describe('DatabaseModal', () => {
     });
 
     it('renders the schema field when allowCVAS is checked', () => {
-      render(
-        <ThemeProvider theme={supersetTheme}>
-          <Provider store={store}>
-            <DatabaseModal {...dbProps} />
-          </Provider>
-        </ThemeProvider>,
-      );
+      render(<DatabaseModal {...dbProps} />, { useRedux: true });
+
+      // Select Advanced tab
+      const advancedTab = screen.getByRole('tab', {
+        name: /advanced/i,
+      });
+      userEvent.click(advancedTab);
 
-      // Select SQL Lab settings tab
+      // Select SQL Lab tab
       const sqlLabSettingsTab = screen.getByRole('tab', {
-        name: /sql lab settings/i,
+        name: /sql lab/i,
       });
       userEvent.click(sqlLabSettingsTab);
       // Grab CVAS by it's label & schema field
@@ -225,17 +227,17 @@ describe('DatabaseModal', () => {
     });
 
     it('renders the schema field when both allowCTAS and allowCVAS are 
checked', () => {
-      render(
-        <ThemeProvider theme={supersetTheme}>
-          <Provider store={store}>
-            <DatabaseModal {...dbProps} />
-          </Provider>
-        </ThemeProvider>,
-      );
+      render(<DatabaseModal {...dbProps} />, { useRedux: true });
+
+      // Select Advanced tab
+      const advancedTab = screen.getByRole('tab', {
+        name: /advanced/i,
+      });
+      userEvent.click(advancedTab);
 
-      // Select SQL Lab settings tab
+      // Select SQL Lab tab
       const sqlLabSettingsTab = screen.getByRole('tab', {
-        name: /sql lab settings/i,
+        name: /sql lab/i,
       });
       userEvent.click(sqlLabSettingsTab);
       // Grab CTAS and CVAS by their labels, & schema field
diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx 
b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx
index 105b77e..693b87f 100644
--- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx
+++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx
@@ -17,21 +17,27 @@
  * under the License.
  */
 import React, { FunctionComponent, useState, useEffect } from 'react';
-import { styled, t } from '@superset-ui/core';
+import cx from 'classnames';
 import InfoTooltip from 'src/components/InfoTooltip';
+import { t, supersetTheme } from '@superset-ui/core';
 import {
   useSingleViewResource,
   testDatabaseConnection,
 } from 'src/views/CRUD/hooks';
 import withToasts from 'src/messageToasts/enhancers/withToasts';
-import Icon from 'src/components/Icon';
-import Modal from 'src/components/Modal';
 import Tabs from 'src/common/components/Tabs';
 import Button from 'src/components/Button';
 import IndeterminateCheckbox from 'src/components/IndeterminateCheckbox';
-import { JsonEditor } from 'src/components/AsyncAceEditor';
+import Collapse from 'src/components/Collapse';
 import { DatabaseObject } from './types';
 import { useCommonConf } from './state';
+import {
+  StyledModal,
+  StyledInputContainer,
+  StyledJsonEditor,
+  StyledExpandableForm,
+  StyledRequiredTab,
+} from './styles';
 
 interface DatabaseModalProps {
   addDangerToast: (msg: string) => void;
@@ -43,118 +49,6 @@ interface DatabaseModalProps {
 }
 
 const DEFAULT_TAB_KEY = '1';
-const EXPOSE_SQLLAB_FORM_HEIGHT = '270px';
-const CTAS_CVAS_SCHEMA_FORM_HEIGHT = '94px';
-
-const StyledIcon = styled(Icon)`
-  margin: auto ${({ theme }) => theme.gridUnit * 2}px auto 0;
-`;
-
-const StyledInputContainer = styled.div`
-  margin-bottom: ${({ theme }) => theme.gridUnit * 2}px;
-
-  &.extra-container {
-    padding-top: 8px;
-  }
-
-  &.expandable {
-    height: 0;
-    overflow: hidden;
-    transition: height 0.25s;
-    margin-left: ${({ theme }) => theme.gridUnit * 8}px;
-    padding: 0;
-    &.open {
-      height: ${CTAS_CVAS_SCHEMA_FORM_HEIGHT};
-    }
-  }
-
-  .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};
-    }
-  }
-
-  .input-container {
-    display: flex;
-    align-items: top;
-
-    label {
-      display: flex;
-      margin-left: ${({ theme }) => theme.gridUnit * 2}px;
-      margin-top: ${({ theme }) => theme.gridUnit * 0.75}px;
-      font-family: ${({ theme }) => theme.typography.families.sansSerif};
-      font-size: ${({ theme }) => theme.typography.sizes.m}px;
-    }
-
-    i {
-      margin: 0 ${({ theme }) => theme.gridUnit}px;
-    }
-  }
-
-  input,
-  textarea {
-    flex: 1 1 auto;
-  }
-
-  textarea {
-    height: 160px;
-    resize: none;
-  }
-
-  input::placeholder,
-  textarea::placeholder {
-    color: ${({ theme }) => theme.colors.grayscale.light1};
-  }
-
-  textarea,
-  input[type='text'],
-  input[type='number'] {
-    padding: ${({ theme }) => theme.gridUnit * 1.5}px
-      ${({ theme }) => theme.gridUnit * 2}px;
-    border-style: none;
-    border: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
-    border-radius: ${({ theme }) => theme.gridUnit}px;
-
-    &[name='name'] {
-      flex: 0 1 auto;
-      width: 40%;
-    }
-
-    &[name='sqlalchemy_uri'] {
-      margin-right: ${({ theme }) => theme.gridUnit * 3}px;
-    }
-  }
-`;
-
-const StyledJsonEditor = styled(JsonEditor)`
-  flex: 1 1 auto;
-  border: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
-  border-radius: ${({ theme }) => theme.gridUnit}px;
-`;
-
-const StyledExpandableForm = styled.div`
-  padding-top: ${({ theme }) => theme.gridUnit}px;
-  .input-container {
-    padding-top: ${({ theme }) => theme.gridUnit}px;
-    padding-bottom: ${({ theme }) => theme.gridUnit}px;
-  }
-  &.expandable {
-    height: 0;
-    overflow: hidden;
-    transition: height 0.25s;
-    margin-left: ${({ theme }) => theme.gridUnit * 7}px;
-    &.open {
-      height: ${EXPOSE_SQLLAB_FORM_HEIGHT};
-    }
-  }
-`;
 
 const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
   addDangerToast,
@@ -196,14 +90,11 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> 
= ({
 
     const connection = {
       sqlalchemy_uri: db ? db.sqlalchemy_uri : '',
-      database_name:
-        db && db.database_name.trim().length
-          ? db.database_name.trim()
-          : undefined,
-      impersonate_user: db ? db.impersonate_user || undefined : undefined,
-      extra: db && db.extra && db.extra.length ? db.extra : undefined,
-      encrypted_extra: db ? db.encrypted_extra || undefined : undefined,
-      server_cert: db ? db.server_cert || undefined : undefined,
+      database_name: db?.database_name?.trim() || undefined,
+      impersonate_user: db?.impersonate_user || undefined,
+      extra: db?.extra || undefined,
+      encrypted_extra: db?.encrypted_extra || undefined,
+      server_cert: db?.server_cert || undefined,
     };
 
     testDatabaseConnection(connection, addDangerToast, addSuccessToast);
@@ -219,8 +110,8 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> 
= ({
     if (isEditMode) {
       // Edit
       const update: DatabaseObject = {
-        database_name: db ? db.database_name.trim() : '',
-        sqlalchemy_uri: db ? db.sqlalchemy_uri : '',
+        database_name: db?.database_name.trim() || '',
+        sqlalchemy_uri: db?.sqlalchemy_uri || '',
         ...db,
       };
 
@@ -296,12 +187,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> 
= ({
   };
 
   const validate = () => {
-    if (
-      db &&
-      db.database_name.trim().length &&
-      db.sqlalchemy_uri &&
-      db.sqlalchemy_uri.length
-    ) {
+    if (db?.database_name?.trim() && db?.sqlalchemy_uri) {
       setDisableSave(false);
     } else {
       setDisableSave(true);
@@ -356,39 +242,27 @@ const DatabaseModal: 
FunctionComponent<DatabaseModalProps> = ({
   const createAsOpen = !!(db?.allow_ctas || db?.allow_cvas);
 
   return (
-    <Modal
+    <StyledModal
       name="database"
       className="database-modal"
       disablePrimaryButton={disableSave}
+      height="600px"
       onHandledPrimaryAction={onSave}
       onHide={hide}
       primaryButtonName={isEditMode ? t('Save') : t('Add')}
-      width="750px"
+      width="500px"
       show={show}
-      title={
-        <h4>
-          <StyledIcon name="database" />
-          {isEditMode ? t('Edit database') : t('Add database')}
-        </h4>
-      }
+      title={<h4>{isEditMode ? t('Edit database') : t('Add database')}</h4>}
     >
       <Tabs
         defaultActiveKey={DEFAULT_TAB_KEY}
         activeKey={tabKey}
         onTabClick={tabChange}
       >
-        <Tabs.TabPane
-          tab={
-            <span>
-              {t('Connection')}
-              <span className="required">*</span>
-            </span>
-          }
-          key="1"
-        >
+        <StyledRequiredTab tab={<span>{t('Basic')}</span>} key="1">
           <StyledInputContainer>
             <div className="control-label">
-              {t('Database name')}
+              {t('Display Name')}
               <span className="required">*</span>
             </div>
             <div className="input-container">
@@ -400,6 +274,9 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> 
= ({
                 onChange={onInputChange}
               />
             </div>
+            <div className="helper">
+              {t('Pick a name to help you identify this database.')}
+            </div>
           </StyledInputContainer>
           <StyledInputContainer>
             <div className="control-label">
@@ -417,9 +294,6 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> 
= ({
                 )}
                 onChange={onInputChange}
               />
-              <Button buttonStyle="primary" onClick={testConnection} cta>
-                {t('Test connection')}
-              </Button>
             </div>
             <div className="helper">
               {t('Refer to the ')}
@@ -433,300 +307,355 @@ const DatabaseModal: 
FunctionComponent<DatabaseModalProps> = ({
               {t(' for more information on how to structure your URI.')}
             </div>
           </StyledInputContainer>
-        </Tabs.TabPane>
-        <Tabs.TabPane tab={<span>{t('Performance')}</span>} key="2">
-          <StyledInputContainer>
-            <div className="control-label">{t('Chart cache timeout')}</div>
-            <div className="input-container">
-              <input
-                type="number"
-                name="cache_timeout"
-                value={db?.cache_timeout || ''}
-                placeholder={t('Chart cache timeout')}
-                onChange={onInputChange}
-              />
-            </div>
-            <div className="helper">
-              {t(
-                'Duration (in seconds) of the caching timeout for charts of 
this database.' +
-                  ' A timeout of 0 indicates that the cache never expires.' +
-                  ' Note this defaults to the global timeout if undefined.',
-              )}
-            </div>
-          </StyledInputContainer>
-          <StyledInputContainer>
-            <div className="input-container">
-              <IndeterminateCheckbox
-                id="allow_run_async"
-                indeterminate={false}
-                checked={!!db?.allow_run_async}
-                onChange={onInputChange}
-                labelText={t('Asynchronous query execution')}
-              />
-              <InfoTooltip
-                tooltip={t(
-                  'Operate the database in asynchronous mode, meaning that the 
queries ' +
-                    'are executed on remote workers as opposed to on the web 
server itself. ' +
-                    'This assumes that you have a Celery worker setup as well 
as a results ' +
-                    'backend. Refer to the installation docs for more 
information.',
-                )}
-              />
-            </div>
-          </StyledInputContainer>
-        </Tabs.TabPane>
-        <Tabs.TabPane tab={<span>{t('SQL Lab settings')}</span>} key="3">
-          <StyledInputContainer>
-            <StyledInputContainer>
-              <div className="input-container">
-                <IndeterminateCheckbox
-                  id="expose_in_sqllab"
-                  indeterminate={false}
-                  checked={!!db?.expose_in_sqllab}
-                  onChange={onInputChange}
-                  labelText={t('Expose in SQL Lab')}
-                />
-                <InfoTooltip
-                  tooltip={t('Allow this database to be queried in SQL Lab')}
-                />
-              </div>
-              <StyledExpandableForm
-                className={`expandable ${expandableModalIsOpen ? 'open' : ''}`}
-              >
-                <StyledInputContainer>
-                  <div className="input-container">
-                    <IndeterminateCheckbox
-                      id="allow_ctas"
-                      indeterminate={false}
-                      checked={!!db?.allow_ctas}
-                      onChange={onInputChange}
-                      labelText={t('Allow CREATE TABLE AS')}
-                    />
-                    <InfoTooltip
-                      tooltip={t(
-                        'Allow creation of new tables based on queries',
-                      )}
-                    />
-                  </div>
-                </StyledInputContainer>
-                <StyledInputContainer>
-                  <div className="input-container">
-                    <IndeterminateCheckbox
-                      id="allow_cvas"
-                      indeterminate={false}
-                      checked={!!db?.allow_cvas}
-                      onChange={onInputChange}
-                      labelText={t('Allow CREATE VIEW AS')}
-                    />
-                    <InfoTooltip
-                      tooltip={t(
-                        'Allow creation of new views based on queries',
-                      )}
-                    />
-                  </div>
-                  <StyledInputContainer
-                    className={`expandable ${createAsOpen ? 'open' : ''}`}
-                  >
-                    <div className="control-label">
-                      {t('CTAS & CVAS SCHEMA')}
+          <Button
+            onClick={testConnection}
+            cta
+            buttonStyle="link"
+            style={{
+              width: '100%',
+              border: `1px solid ${supersetTheme.colors.primary.base}`,
+            }}
+          >
+            {t('Test connection')}
+          </Button>
+        </StyledRequiredTab>
+        <Tabs.TabPane tab={<span>{t('Advanced')}</span>} key="2">
+          <Collapse expandIconPosition="right" accordion>
+            <Collapse.Panel
+              header={
+                <div>
+                  <h4>SQL Lab</h4>
+                  <p className="helper">
+                    Configure how this database will function in SQL Lab.
+                  </p>
+                </div>
+              }
+              key="1"
+            >
+              <StyledInputContainer className="mb-0">
+                <div className="input-container">
+                  <IndeterminateCheckbox
+                    id="expose_in_sqllab"
+                    indeterminate={false}
+                    checked={!!db?.expose_in_sqllab}
+                    onChange={onInputChange}
+                    labelText={t('Expose in SQL Lab')}
+                  />
+                  <InfoTooltip
+                    tooltip={t('Allow this database to be queried in SQL Lab')}
+                  />
+                </div>
+                <StyledExpandableForm
+                  className={cx('expandable', {
+                    open: expandableModalIsOpen,
+                    'ctas-open': createAsOpen,
+                  })}
+                >
+                  <StyledInputContainer className="mb-0">
+                    <div className="input-container">
+                      <IndeterminateCheckbox
+                        id="allow_ctas"
+                        indeterminate={false}
+                        checked={!!db?.allow_ctas}
+                        onChange={onInputChange}
+                        labelText={t('Allow CREATE TABLE AS')}
+                      />
+                      <InfoTooltip
+                        tooltip={t(
+                          'Allow creation of new tables based on queries',
+                        )}
+                      />
                     </div>
+                  </StyledInputContainer>
+                  <StyledInputContainer className="mb-0">
                     <div className="input-container">
-                      <input
-                        type="text"
-                        name="force_ctas_schema"
-                        value={db?.force_ctas_schema || ''}
-                        placeholder={t('Search or select schema')}
+                      <IndeterminateCheckbox
+                        id="allow_cvas"
+                        indeterminate={false}
+                        checked={!!db?.allow_cvas}
                         onChange={onInputChange}
+                        labelText={t('Allow CREATE VIEW AS')}
+                      />
+                      <InfoTooltip
+                        tooltip={t(
+                          'Allow creation of new views based on queries',
+                        )}
                       />
                     </div>
-                    <div className="helper">
-                      {t(
-                        'When allowing CREATE TABLE AS option in SQL Lab, this 
option ' +
-                          'forces the table to be created in this schema.',
-                      )}
+                    <StyledInputContainer
+                      className={cx('expandable', { open: createAsOpen })}
+                    >
+                      <div className="control-label">
+                        {t('CTAS & CVAS SCHEMA')}
+                      </div>
+                      <div className="input-container">
+                        <input
+                          type="text"
+                          name="force_ctas_schema"
+                          value={db?.force_ctas_schema || ''}
+                          placeholder={t('Search or select schema')}
+                          onChange={onInputChange}
+                        />
+                      </div>
+                      <div className="helper">
+                        {t(
+                          'When allowing CREATE TABLE AS option in SQL Lab, 
this option ' +
+                            'forces the table to be created in this schema.',
+                        )}
+                      </div>
+                    </StyledInputContainer>
+                  </StyledInputContainer>
+                  <StyledInputContainer className="mb-0">
+                    <div className="input-container">
+                      <IndeterminateCheckbox
+                        id="allow_dml"
+                        indeterminate={false}
+                        checked={!!db?.allow_dml}
+                        onChange={onInputChange}
+                        labelText={t('Allow DML')}
+                      />
+                      <InfoTooltip
+                        tooltip={t(
+                          'Allow manipulation of the database using non-SELECT 
statements such as UPDATE, DELETE, CREATE, etc.',
+                        )}
+                      />
                     </div>
                   </StyledInputContainer>
-                </StyledInputContainer>
-                <StyledInputContainer>
-                  <div className="input-container">
-                    <IndeterminateCheckbox
-                      id="allow_dml"
-                      indeterminate={false}
-                      checked={!!db?.allow_dml}
-                      onChange={onInputChange}
-                      labelText={t('Allow DML')}
-                    />
-                    <InfoTooltip
-                      tooltip={t(
-                        'Allow manipulation of the database using non-SELECT 
statements such as UPDATE, DELETE, CREATE, etc.',
-                      )}
-                    />
+                  <StyledInputContainer>
+                    <div className="input-container">
+                      <IndeterminateCheckbox
+                        id="allow_multi_schema_metadata_fetch"
+                        indeterminate={false}
+                        checked={!!db?.allow_multi_schema_metadata_fetch}
+                        onChange={onInputChange}
+                        labelText={t('Allow multi schema metadata fetch')}
+                      />
+                      <InfoTooltip
+                        tooltip={t(
+                          'Allow SQL Lab to fetch a list of all tables and all 
views across all database ' +
+                            'schemas. For large data warehouse with thousands 
of tables, this can be ' +
+                            'expensive and put strain on the system.',
+                        )}
+                      />
+                    </div>
+                  </StyledInputContainer>
+                </StyledExpandableForm>
+              </StyledInputContainer>
+            </Collapse.Panel>
+            <Collapse.Panel
+              header={
+                <div>
+                  <h4>Performance</h4>
+                  <p className="helper">
+                    Adjust settings that will impact the performance of this
+                    database.
+                  </p>
+                </div>
+              }
+              key="2"
+            >
+              <StyledInputContainer className="mb-8">
+                <div className="control-label">{t('Chart cache timeout')}</div>
+                <div className="input-container">
+                  <input
+                    type="number"
+                    name="cache_timeout"
+                    value={db?.cache_timeout || ''}
+                    placeholder={t('Chart cache timeout')}
+                    onChange={onInputChange}
+                  />
+                </div>
+                <div className="helper">
+                  {t(
+                    'Duration (in seconds) of the caching timeout for charts 
of this database.' +
+                      ' A timeout of 0 indicates that the cache never 
expires.' +
+                      ' Note this defaults to the global timeout if 
undefined.',
+                  )}
+                </div>
+              </StyledInputContainer>
+              <StyledInputContainer className="mb-0">
+                <div className="input-container">
+                  <IndeterminateCheckbox
+                    id="allow_run_async"
+                    indeterminate={false}
+                    checked={!!db?.allow_run_async}
+                    onChange={onInputChange}
+                    labelText={t('Asynchronous query execution')}
+                  />
+                  <InfoTooltip
+                    tooltip={t(
+                      'Operate the database in asynchronous mode, meaning that 
the queries ' +
+                        'are executed on remote workers as opposed to on the 
web server itself. ' +
+                        'This assumes that you have a Celery worker setup as 
well as a results ' +
+                        'backend. Refer to the installation docs for more 
information.',
+                    )}
+                  />
+                </div>
+              </StyledInputContainer>
+            </Collapse.Panel>
+            <Collapse.Panel
+              header={
+                <div>
+                  <h4>Security</h4>
+                  <p className="helper">
+                    Add connection information for other systems.
+                  </p>
+                </div>
+              }
+              key="3"
+            >
+              <StyledInputContainer>
+                <div className="control-label">{t('Secure extra')}</div>
+                <div className="input-container">
+                  <StyledJsonEditor
+                    name="encrypted_extra"
+                    value={db?.encrypted_extra || ''}
+                    placeholder={t('Secure extra')}
+                    onChange={(json: string) =>
+                      onEditorChange(json, 'encrypted_extra')
+                    }
+                    width="100%"
+                    height="160px"
+                  />
+                </div>
+                <div className="helper">
+                  <div>
+                    {t(
+                      'JSON string containing additional connection 
configuration.',
+                    )}
                   </div>
-                </StyledInputContainer>
-                <StyledInputContainer>
-                  <div className="input-container">
-                    <IndeterminateCheckbox
-                      id="allow_multi_schema_metadata_fetch"
-                      indeterminate={false}
-                      checked={!!db?.allow_multi_schema_metadata_fetch}
-                      onChange={onInputChange}
-                      labelText={t('Allow multi schema metadata fetch')}
-                    />
-                    <InfoTooltip
-                      tooltip={t(
-                        'Allow SQL Lab to fetch a list of all tables and all 
views across all database ' +
-                          'schemas. For large data warehouse with thousands of 
tables, this can be ' +
-                          'expensive and put strain on the system.',
-                      )}
-                    />
+                  <div>
+                    {t(
+                      'This is used to provide connection information for 
systems like Hive, ' +
+                        'Presto, and BigQuery, which do not conform to the 
username:password syntax ' +
+                        'normally used by SQLAlchemy.',
+                    )}
                   </div>
-                </StyledInputContainer>
-              </StyledExpandableForm>
-            </StyledInputContainer>
-          </StyledInputContainer>
-        </Tabs.TabPane>
-        <Tabs.TabPane tab={<span>{t('Security')}</span>} key="4">
-          <StyledInputContainer>
-            <div className="control-label">{t('Secure extra')}</div>
-            <div className="input-container">
-              <StyledJsonEditor
-                name="encrypted_extra"
-                value={db?.encrypted_extra || ''}
-                placeholder={t('Secure extra')}
-                onChange={(json: string) =>
-                  onEditorChange(json, 'encrypted_extra')
-                }
-                width="100%"
-                height="160px"
-              />
-            </div>
-            <div className="helper">
-              <div>
-                {t(
-                  'JSON string containing additional connection 
configuration.',
-                )}
-              </div>
-              <div>
-                {t(
-                  'This is used to provide connection information for systems 
like Hive, ' +
-                    'Presto, and BigQuery, which do not conform to the 
username:password syntax ' +
-                    'normally used by SQLAlchemy.',
-                )}
-              </div>
-            </div>
-          </StyledInputContainer>
-          <StyledInputContainer>
-            <div className="control-label">{t('Root certificate')}</div>
-            <div className="input-container">
-              <textarea
-                name="server_cert"
-                value={db?.server_cert || ''}
-                placeholder={t('Root certificate')}
-                onChange={onTextChange}
-              />
-            </div>
-            <div className="helper">
-              {t(
-                'Optional CA_BUNDLE contents to validate HTTPS requests. Only 
available on ' +
-                  'certain database engines.',
-              )}
-            </div>
-          </StyledInputContainer>
-        </Tabs.TabPane>
-        <Tabs.TabPane tab={<span>{t('Extra')}</span>} key="5">
-          <StyledInputContainer>
-            <div className="input-container">
-              <IndeterminateCheckbox
-                id="impersonate_user"
-                indeterminate={false}
-                checked={!!db?.impersonate_user}
-                onChange={onInputChange}
-                labelText={t('Impersonate Logged In User (Presto & Hive)')}
-              />
-              <InfoTooltip
-                tooltip={t(
-                  'If Presto, all the queries in SQL Lab are going to be 
executed as the ' +
-                    'currently logged on user who must have permission to run 
them. If Hive ' +
-                    'and hive.server2.enable.doAs is enabled, will run the 
queries as ' +
-                    'service account, but impersonate the currently logged on 
user via ' +
-                    'hive.server2.proxy.user property.',
-                )}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledInputContainer>
-            <div className="input-container">
-              <IndeterminateCheckbox
-                id="allow_csv_upload"
-                indeterminate={false}
-                checked={!!db?.allow_csv_upload}
-                onChange={onInputChange}
-                labelText={t('Allow data upload')}
-              />
-              <InfoTooltip
-                tooltip={t(
-                  'If selected, please set the schemas allowed for data upload 
in Extra.',
-                )}
-              />
-            </div>
-          </StyledInputContainer>
-          <StyledInputContainer className="extra-container">
-            <div className="control-label">{t('Extra')}</div>
-            <div className="input-container">
-              <StyledJsonEditor
-                name="extra"
-                value={db?.extra ?? defaultExtra}
-                placeholder={t('Secure extra')}
-                onChange={(json: string) => onEditorChange(json, 'extra')}
-                width="100%"
-                height="160px"
-              />
-            </div>
-            <div className="helper">
-              <div>
-                {t('JSON string containing extra configuration elements.')}
-              </div>
-              <div>
-                {t(
-                  '1. The engine_params object gets unpacked into the 
sqlalchemy.create_engine ' +
-                    'call, while the metadata_params gets unpacked into the 
sqlalchemy.MetaData ' +
-                    'call.',
-                )}
-              </div>
-              <div>
-                {t(
-                  '2. The metadata_cache_timeout is a cache timeout setting in 
seconds for ' +
-                    'metadata fetch of this database. Specify it as 
"metadata_cache_timeout": ' +
-                    '{"schema_cache_timeout": 600, "table_cache_timeout": 
600}. If unset, cache ' +
-                    'will not be enabled for the functionality. A timeout of 0 
indicates that ' +
-                    'the cache never expires.',
-                )}
-              </div>
-              <div>
-                {t(
-                  '3. The schemas_allowed_for_csv_upload is a comma separated 
list of schemas ' +
-                    'that CSVs are allowed to upload to. Specify it as ' +
-                    '"schemas_allowed_for_csv_upload": ["public", 
"csv_upload"]. If database ' +
-                    'flavor does not support schema or any schema is allowed 
to be accessed, ' +
-                    'just leave the list empty.',
-                )}
-              </div>
-              <div>
-                {t(
-                  "4. The version field is a string specifying this db's 
version. This " +
-                    'should be used with Presto DBs so that the syntax is 
correct.',
-                )}
-              </div>
-              <div>
-                {t(
-                  '5. The allows_virtual_table_explore field is a boolean 
specifying whether ' +
-                    'or not the Explore button in SQL Lab results is shown.',
-                )}
-              </div>
-            </div>
-          </StyledInputContainer>
+                </div>
+              </StyledInputContainer>
+              <StyledInputContainer>
+                <div className="control-label">{t('Root certificate')}</div>
+                <div className="input-container">
+                  <textarea
+                    name="server_cert"
+                    value={db?.server_cert || ''}
+                    placeholder={t('Root certificate')}
+                    onChange={onTextChange}
+                  />
+                </div>
+                <div className="helper">
+                  {t(
+                    'Optional CA_BUNDLE contents to validate HTTPS requests. 
Only available on ' +
+                      'certain database engines.',
+                  )}
+                </div>
+              </StyledInputContainer>
+            </Collapse.Panel>
+            <Collapse.Panel
+              header={
+                <div>
+                  <h4>Other</h4>
+                  <p className="helper">Additional settings.</p>
+                </div>
+              }
+              key="4"
+            >
+              <StyledInputContainer className="mb-0">
+                <div className="input-container">
+                  <IndeterminateCheckbox
+                    id="impersonate_user"
+                    indeterminate={false}
+                    checked={!!db?.impersonate_user}
+                    onChange={onInputChange}
+                    labelText={t('Impersonate Logged In User (Presto & Hive)')}
+                  />
+                  <InfoTooltip
+                    tooltip={t(
+                      'If Presto, all the queries in SQL Lab are going to be 
executed as the ' +
+                        'currently logged on user who must have permission to 
run them. If Hive ' +
+                        'and hive.server2.enable.doAs is enabled, will run the 
queries as ' +
+                        'service account, but impersonate the currently logged 
on user via ' +
+                        'hive.server2.proxy.user property.',
+                    )}
+                  />
+                </div>
+              </StyledInputContainer>
+              <StyledInputContainer className="mb-0">
+                <div className="input-container">
+                  <IndeterminateCheckbox
+                    id="allow_csv_upload"
+                    indeterminate={false}
+                    checked={!!db?.allow_csv_upload}
+                    onChange={onInputChange}
+                    labelText={t('Allow data upload')}
+                  />
+                  <InfoTooltip
+                    tooltip={t(
+                      'If selected, please set the schemas allowed for data 
upload in Extra.',
+                    )}
+                  />
+                </div>
+              </StyledInputContainer>
+              <StyledInputContainer className="extra-container">
+                <div className="control-label">{t('Extra')}</div>
+                <div className="input-container">
+                  <StyledJsonEditor
+                    name="extra"
+                    value={db?.extra ?? defaultExtra}
+                    placeholder={t('Secure extra')}
+                    onChange={(json: string) => onEditorChange(json, 'extra')}
+                    width="100%"
+                    height="160px"
+                  />
+                </div>
+                <div className="helper">
+                  <div>
+                    {t('JSON string containing extra configuration elements.')}
+                  </div>
+                  <div>
+                    {t(
+                      '1. The engine_params object gets unpacked into the 
sqlalchemy.create_engine ' +
+                        'call, while the metadata_params gets unpacked into 
the sqlalchemy.MetaData ' +
+                        'call.',
+                    )}
+                  </div>
+                  <div>
+                    {t(
+                      '2. The metadata_cache_timeout is a cache timeout 
setting in seconds for ' +
+                        'metadata fetch of this database. Specify it as 
"metadata_cache_timeout": ' +
+                        '{"schema_cache_timeout": 600, "table_cache_timeout": 
600}. If unset, cache ' +
+                        'will not be enabled for the functionality. A timeout 
of 0 indicates that ' +
+                        'the cache never expires.',
+                    )}
+                  </div>
+                  <div>
+                    {t(
+                      '3. The schemas_allowed_for_csv_upload is a comma 
separated list of schemas ' +
+                        'that CSVs are allowed to upload to. Specify it as ' +
+                        '"schemas_allowed_for_csv_upload": ["public", 
"csv_upload"]. If database ' +
+                        'flavor does not support schema or any schema is 
allowed to be accessed, ' +
+                        'just leave the list empty.',
+                    )}
+                  </div>
+                  <div>
+                    {t(
+                      "4. The version field is a string specifying this db's 
version. This " +
+                        'should be used with Presto DBs so that the syntax is 
correct.',
+                    )}
+                  </div>
+                  <div>
+                    {t(
+                      '5. The allows_virtual_table_explore field is a boolean 
specifying whether ' +
+                        'or not the Explore button in SQL Lab results is 
shown.',
+                    )}
+                  </div>
+                </div>
+              </StyledInputContainer>
+            </Collapse.Panel>
+          </Collapse>
         </Tabs.TabPane>
       </Tabs>
-    </Modal>
+    </StyledModal>
   );
 };
 
diff --git a/superset-frontend/src/views/CRUD/data/database/styles.ts 
b/superset-frontend/src/views/CRUD/data/database/styles.ts
new file mode 100644
index 0000000..93ce119
--- /dev/null
+++ b/superset-frontend/src/views/CRUD/data/database/styles.ts
@@ -0,0 +1,203 @@
+/**
+ * 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 { styled } from '@superset-ui/core';
+import Modal from 'src/components/Modal';
+import { JsonEditor } from 'src/components/AsyncAceEditor';
+import Tabs from 'src/common/components/Tabs';
+
+const CTAS_CVAS_SCHEMA_FORM_HEIGHT = 102;
+const EXPOSE_IN_SQLLAB_FORM_HEIGHT = CTAS_CVAS_SCHEMA_FORM_HEIGHT + 52;
+const EXPOSE_ALL_FORM_HEIGHT = EXPOSE_IN_SQLLAB_FORM_HEIGHT + 102;
+
+const anticonHeight = 12;
+
+export const StyledModal = styled(Modal)`
+  .ant-collapse {
+    .ant-collapse-header {
+      padding-top: ${({ theme }) => theme.gridUnit * 3.5}px;
+      padding-bottom: ${({ theme }) => theme.gridUnit * 2.5}px;
+
+      .anticon.ant-collapse-arrow {
+        top: calc(50% - ${anticonHeight / 2}px);
+      }
+      .helper {
+        color: ${({ theme }) => theme.colors.grayscale.base};
+      }
+    }
+    h4 {
+      font-size: 16px;
+      font-weight: bold;
+      margin-top: 0;
+      margin-bottom: ${({ theme }) => theme.gridUnit}px;
+    }
+    p.helper {
+      margin-bottom: 0;
+      padding: 0;
+    }
+  }
+  .ant-modal-header {
+    padding: 18px 16px 16px;
+  }
+  .ant-modal-body {
+    padding-left: 0;
+    padding-right: 0;
+  }
+  .ant-tabs-top > .ant-tabs-nav {
+    margin-bottom: 0;
+  }
+  .ant-modal-close-x .close {
+    color: ${({ theme }) => theme.colors.grayscale.dark1};
+    opacity: 1;
+  }
+
+  .required {
+    margin-left: ${({ theme }) => theme.gridUnit / 2}px;
+    color: ${({ theme }) => theme.colors.error.base};
+  }
+
+  .helper {
+    display: block;
+    padding: ${({ theme }) => theme.gridUnit}px 0;
+    color: ${({ theme }) => theme.colors.grayscale.light1};
+    font-size: ${({ theme }) => theme.typography.sizes.s - 1}px;
+    text-align: left;
+  }
+  .ant-modal-title > h4 {
+    font-weight: bold;
+  }
+`;
+
+export const StyledInputContainer = styled.div`
+  margin-bottom: ${({ theme }) => theme.gridUnit * 6}px;
+  &.mb-0 {
+    margin-bottom: 0;
+  }
+  &.mb-8 {
+    margin-bottom: ${({ theme }) => theme.gridUnit * 2}px;
+  }
+
+  .control-label {
+    color: ${({ theme }) => theme.colors.grayscale.dark1};
+    font-size: ${({ theme }) => theme.typography.sizes.s - 1}px;
+    margin-bottom: ${({ theme }) => theme.gridUnit * 2}px;
+  }
+
+  &.extra-container {
+    padding-top: 8px;
+  }
+
+  .input-container {
+    display: flex;
+    align-items: top;
+
+    label {
+      display: flex;
+      margin-left: ${({ theme }) => theme.gridUnit * 2}px;
+      margin-top: ${({ theme }) => theme.gridUnit * 0.75}px;
+      font-family: ${({ theme }) => theme.typography.families.sansSerif};
+      font-size: ${({ theme }) => theme.typography.sizes.m}px;
+    }
+
+    i {
+      margin: 0 ${({ theme }) => theme.gridUnit}px;
+    }
+  }
+
+  input,
+  textarea {
+    flex: 1 1 auto;
+  }
+
+  textarea {
+    height: 160px;
+    resize: none;
+  }
+
+  input::placeholder,
+  textarea::placeholder {
+    color: ${({ theme }) => theme.colors.grayscale.light1};
+  }
+
+  textarea,
+  input[type='text'],
+  input[type='number'] {
+    padding: ${({ theme }) => theme.gridUnit * 1.5}px
+      ${({ theme }) => theme.gridUnit * 2}px;
+    border-style: none;
+    border: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
+    border-radius: ${({ theme }) => theme.gridUnit}px;
+
+    &[name='name'] {
+      flex: 0 1 auto;
+      width: 40%;
+    }
+
+    &[name='sqlalchemy_uri'] {
+      margin-right: ${({ theme }) => theme.gridUnit * 3}px;
+    }
+  }
+  &.expandable {
+    height: 0;
+    overflow: hidden;
+    transition: height 0.25s;
+    margin-left: ${({ theme }) => theme.gridUnit * 8}px;
+    margin-bottom: 0;
+    padding: 0;
+    .control-label {
+      margin-bottom: 0;
+    }
+    &.open {
+      height: ${CTAS_CVAS_SCHEMA_FORM_HEIGHT}px;
+      padding-right: ${({ theme }) => theme.gridUnit * 5}px;
+    }
+  }
+`;
+
+export const StyledJsonEditor = styled(JsonEditor)`
+  flex: 1 1 auto;
+  border: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
+  border-radius: ${({ theme }) => theme.gridUnit}px;
+`;
+
+export const StyledExpandableForm = styled.div`
+  padding-top: ${({ theme }) => theme.gridUnit}px;
+  .input-container {
+    padding-top: ${({ theme }) => theme.gridUnit}px;
+    padding-bottom: ${({ theme }) => theme.gridUnit}px;
+  }
+  &.expandable {
+    height: 0;
+    overflow: hidden;
+    transition: height 0.25s;
+    margin-left: ${({ theme }) => theme.gridUnit * 7}px;
+    &.open {
+      height: ${EXPOSE_IN_SQLLAB_FORM_HEIGHT}px;
+      &.ctas-open {
+        height: ${EXPOSE_ALL_FORM_HEIGHT}px;
+      }
+    }
+  }
+`;
+
+export const StyledRequiredTab = styled(Tabs.TabPane)`
+  padding-left: ${({ theme }) => theme.gridUnit * 4}px;
+  padding-right: ${({ theme }) => theme.gridUnit * 4}px;
+  margin-top: ${({ theme }) => theme.gridUnit * 4}px;
+`;

Reply via email to