diff --git a/superset-frontend/cypress-base/cypress/integration/chart_list/card_view.test.ts b/superset-frontend/cypress-base/cypress/integration/chart_list/card_view.test.ts index 8112bc113f..3691fa7e84 100644 --- a/superset-frontend/cypress-base/cypress/integration/chart_list/card_view.test.ts +++ b/superset-frontend/cypress-base/cypress/integration/chart_list/card_view.test.ts @@ -92,11 +92,14 @@ describe('chart card view', () => { cy.get('[data-test="more-horiz"]').last().trigger('mouseover'); cy.get('[data-test="chart-list-delete-option"]').should('be.visible'); cy.get('[data-test="chart-list-delete-option"]').contains('Delete').click(); - cy.get('[data-test="Please Confirm-modal-header"]').should('be.visible'); - cy.get('[data-test="modal-delete-button"]').should('have.attr', 'disabled'); - cy.get('[data-test="Please Confirm-modal-header"]').should('be.visible'); + cy.get('[data-test="Please Confirm-modal"]').should('be.visible'); + cy.get('[data-test="modal-confirm-button"]').should( + 'have.attr', + 'disabled', + ); + cy.get('[data-test="Please Confirm-modal"]').should('be.visible'); cy.get("[data-test='delete-modal-input']").type('DELETE'); - cy.get('[data-test="modal-delete-button"]').should( + cy.get('[data-test="modal-confirm-button"]').should( 'not.have.attr', 'disabled', ); diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard_list/card_view.test.ts b/superset-frontend/cypress-base/cypress/integration/dashboard_list/card_view.test.ts index e0f5e9c8c0..5afc168f4b 100644 --- a/superset-frontend/cypress-base/cypress/integration/dashboard_list/card_view.test.ts +++ b/superset-frontend/cypress-base/cypress/integration/dashboard_list/card_view.test.ts @@ -89,11 +89,11 @@ describe('Dashboard card view', () => { cy.get('.ant-dropdown-trigger').last().trigger('mouseover'); cy.get('.ant-dropdown-menu-item').contains('Delete').should('exist'); cy.get('.ant-dropdown-menu-item').contains('Delete').click(); - cy.get('.modal-dialog').should('be.visible'); - cy.get('.modal-dialog .btn-danger').should('have.attr', 'disabled'); - cy.get(".modal-dialog input[id='delete']").type('DELETE'); - cy.get('.modal-dialog .btn-danger').should('not.have.attr', 'disabled'); - cy.get('.modal-dialog .btn-default').contains('Cancel').click(); + cy.get('.ant-modal').should('be.visible'); + cy.get('.ant-modal .btn-danger').should('have.attr', 'disabled'); + cy.get(".ant-modal input[id='delete']").type('DELETE'); + cy.get('.ant-modal .btn-danger').should('not.have.attr', 'disabled'); + cy.get('.ant-modal .btn-default').contains('Cancel').click(); }); it('should edit correctly', () => { diff --git a/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts b/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts index 1fd517b639..4cf22dffa2 100644 --- a/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts +++ b/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts @@ -69,7 +69,7 @@ describe('Datasource control', () => { // delete metric cy.get('[data-test="datasource-menu-trigger"]').click(); cy.get('[data-test="edit-dataset"]').click(); - cy.get('.modal-content').within(() => { + cy.get('.ant-modal-content').within(() => { cy.get('a[role="tab"]').contains('Metrics').click(); }); cy.get(`input[value="${newMetricName}"]`) diff --git a/superset-frontend/spec/javascripts/components/ConfirmStatusChange_spec.jsx b/superset-frontend/spec/javascripts/components/ConfirmStatusChange_spec.jsx index 506280b591..6abdb7aace 100644 --- a/superset-frontend/spec/javascripts/components/ConfirmStatusChange_spec.jsx +++ b/superset-frontend/spec/javascripts/components/ConfirmStatusChange_spec.jsx @@ -21,7 +21,7 @@ import { mount } from 'enzyme'; import Button from 'src/components/Button'; import { supersetTheme, ThemeProvider } from '@superset-ui/core'; import ConfirmStatusChange from 'src/components/ConfirmStatusChange'; -import Modal from 'src/components/Modal'; +import Modal from 'src/common/components/Modal'; describe('ConfirmStatusChange', () => { const mockedProps = { diff --git a/superset-frontend/spec/javascripts/datasource/ChangeDatasourceModal_spec.jsx b/superset-frontend/spec/javascripts/datasource/ChangeDatasourceModal_spec.jsx index da4854124f..739ef59158 100644 --- a/superset-frontend/spec/javascripts/datasource/ChangeDatasourceModal_spec.jsx +++ b/superset-frontend/spec/javascripts/datasource/ChangeDatasourceModal_spec.jsx @@ -18,14 +18,13 @@ */ import React from 'react'; import { mount } from 'enzyme'; -import { Modal } from 'react-bootstrap'; import configureStore from 'redux-mock-store'; import fetchMock from 'fetch-mock'; import thunk from 'redux-thunk'; import sinon from 'sinon'; import { supersetTheme, ThemeProvider } from '@superset-ui/core'; import { act } from 'react-dom/test-utils'; - +import Modal from 'src/common/components/Modal'; import ChangeDatasourceModal from 'src/datasource/ChangeDatasourceModal'; import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint'; import mockDatasource from '../../fixtures/mockDatasource'; diff --git a/superset-frontend/spec/javascripts/datasource/DatasourceModal_spec.jsx b/superset-frontend/spec/javascripts/datasource/DatasourceModal_spec.jsx index 11ba0dba4f..23f7a36308 100644 --- a/superset-frontend/spec/javascripts/datasource/DatasourceModal_spec.jsx +++ b/superset-frontend/spec/javascripts/datasource/DatasourceModal_spec.jsx @@ -18,7 +18,6 @@ */ import React from 'react'; import { act } from 'react-dom/test-utils'; -import { Modal } from 'react-bootstrap'; import configureStore from 'redux-mock-store'; import { mount } from 'enzyme'; import fetchMock from 'fetch-mock'; @@ -27,6 +26,7 @@ import sinon from 'sinon'; import { supersetTheme, ThemeProvider } from '@superset-ui/core'; import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint'; +import Modal from 'src/common/components/Modal'; import DatasourceModal from 'src/datasource/DatasourceModal'; import DatasourceEditor from 'src/datasource/DatasourceEditor'; import * as featureFlags from 'src/featureFlags'; @@ -80,7 +80,7 @@ describe('DatasourceModal', () => { }); it('renders a Modal', () => { - expect(wrapper.find(Modal)).toHaveLength(2); + expect(wrapper.find(Modal)).toExist(); }); it('renders a DatasourceEditor', () => { diff --git a/superset-frontend/src/common/components/Modal/Modal.tsx b/superset-frontend/src/common/components/Modal/Modal.tsx index ffc88cd6f7..35e0d41962 100644 --- a/superset-frontend/src/common/components/Modal/Modal.tsx +++ b/superset-frontend/src/common/components/Modal/Modal.tsx @@ -124,7 +124,7 @@ export default function Modal({ }: ModalProps) { const modalFooter = isNil(footer) ? [ - , , @@ -156,7 +157,7 @@ export default function Modal({ } footer={!hideFooter ? modalFooter : null} - data-test={`${title}-modal`} + wrapProps={{ 'data-test': `${title}-modal` }} {...rest} > {children} diff --git a/superset-frontend/src/components/DeleteModal.tsx b/superset-frontend/src/components/DeleteModal.tsx index 70630d62db..ad58aff54c 100644 --- a/superset-frontend/src/components/DeleteModal.tsx +++ b/superset-frontend/src/components/DeleteModal.tsx @@ -19,7 +19,7 @@ import { t, styled } from '@superset-ui/core'; import React, { useState } from 'react'; import { FormGroup, FormControl, FormControlProps } from 'react-bootstrap'; -import Modal from 'src/components/Modal'; +import Modal from 'src/common/components/Modal'; import FormLabel from 'src/components/FormLabel'; const StyleFormGroup = styled(FormGroup)` diff --git a/superset-frontend/src/components/Modal.tsx b/superset-frontend/src/components/Modal.tsx deleted file mode 100644 index b4ae415001..0000000000 --- a/superset-frontend/src/components/Modal.tsx +++ /dev/null @@ -1,103 +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 { styled, t } from '@superset-ui/core'; -import { Modal as BaseModal } from 'react-bootstrap'; -import Button from 'src/components/Button'; - -interface ModalProps { - children: React.ReactNode; - disablePrimaryButton?: boolean; - onHide: () => void; - onHandledPrimaryAction: () => void; - primaryButtonName: string; - primaryButtonType?: 'primary' | 'danger'; - show: boolean; - title: React.ReactNode; - bsSize?: 'small' | 'large'; // react-bootstrap also supports 'sm', 'lg' but we're keeping it simple. -} - -const StyledModal = styled(BaseModal)` - .modal-header { - background-color: ${({ theme }) => theme.colors.grayscale.light4}; - border-radius: ${({ theme }) => theme.borderRadius}px - ${({ theme }) => theme.borderRadius}px 0 0; - .close { - color: ${({ theme }) => theme.colors.secondary.dark1}; - font-size: 32px; - font-weight: ${({ theme }) => theme.typography.weights.light}; - margin-top: -3px; - } - } - - .modal-body { - padding: 18px; - } - - .modal-footer { - border-top: 1px solid ${({ theme }) => theme.colors.grayscale.light2}; - padding: 16px; - } -`; - -const Title = styled.div` - color: ${({ theme }) => theme.colors.secondary.dark2}; - display: flex; - justify-items: center; -`; - -export default function Modal({ - children, - disablePrimaryButton = false, - onHandledPrimaryAction, - onHide, - primaryButtonName, - primaryButtonType = 'primary', - show, - title, -}: ModalProps) { - return ( - - - - {title} - - - - {children} - - - - - - - - - ); -} diff --git a/superset-frontend/src/datasource/ChangeDatasourceModal.tsx b/superset-frontend/src/datasource/ChangeDatasourceModal.tsx index 011be9c6b9..4f1597be90 100644 --- a/superset-frontend/src/datasource/ChangeDatasourceModal.tsx +++ b/superset-frontend/src/datasource/ChangeDatasourceModal.tsx @@ -16,11 +16,17 @@ * specific language governing permissions and limitations * under the License. */ -import React, { FunctionComponent, useState, useRef, useMemo } from 'react'; -import { Alert, FormControl, FormControlProps, Modal } from 'react-bootstrap'; +import React, { + FunctionComponent, + useState, + useRef, + useMemo, + useEffect, +} from 'react'; +import { Alert, FormControl, FormControlProps } from 'react-bootstrap'; import { SupersetClient, t } from '@superset-ui/core'; import TableView from 'src/components/TableView'; - +import Modal from 'src/common/components/Modal'; import getClientErrorObject from '../utils/getClientErrorObject'; import Loading from '../components/Loading'; import withToasts from '../messageToasts/enhancers/withToasts'; @@ -59,62 +65,68 @@ const ChangeDatasourceModal: FunctionComponent = ({ const [loading, setLoading] = useState(true); let searchRef = useRef(null); - const selectDatasource = (datasource: any) => { - SupersetClient.get({ - endpoint: `/datasource/get/${datasource.type}/${datasource.id}`, - }) - .then(({ json }) => { - onDatasourceSave(json); - onChange(datasource.uid); - }) - .catch(response => { - getClientErrorObject(response).then( - ({ error, message }: { error: any; message: string }) => { - const errorMessage = error - ? error.error || error.statusText || error - : message; - addDangerToast(errorMessage); - }, - ); - }); - onHide(); - }; - - const onEnterModal = () => { - if (searchRef && searchRef.current) { - searchRef.current.focus(); - } - if (!datasources) { + useEffect(() => { + const selectDatasource = (datasource: any) => { SupersetClient.get({ - endpoint: '/superset/datasources/', + endpoint: `/datasource/get/${datasource.type}/${datasource.id}`, }) .then(({ json }) => { - const data = json.map((ds: any) => ({ - rawName: ds.name, - connection: ds.connection, - schema: ds.schema, - name: ( - selectDatasource(ds)} - className="datasource-link" - > - {ds.name} - - ), - type: ds.type, - })); - setLoading(false); - setDatasources(data); + onDatasourceSave(json); + onChange(datasource.uid); }) .catch(response => { - setLoading(false); - getClientErrorObject(response).then(({ error }: any) => { - addDangerToast(error.error || error.statusText || error); - }); + getClientErrorObject(response).then( + ({ error, message }: { error: any; message: string }) => { + const errorMessage = error + ? error.error || error.statusText || error + : message; + addDangerToast(errorMessage); + }, + ); }); + onHide(); + }; + + const onEnterModal = () => { + if (searchRef && searchRef.current) { + searchRef.current.focus(); + } + if (!datasources) { + SupersetClient.get({ + endpoint: '/superset/datasources/', + }) + .then(({ json }) => { + const data = json.map((ds: any) => ({ + rawName: ds.name, + connection: ds.connection, + schema: ds.schema, + name: ( + selectDatasource(ds)} + className="datasource-link" + > + {ds.name} + + ), + type: ds.type, + })); + setLoading(false); + setDatasources(data); + }) + .catch(response => { + setLoading(false); + getClientErrorObject(response).then(({ error }: any) => { + addDangerToast(error.error || error.statusText || error); + }); + }); + } + }; + + if (show) { + onEnterModal(); } - }; + }, [addDangerToast, datasources, onChange, onDatasourceSave, onHide, show]); const setSearchRef = (ref: any) => { searchRef = ref; @@ -137,11 +149,14 @@ const ChangeDatasourceModal: FunctionComponent = ({ ); return ( - - - {t('Select a dataset')} - - + + <> {t('Warning!')} {CHANGE_WARNING_MSG} @@ -166,7 +181,7 @@ const ChangeDatasourceModal: FunctionComponent = ({ className="table-condensed" /> )} - + ); }; diff --git a/superset-frontend/src/datasource/DatasourceModal.tsx b/superset-frontend/src/datasource/DatasourceModal.tsx index 73fb87c549..2bd6baec47 100644 --- a/superset-frontend/src/datasource/DatasourceModal.tsx +++ b/superset-frontend/src/datasource/DatasourceModal.tsx @@ -17,10 +17,12 @@ * under the License. */ import React, { FunctionComponent, useState, useRef } from 'react'; -import { Alert, Modal } from 'react-bootstrap'; +import { Alert } from 'react-bootstrap'; import Button from 'src/components/Button'; import Dialog from 'react-bootstrap-dialog'; import { styled, t, SupersetClient } from '@superset-ui/core'; + +import Modal from 'src/common/components/Modal'; import AsyncEsmComponent from 'src/components/AsyncEsmComponent'; import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags'; @@ -163,29 +165,17 @@ const DatasourceModal: FunctionComponent = ({ }; return ( - - - -
- - {t('Edit Dataset ')} - {currentDatasource.table_name} - -
-
-
- - {show && ( - - )} - - - + + {t('Edit Dataset ')} + {currentDatasource.table_name} + + } + footer={ + <> {isFeatureEnabled(FeatureFlag.ENABLE_REACT_CRUD_VIEWS) && ( )} - - - - - + + } + responsive + > +
); }; diff --git a/superset-frontend/src/views/CRUD/data/dataset/AddDatasetModal.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDatasetModal.tsx index 2acf23849c..8c5458ded2 100644 --- a/superset-frontend/src/views/CRUD/data/dataset/AddDatasetModal.tsx +++ b/superset-frontend/src/views/CRUD/data/dataset/AddDatasetModal.tsx @@ -20,7 +20,7 @@ import React, { FunctionComponent, useState } from 'react'; import { styled, SupersetClient, t } from '@superset-ui/core'; import { isEmpty, isNil } from 'lodash'; import Icon from 'src/components/Icon'; -import Modal from 'src/components/Modal'; +import Modal from 'src/common/components/Modal'; import TableSelector from 'src/components/TableSelector'; import withToasts from 'src/messageToasts/enhancers/withToasts'; import { createErrorHandler } from 'src/views/CRUD/utils';