From 6114e451bbc6b393dd224f9028d23a0128eb4914 Mon Sep 17 00:00:00 2001 From: riahk Date: Mon, 24 Aug 2020 13:22:10 -0700 Subject: [PATCH 01/12] Basic Database Add Modal UI --- superset-frontend/images/icons/databases.svg | 21 ++ .../src/components/Icon/index.tsx | 3 + .../views/CRUD/data/database/DatabaseList.tsx | 76 ++++++- .../CRUD/data/database/DatabaseModal.tsx | 196 ++++++++++++++++++ 4 files changed, 288 insertions(+), 8 deletions(-) create mode 100644 superset-frontend/images/icons/databases.svg create mode 100644 superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx diff --git a/superset-frontend/images/icons/databases.svg b/superset-frontend/images/icons/databases.svg new file mode 100644 index 000000000000..3464e04d7a6e --- /dev/null +++ b/superset-frontend/images/icons/databases.svg @@ -0,0 +1,21 @@ + + + + diff --git a/superset-frontend/src/components/Icon/index.tsx b/superset-frontend/src/components/Icon/index.tsx index 2107de8b9a75..91872c6c0344 100644 --- a/superset-frontend/src/components/Icon/index.tsx +++ b/superset-frontend/src/components/Icon/index.tsx @@ -28,6 +28,7 @@ import { ReactComponent as CircleCheckIcon } from 'images/icons/circle-check.svg import { ReactComponent as CircleCheckSolidIcon } from 'images/icons/circle-check-solid.svg'; import { ReactComponent as CloseIcon } from 'images/icons/close.svg'; import { ReactComponent as CompassIcon } from 'images/icons/compass.svg'; +import { ReactComponent as DatabasesIcon } from 'images/icons/databases.svg'; import { ReactComponent as DatasetPhysicalIcon } from 'images/icons/dataset_physical.svg'; import { ReactComponent as DatasetVirtualIcon } from 'images/icons/dataset_virtual.svg'; import { ReactComponent as DropdownArrowIcon } from 'images/icons/dropdown-arrow.svg'; @@ -57,6 +58,7 @@ type IconName = | 'circle-check' | 'close' | 'compass' + | 'databases' | 'dataset-physical' | 'dataset-virtual' | 'dropdown-arrow' @@ -85,6 +87,7 @@ export const iconsRegistry: Record< 'checkbox-on': CheckboxOnIcon, 'circle-check-solid': CircleCheckSolidIcon, 'circle-check': CircleCheckIcon, + databases: DatabasesIcon, 'dataset-physical': DatasetPhysicalIcon, 'dataset-virtual': DatasetVirtualIcon, 'favorite-selected': FavoriteSelectedIcon, diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx index b13c211a1b27..f7ae0a427088 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx @@ -16,31 +16,91 @@ * specific language governing permissions and limitations * under the License. */ -import React from 'react'; - +import { SupersetClient } from '@superset-ui/connection'; +import { t } from '@superset-ui/translation'; +import React, { useEffect, useState } from 'react'; +import { createErrorHandler } from 'src/views/CRUD/utils'; import withToasts from 'src/messageToasts/enhancers/withToasts'; import SubMenu, { SubMenuProps } from 'src/components/Menu/SubMenu'; import { commonMenuData } from 'src/views/CRUD/data/common'; +import DatabaseModal from './DatabaseModal'; + +type DatabaseObject = { + id: number; + // TODO: add more props +}; -interface DatasourceListProps { +interface DatabaseListProps { addDangerToast: (msg: string) => void; addSuccessToast: (msg: string) => void; } -function DatasourceList({ - addDangerToast, - addSuccessToast, -}: DatasourceListProps) { +function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) { + const [databaseModalOpen, setDatabaseModalOpen] = useState(false); + const [currentDatabase, setCurrentDatabase] = useState( + null, + ); + const [permissions, setPermissions] = useState([]); + + const fetchDatasetInfo = () => { + SupersetClient.get({ + endpoint: `/api/v1/dataset/_info`, + }).then( + ({ json: infoJson = {} }) => { + setPermissions(infoJson.permissions); + }, + createErrorHandler(errMsg => + addDangerToast(t('An error occurred while fetching datasets', errMsg)), + ), + ); + }; + + useEffect(() => { + fetchDatasetInfo(); + }, []); + + const hasPerm = (perm: string) => { + if (!permissions.length) { + return false; + } + + return Boolean(permissions.find(p => p === perm)); + }; + + const canCreate = hasPerm('can_add'); + const menuData: SubMenuProps = { activeChild: 'Databases', ...commonMenuData, }; + if (canCreate) { + menuData.primaryButton = { + name: ( + <> + {' '} + {t('Database')}{' '} + + ), + onClick: () => { + // Ensure modal will be opened in add mode + setCurrentDatabase(null); + setDatabaseModalOpen(true); + }, + }; + } + return ( <> + setDatabaseModalOpen(false)} + onDatabaseAdd={() => {}} + /> ); } -export default withToasts(DatasourceList); +export default withToasts(DatabaseList); diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx new file mode 100644 index 000000000000..64793da4651e --- /dev/null +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx @@ -0,0 +1,196 @@ +/** + * 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, { FunctionComponent, useState } from 'react'; +import styled from '@superset-ui/style'; +import { t } from '@superset-ui/translation'; +import withToasts from 'src/messageToasts/enhancers/withToasts'; +import Icon from 'src/components/Icon'; +import Button from 'src/views/CRUD/data/dataset/Button'; +import { Tabs, Modal } from 'src/common/components'; + +type DatabaseObject = { + id: number; + // TODO: add more props +}; + +interface DatabaseModalProps { + addDangerToast: (msg: string) => void; + addSuccessToast: (msg: string) => void; + onDatabaseAdd?: (database?: DatabaseObject) => void; // TODO: should we add a separate function for edit? + onHide: () => void; + show: boolean; + database?: DatabaseObject | null; // If included, will go into edit mode +} + +const { TabPane } = Tabs; + +const StyledIcon = styled(Icon)` + margin: auto 10px auto 0; +`; + +const StyledTabs = styled(Tabs)` + margin-top: -18px; + + .ant-tabs-nav-list { + width: 100%; + } + + .ant-tabs-tab { + width: 20%; + + &.ant-tabs-tab-active .ant-tabs-tab-btn { + color: inherit; + } + } + + .ant-tabs-tab-btn { + flex: 1 1 auto; + font-size: 12px; + text-align: center; + text-transform: uppercase; + + .required { + margin-left: 2px; + color: #e04355; + } + } + + .ant-tabs-ink-bar { + background: ${({ theme }) => theme.colors.secondary.base}; + } +`; + +const StyledModal = styled(Modal)` + .ant-modal-header { + background-color: ${({ theme }) => theme.colors.grayscale.light4}; + border-radius: ${({ theme }) => theme.borderRadius}px + ${({ theme }) => theme.borderRadius}px 0 0; + + .ant-modal-title h4 { + display: flex; + margin: 0; + align-items: center; + } + } + + .ant-modal-close-x { + display: flex; + align-items: center; + + .close { + flex: 1 1 auto; + margin-bottom: 3px; + color: ${({ theme }) => theme.colors.secondary.dark1}; + font-size: 32px; + font-weight: ${({ theme }) => theme.typography.weights.light}; + } + } + + .ant-modal-body { + padding: 18px; + } + + .ant-modal-footer { + border-top: 1px solid ${({ theme }) => theme.colors.grayscale.light2}; + padding: 16px; + + .btn { + font-size: 12px; + text-transform: uppercase; + } + + .btn + .btn { + margin-left: 8px; + } + } +`; + +const DatabaseModal: FunctionComponent = ({ + addDangerToast, + addSuccessToast, + onDatabaseAdd, + onHide, + show, + database, +}) => { + // const [disableSave, setDisableSave] = useState(true); + const [disableSave] = useState(true); + const onSave = () => { + if (onDatabaseAdd) { + onDatabaseAdd(); + } + }; + + return ( + + ); +}; + +export default withToasts(DatabaseModal); From 5b1edf823c941413e382071532e1ef8be557baac Mon Sep 17 00:00:00 2001 From: riahk Date: Mon, 24 Aug 2020 15:36:27 -0700 Subject: [PATCH 02/12] Add customized antd modal to common --- .../src/common/components/Modal.tsx | 125 ++++++++++++++++++ .../views/CRUD/data/database/DatabaseList.tsx | 4 +- .../CRUD/data/database/DatabaseModal.tsx | 81 ++---------- 3 files changed, 142 insertions(+), 68 deletions(-) create mode 100644 superset-frontend/src/common/components/Modal.tsx diff --git a/superset-frontend/src/common/components/Modal.tsx b/superset-frontend/src/common/components/Modal.tsx new file mode 100644 index 000000000000..8fae65f474ac --- /dev/null +++ b/superset-frontend/src/common/components/Modal.tsx @@ -0,0 +1,125 @@ +/** + * 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 styled from '@superset-ui/style'; +import { Modal as BaseModal } from 'src/common/components'; +import { t } from '@superset-ui/translation'; +import Button from 'src/views/CRUD/data/dataset/Button'; + +interface ModalProps { + className?: string; + children: React.ReactNode; + disablePrimaryButton?: boolean; + onHide: () => void; + onHandledPrimaryAction: () => void; + primaryButtonName: string; + primaryButtonType?: 'primary' | 'danger'; + show: boolean; + title: React.ReactNode; + width?: string; + centered?: boolean; +} + +const StyledModal = styled(BaseModal)` + .ant-modal-header { + background-color: ${({ theme }) => theme.colors.grayscale.light4}; + border-radius: ${({ theme }) => theme.borderRadius}px + ${({ theme }) => theme.borderRadius}px 0 0; + + .ant-modal-title h4 { + display: flex; + margin: 0; + align-items: center; + } + } + + .ant-modal-close-x { + display: flex; + align-items: center; + + .close { + flex: 1 1 auto; + margin-bottom: 3px; + color: ${({ theme }) => theme.colors.secondary.dark1}; + font-size: 32px; + font-weight: ${({ theme }) => theme.typography.weights.light}; + } + } + + .ant-modal-body { + padding: 18px; + } + + .ant-modal-footer { + border-top: 1px solid ${({ theme }) => theme.colors.grayscale.light2}; + padding: 16px; + + .btn { + font-size: 12px; + text-transform: uppercase; + } + + .btn + .btn { + margin-left: 8px; + } + } +`; + +export default function Modal({ + children, + disablePrimaryButton = false, + onHide, + onHandledPrimaryAction, + primaryButtonName, + primaryButtonType = 'primary', + show, + title, + width, + centered, +}: ModalProps) { + return ( + + ); +} diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx index f7ae0a427088..1a9e4f94b8b8 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx @@ -97,7 +97,9 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) { database={currentDatabase} show={databaseModalOpen} onHide={() => setDatabaseModalOpen(false)} - onDatabaseAdd={() => {}} + onDatabaseAdd={() => { + /* TODO: add database logic here */ + }} /> ); diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx index 64793da4651e..b57422a09413 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx @@ -21,8 +21,8 @@ import styled from '@superset-ui/style'; import { t } from '@superset-ui/translation'; import withToasts from 'src/messageToasts/enhancers/withToasts'; import Icon from 'src/components/Icon'; -import Button from 'src/views/CRUD/data/dataset/Button'; -import { Tabs, Modal } from 'src/common/components'; +import Modal from 'src/common/components/Modal'; +import { Tabs } from 'src/common/components'; type DatabaseObject = { id: number; @@ -76,51 +76,6 @@ const StyledTabs = styled(Tabs)` } `; -const StyledModal = styled(Modal)` - .ant-modal-header { - background-color: ${({ theme }) => theme.colors.grayscale.light4}; - border-radius: ${({ theme }) => theme.borderRadius}px - ${({ theme }) => theme.borderRadius}px 0 0; - - .ant-modal-title h4 { - display: flex; - margin: 0; - align-items: center; - } - } - - .ant-modal-close-x { - display: flex; - align-items: center; - - .close { - flex: 1 1 auto; - margin-bottom: 3px; - color: ${({ theme }) => theme.colors.secondary.dark1}; - font-size: 32px; - font-weight: ${({ theme }) => theme.typography.weights.light}; - } - } - - .ant-modal-body { - padding: 18px; - } - - .ant-modal-footer { - border-top: 1px solid ${({ theme }) => theme.colors.grayscale.light2}; - padding: 16px; - - .btn { - font-size: 12px; - text-transform: uppercase; - } - - .btn + .btn { - margin-left: 8px; - } - } -`; - const DatabaseModal: FunctionComponent = ({ addDangerToast, addSuccessToast, @@ -135,35 +90,27 @@ const DatabaseModal: FunctionComponent = ({ if (onDatabaseAdd) { onDatabaseAdd(); } + + onHide(); }; + const isEditMode = database !== null; + return ( - + ); }; From a51106ddc069e2a00ea351caf9fa232e54b9f444 Mon Sep 17 00:00:00 2001 From: riahk Date: Mon, 24 Aug 2020 16:54:10 -0700 Subject: [PATCH 03/12] tweaks --- .../src/views/CRUD/data/database/DatabaseList.tsx | 7 +------ .../src/views/CRUD/data/database/DatabaseModal.tsx | 4 +++- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx index 1a9e4f94b8b8..fb0edbc5bc65 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx @@ -23,12 +23,7 @@ import { createErrorHandler } from 'src/views/CRUD/utils'; import withToasts from 'src/messageToasts/enhancers/withToasts'; import SubMenu, { SubMenuProps } from 'src/components/Menu/SubMenu'; import { commonMenuData } from 'src/views/CRUD/data/common'; -import DatabaseModal from './DatabaseModal'; - -type DatabaseObject = { - id: number; - // TODO: add more props -}; +import DatabaseModal, { DatabaseObject } from './DatabaseModal'; interface DatabaseListProps { addDangerToast: (msg: string) => void; diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx index b57422a09413..21f546b0cabd 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx @@ -24,8 +24,10 @@ import Icon from 'src/components/Icon'; import Modal from 'src/common/components/Modal'; import { Tabs } from 'src/common/components'; -type DatabaseObject = { +export type DatabaseObject = { id: number; + name: string; + uri: string; // TODO: add more props }; From cdffca8e746b5150625541ac846bc4edc3f039c1 Mon Sep 17 00:00:00 2001 From: riahk Date: Tue, 25 Aug 2020 10:57:03 -0700 Subject: [PATCH 04/12] translation wrapping --- .../src/views/CRUD/data/database/DatabaseModal.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx index 21f546b0cabd..8fa6b4135fc6 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx @@ -118,23 +118,24 @@ const DatabaseModal: FunctionComponent = ({ - Connection* + {t('Connection')} + * } key="1" > Connection Form Data - + {t('Performance')}} key="2"> Performance Form Data - + {t('SQL Lab Settings')}} key="3"> SQL Lab Settings Form Data - + {t('Security')}} key="4"> Security Form Data - + {t('Extra')}} key="5"> Extra Form Data From 5b008fff3412a4a506a7f41ea5e7725374eb002e Mon Sep 17 00:00:00 2001 From: riahk Date: Tue, 25 Aug 2020 13:21:59 -0700 Subject: [PATCH 05/12] style tweaks --- superset-frontend/src/components/Icon/index.tsx | 2 ++ .../src/views/CRUD/data/database/DatabaseModal.tsx | 11 ++++++----- .../src/views/CRUD/data/dataset/AddDatasetModal.tsx | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/superset-frontend/src/components/Icon/index.tsx b/superset-frontend/src/components/Icon/index.tsx index 91872c6c0344..f033fd81341f 100644 --- a/superset-frontend/src/components/Icon/index.tsx +++ b/superset-frontend/src/components/Icon/index.tsx @@ -109,6 +109,7 @@ export const iconsRegistry: Record< trash: TrashIcon, warning: WarningIcon, }; + interface IconProps extends SVGProps { name: IconName; } @@ -120,6 +121,7 @@ const Icon = ({ ...rest }: IconProps) => { const Component = iconsRegistry[name]; + return ( ); diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx index 8fa6b4135fc6..a1eefda59ba6 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx @@ -43,7 +43,7 @@ interface DatabaseModalProps { const { TabPane } = Tabs; const StyledIcon = styled(Icon)` - margin: auto 10px auto 0; + margin: auto ${({ theme }) => theme.gridUnit * 2}px auto 0; `; const StyledTabs = styled(Tabs)` @@ -54,7 +54,8 @@ const StyledTabs = styled(Tabs)` } .ant-tabs-tab { - width: 20%; + flex: 1 1 auto; + width: 0; &.ant-tabs-tab-active .ant-tabs-tab-btn { color: inherit; @@ -63,13 +64,13 @@ const StyledTabs = styled(Tabs)` .ant-tabs-tab-btn { flex: 1 1 auto; - font-size: 12px; + font-size: ${({ theme }) => theme.typography.sizes.s}px; text-align: center; text-transform: uppercase; .required { - margin-left: 2px; - color: #e04355; + margin-left: ${({ theme }) => theme.gridUnit / 2}px; + color: ${({ theme }) => theme.colors.error.base}; } } diff --git a/superset-frontend/src/views/CRUD/data/dataset/AddDatasetModal.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDatasetModal.tsx index 045837ceac87..17c5182544d8 100644 --- a/superset-frontend/src/views/CRUD/data/dataset/AddDatasetModal.tsx +++ b/superset-frontend/src/views/CRUD/data/dataset/AddDatasetModal.tsx @@ -42,7 +42,7 @@ interface DatasetModalProps { } const StyledIcon = styled(Icon)` - margin: auto 10px auto 0; + margin: auto ${({ theme }) => theme.gridUnit * 2}px auto 0; `; const TableSelectorContainer = styled.div` From 565f59109d8fc73b233083f282419c0964a4b24a Mon Sep 17 00:00:00 2001 From: riahk Date: Tue, 25 Aug 2020 15:20:33 -0700 Subject: [PATCH 06/12] move tabs re-styling to common components --- .../src/common/components/Tabs.tsx | 55 +++++++++++++++++++ .../CRUD/data/database/DatabaseModal.tsx | 42 ++------------ 2 files changed, 60 insertions(+), 37 deletions(-) create mode 100644 superset-frontend/src/common/components/Tabs.tsx diff --git a/superset-frontend/src/common/components/Tabs.tsx b/superset-frontend/src/common/components/Tabs.tsx new file mode 100644 index 000000000000..dde0102fa5e4 --- /dev/null +++ b/superset-frontend/src/common/components/Tabs.tsx @@ -0,0 +1,55 @@ +/** + * 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/style'; +import { Tabs as BaseTabs } from 'src/common/components'; + +const Tabs = styled(BaseTabs)` + margin-top: -18px; + + .ant-tabs-nav-list { + width: 100%; + } + + .ant-tabs-tab { + flex: 1 1 auto; + width: 0; + + &.ant-tabs-tab-active .ant-tabs-tab-btn { + color: inherit; + } + } + + .ant-tabs-tab-btn { + flex: 1 1 auto; + font-size: ${({ theme }) => theme.typography.sizes.s}px; + text-align: center; + text-transform: uppercase; + + .required { + margin-left: ${({ theme }) => theme.gridUnit / 2}px; + color: ${({ theme }) => theme.colors.error.base}; + } + } + + .ant-tabs-ink-bar { + background: ${({ theme }) => theme.colors.secondary.base}; + } +`; + +export default Tabs; diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx index a1eefda59ba6..e17b92979ea6 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx @@ -22,7 +22,8 @@ import { t } from '@superset-ui/translation'; import withToasts from 'src/messageToasts/enhancers/withToasts'; import Icon from 'src/components/Icon'; import Modal from 'src/common/components/Modal'; -import { Tabs } from 'src/common/components'; +import Tabs from 'src/common/components/Tabs'; +import { Tabs as BaseTabs } from 'src/common/components'; export type DatabaseObject = { id: number; @@ -40,45 +41,12 @@ interface DatabaseModalProps { database?: DatabaseObject | null; // If included, will go into edit mode } -const { TabPane } = Tabs; +const { TabPane } = BaseTabs; const StyledIcon = styled(Icon)` margin: auto ${({ theme }) => theme.gridUnit * 2}px auto 0; `; -const StyledTabs = styled(Tabs)` - margin-top: -18px; - - .ant-tabs-nav-list { - width: 100%; - } - - .ant-tabs-tab { - flex: 1 1 auto; - width: 0; - - &.ant-tabs-tab-active .ant-tabs-tab-btn { - color: inherit; - } - } - - .ant-tabs-tab-btn { - flex: 1 1 auto; - font-size: ${({ theme }) => theme.typography.sizes.s}px; - text-align: center; - text-transform: uppercase; - - .required { - margin-left: ${({ theme }) => theme.gridUnit / 2}px; - color: ${({ theme }) => theme.colors.error.base}; - } - } - - .ant-tabs-ink-bar { - background: ${({ theme }) => theme.colors.secondary.base}; - } -`; - const DatabaseModal: FunctionComponent = ({ addDangerToast, addSuccessToast, @@ -115,7 +83,7 @@ const DatabaseModal: FunctionComponent = ({ } > - + @@ -139,7 +107,7 @@ const DatabaseModal: FunctionComponent = ({ {t('Extra')}} key="5"> Extra Form Data - + ); }; From 152ad914e640a065d3505d0d319b4fae6683fef9 Mon Sep 17 00:00:00 2001 From: riahk Date: Wed, 26 Aug 2020 12:11:08 -0700 Subject: [PATCH 07/12] add test + basic form state --- .../CRUD/data/database/DatabaseModal_spec.jsx | 44 ++++++++++ .../src/common/components/Modal.tsx | 2 + .../views/CRUD/data/database/DatabaseList.tsx | 8 +- .../CRUD/data/database/DatabaseModal.tsx | 87 +++++++++++++++++-- 4 files changed, 134 insertions(+), 7 deletions(-) create mode 100644 superset-frontend/spec/javascripts/views/CRUD/data/database/DatabaseModal_spec.jsx 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 new file mode 100644 index 000000000000..c80bb380c483 --- /dev/null +++ b/superset-frontend/spec/javascripts/views/CRUD/data/database/DatabaseModal_spec.jsx @@ -0,0 +1,44 @@ +/** + * 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 thunk from 'redux-thunk'; +import configureStore from 'redux-mock-store'; +import { styledMount as mount } from 'spec/helpers/theming'; + +import DatabaseModal from 'src/views/CRUD/data/database/DatabaseModal'; +import Modal from 'src/common/components/Modal'; +// import Tabs from 'src/common/components/Tabs'; +// import BaseTabs from 'src/common/components'; + +// store needed for withToasts(DatabaseModal) +const mockStore = configureStore([thunk]); +const store = mockStore({}); + +describe('DatabaseModal', () => { + const wrapper = mount(, { context: { store } }); + + it('renders', () => { + expect(wrapper.find(DatabaseModal)).toExist(); + }); + + it('renders a Modal', () => { + expect(wrapper.find(Modal)).toExist(); + }); + +}); diff --git a/superset-frontend/src/common/components/Modal.tsx b/superset-frontend/src/common/components/Modal.tsx index 8fae65f474ac..ab29c9fce0ef 100644 --- a/superset-frontend/src/common/components/Modal.tsx +++ b/superset-frontend/src/common/components/Modal.tsx @@ -92,6 +92,7 @@ export default function Modal({ title, width, centered, + ...rest }: ModalProps) { return ( , ]} + {...rest} > {children} diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx index fb0edbc5bc65..3b577a688534 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx @@ -69,6 +69,12 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) { ...commonMenuData, }; + const testDB = { + id: 10, + name: 'test', + uri: 'test/test/', + }; + if (canCreate) { menuData.primaryButton = { name: ( @@ -79,7 +85,7 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) { ), onClick: () => { // Ensure modal will be opened in add mode - setCurrentDatabase(null); + setCurrentDatabase(testDB); setDatabaseModalOpen(true); }, }; diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx index e17b92979ea6..acc01f14a774 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx @@ -26,7 +26,7 @@ import Tabs from 'src/common/components/Tabs'; import { Tabs as BaseTabs } from 'src/common/components'; export type DatabaseObject = { - id: number; + id?: number; name: string; uri: string; // TODO: add more props @@ -47,32 +47,95 @@ const StyledIcon = styled(Icon)` margin: auto ${({ theme }) => theme.gridUnit * 2}px auto 0; `; +const StyledInputContainer = styled.div` + .label { + display: block; + padding: ${({ theme }) => theme.gridUnit}px 0; + color: ${({ theme }) => theme.colors.grayscale.light1}; + text-align: left; + } + + input[type='text'] { + 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'] { + width: 40%; + } + } +`; + const DatabaseModal: FunctionComponent = ({ addDangerToast, addSuccessToast, onDatabaseAdd, onHide, show, - database, + database = null, }) => { // const [disableSave, setDisableSave] = useState(true); - const [disableSave] = useState(true); + const [disableSave] = useState(true); + const [db, setDB] = useState(null); + const [isHidden, setIsHidden] = useState(true); + + console.log('db', db); + + // Functions + const hide = () => { + setIsHidden(true); + onHide(); + }; + const onSave = () => { if (onDatabaseAdd) { onDatabaseAdd(); } - onHide(); + hide(); + }; + + const onInputChange = (event: React.ChangeEvent) => { + const target = event.target; + const data = { + name: db ? db.name : '', + uri: db ? db.uri : '', + ...db, + }; + + data[target.name] = target.value; + + setDB(data); }; const isEditMode = database !== null; + // Initialize + if ( + isEditMode && + (!db || !db.id || (database && database.id !== db.id) || (isHidden && show)) + ) { + setDB(database); + } else if (!isEditMode && (!db || db.id)) { + setDB({ + name: '', + uri: '', + }); + } + + // Show/hide + if (isHidden && show) { + setIsHidden(false); + } + return ( = ({ } key="1" > - Connection Form Data + +
+ {t('Datasource Name')} + * +
+ +
{t('Performance')}} key="2"> Performance Form Data From 62c563cc9689d1889a64632639c1955eeea52dbd Mon Sep 17 00:00:00 2001 From: riahk Date: Wed, 26 Aug 2020 13:32:22 -0700 Subject: [PATCH 08/12] update database list spec --- .../views/CRUD/data/database/DatabaseList_spec.jsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/superset-frontend/spec/javascripts/views/CRUD/data/database/DatabaseList_spec.jsx b/superset-frontend/spec/javascripts/views/CRUD/data/database/DatabaseList_spec.jsx index c85ab825d759..c8fb96ac45c6 100644 --- a/superset-frontend/spec/javascripts/views/CRUD/data/database/DatabaseList_spec.jsx +++ b/superset-frontend/spec/javascripts/views/CRUD/data/database/DatabaseList_spec.jsx @@ -22,6 +22,7 @@ import configureStore from 'redux-mock-store'; import { styledMount as mount } from 'spec/helpers/theming'; import DatabaseList from 'src/views/CRUD/data/database/DatabaseList'; +import DatabaseModal from 'src/views/CRUD/data/database/DatabaseModal'; import SubMenu from 'src/components/Menu/SubMenu'; // store needed for withToasts(DatabaseList) @@ -38,4 +39,8 @@ describe('DatabaseList', () => { it('renders a SubMenu', () => { expect(wrapper.find(SubMenu)).toExist(); }); + + it('renders a DatabaseModal', () => { + expect(wrapper.find(DatabaseModal)).toExist(); + }); }); From dc9f701b5262f08c2722adc492a476efa7d15f43 Mon Sep 17 00:00:00 2001 From: riahk Date: Wed, 26 Aug 2020 15:42:37 -0700 Subject: [PATCH 09/12] fix lint + update style --- .../views/CRUD/data/database/DatabaseModal_spec.jsx | 1 - .../src/views/CRUD/data/database/DatabaseModal.tsx | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) 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 c80bb380c483..a9def757f880 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 @@ -40,5 +40,4 @@ describe('DatabaseModal', () => { it('renders a Modal', () => { expect(wrapper.find(Modal)).toExist(); }); - }); diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx index acc01f14a774..de5f50e84ef7 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx @@ -53,6 +53,11 @@ const StyledInputContainer = styled.div` padding: ${({ theme }) => theme.gridUnit}px 0; color: ${({ theme }) => theme.colors.grayscale.light1}; text-align: left; + + .required { + margin-left: ${({ theme }) => theme.gridUnit / 2}px; + color: ${({ theme }) => theme.colors.error.base}; + } } input[type='text'] { From e4874aeeb9383f8173d3f0278e6dea0c24d9fcc3 Mon Sep 17 00:00:00 2001 From: riahk Date: Thu, 27 Aug 2020 11:22:58 -0700 Subject: [PATCH 10/12] rm test code + unnecessary comments --- .../views/CRUD/data/database/DatabaseModal_spec.jsx | 2 -- .../src/views/CRUD/data/database/DatabaseList.tsx | 8 +------- 2 files changed, 1 insertion(+), 9 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 a9def757f880..8b8e29b98943 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,8 +23,6 @@ import { styledMount as mount } from 'spec/helpers/theming'; import DatabaseModal from 'src/views/CRUD/data/database/DatabaseModal'; import Modal from 'src/common/components/Modal'; -// import Tabs from 'src/common/components/Tabs'; -// import BaseTabs from 'src/common/components'; // store needed for withToasts(DatabaseModal) const mockStore = configureStore([thunk]); diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx index 3b577a688534..fb0edbc5bc65 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx @@ -69,12 +69,6 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) { ...commonMenuData, }; - const testDB = { - id: 10, - name: 'test', - uri: 'test/test/', - }; - if (canCreate) { menuData.primaryButton = { name: ( @@ -85,7 +79,7 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) { ), onClick: () => { // Ensure modal will be opened in add mode - setCurrentDatabase(testDB); + setCurrentDatabase(null); setDatabaseModalOpen(true); }, }; From 2f9af9bb155e5aadf77a563d7e9569262fac661f Mon Sep 17 00:00:00 2001 From: riahk Date: Thu, 27 Aug 2020 11:26:44 -0700 Subject: [PATCH 11/12] rm console statement --- .../src/views/CRUD/data/database/DatabaseModal.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx index de5f50e84ef7..d48440d7f1b4 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx @@ -86,8 +86,6 @@ const DatabaseModal: FunctionComponent = ({ const [db, setDB] = useState(null); const [isHidden, setIsHidden] = useState(true); - console.log('db', db); - // Functions const hide = () => { setIsHidden(true); From f86c2ffb67962973b36da396b20a7c6b943cf5c6 Mon Sep 17 00:00:00 2001 From: riahk Date: Thu, 27 Aug 2020 12:54:41 -0700 Subject: [PATCH 12/12] add URI field --- .../CRUD/data/database/DatabaseModal.tsx | 56 ++++++++++++++++--- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx index d48440d7f1b4..4944e2a35fd2 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseModal.tsx @@ -48,10 +48,14 @@ const StyledIcon = styled(Icon)` `; const StyledInputContainer = styled.div` - .label { + margin-bottom: ${({ theme }) => theme.gridUnit * 2}px; + + .label, + .helper { display: block; padding: ${({ theme }) => theme.gridUnit}px 0; color: ${({ theme }) => theme.colors.grayscale.light1}; + font-size: ${({ theme }) => theme.typography.sizes.s}px; text-align: left; .required { @@ -60,7 +64,12 @@ const StyledInputContainer = styled.div` } } + .input-container { + display: flex; + } + input[type='text'] { + flex: 1 1 auto; padding: ${({ theme }) => theme.gridUnit * 1.5}px ${({ theme }) => theme.gridUnit * 2}px; border-style: none; @@ -68,6 +77,7 @@ const StyledInputContainer = styled.div` border-radius: ${({ theme }) => theme.gridUnit}px; &[name='name'] { + flex: 0 1 auto; width: 40%; } } @@ -121,7 +131,7 @@ const DatabaseModal: FunctionComponent = ({ (!db || !db.id || (database && database.id !== db.id) || (isHidden && show)) ) { setDB(database); - } else if (!isEditMode && (!db || db.id)) { + } else if (!isEditMode && (!db || db.id || (isHidden && show))) { setDB({ name: '', uri: '', @@ -164,13 +174,41 @@ const DatabaseModal: FunctionComponent = ({ {t('Datasource Name')} * - +
+ +
+ + +
+ {t('SQLAlchemy URI')} + * +
+
+ +
+
+ {t('Refer to the ')} + + {t('SQLAlchemy docs')} + + {t(' for more information on how to structure your URI.')} +
{t('Performance')}} key="2">