refactor: Replace react-bootstrap Modals with Antd in Datasource (#11390)

* ChangeDatasourceModal

* DatasourceModal

* DeleteModal, AddDatasetModal

* Fix tests

* Remove unused file

* Fix e2e tests

* Fix test

* Remove it.only

* Fix after rebase

* Fix e2e tests

* Fix data-test in modal

* Lint fix
This commit is contained in:
Kamil Gabryjelski 2020-10-28 22:42:13 +01:00 committed by GitHub
parent e230865216
commit 52294c836a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 119 additions and 209 deletions

View File

@ -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',
);

View File

@ -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', () => {

View File

@ -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}"]`)

View File

@ -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 = {

View File

@ -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';

View File

@ -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', () => {

View File

@ -124,7 +124,7 @@ export default function Modal({
}: ModalProps) {
const modalFooter = isNil(footer)
? [
<Button key="back" onClick={onHide} cta>
<Button key="back" onClick={onHide} cta data-test="modal-cancel-button">
{t('Cancel')}
</Button>,
<Button
@ -133,6 +133,7 @@ export default function Modal({
disabled={disablePrimaryButton}
onClick={onHandledPrimaryAction}
cta
data-test="modal-confirm-button"
>
{primaryButtonName}
</Button>,
@ -156,7 +157,7 @@ export default function Modal({
</span>
}
footer={!hideFooter ? modalFooter : null}
data-test={`${title}-modal`}
wrapProps={{ 'data-test': `${title}-modal` }}
{...rest}
>
{children}

View File

@ -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)`

View File

@ -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 (
<StyledModal show={show} onHide={onHide} data-test={`${title}-modal`}>
<BaseModal.Header data-test={`${title}-modal-header`} closeButton>
<BaseModal.Title>
<Title>{title}</Title>
</BaseModal.Title>
</BaseModal.Header>
<BaseModal.Body data-test={`${title}-modal-body`}>
{children}
</BaseModal.Body>
<BaseModal.Footer data-test={`${title}-modal-footer`}>
<span className="float-right">
<Button data-test="modal-cancel-button" onClick={onHide} cta>
{t('Cancel')}
</Button>
<Button
data-test="modal-delete-button"
buttonStyle={primaryButtonType}
disabled={disablePrimaryButton}
onClick={onHandledPrimaryAction}
cta
>
{primaryButtonName}
</Button>
</span>
</BaseModal.Footer>
</StyledModal>
);
}

View File

@ -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<ChangeDatasourceModalProps> = ({
const [loading, setLoading] = useState(true);
let searchRef = useRef<HTMLInputElement>(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: (
<a
href="#"
onClick={() => selectDatasource(ds)}
className="datasource-link"
>
{ds.name}
</a>
),
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: (
<a
href="#"
onClick={() => selectDatasource(ds)}
className="datasource-link"
>
{ds.name}
</a>
),
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<ChangeDatasourceModalProps> = ({
);
return (
<Modal show={show} onHide={onHide} onEnter={onEnterModal} bsSize="large">
<Modal.Header closeButton>
<Modal.Title>{t('Select a dataset')}</Modal.Title>
</Modal.Header>
<Modal.Body>
<Modal
show={show}
onHide={onHide}
responsive
title={t('Select a dataset')}
hideFooter
>
<>
<Alert bsStyle="warning">
<strong>{t('Warning!')}</strong> {CHANGE_WARNING_MSG}
</Alert>
@ -166,7 +181,7 @@ const ChangeDatasourceModal: FunctionComponent<ChangeDatasourceModalProps> = ({
className="table-condensed"
/>
)}
</Modal.Body>
</>
</Modal>
);
};

View File

@ -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<DatasourceModalProps> = ({
};
return (
<StyledDatasourceModal show={show} onHide={onHide} bsSize="large">
<Modal.Header closeButton>
<Modal.Title>
<div>
<span className="float-left">
{t('Edit Dataset ')}
<strong>{currentDatasource.table_name}</strong>
</span>
</div>
</Modal.Title>
</Modal.Header>
<Modal.Body>
{show && (
<DatasourceEditor
showLoadingForImport
height={500}
datasource={currentDatasource}
onChange={onDatasourceChange}
/>
)}
</Modal.Body>
<Modal.Footer>
<span className="float-left">
<StyledDatasourceModal
show={show}
onHide={onHide}
title={
<span>
{t('Edit Dataset ')}
<strong>{currentDatasource.table_name}</strong>
</span>
}
footer={
<>
{isFeatureEnabled(FeatureFlag.ENABLE_REACT_CRUD_VIEWS) && (
<Button
buttonSize="sm"
@ -200,9 +190,6 @@ const DatasourceModal: FunctionComponent<DatasourceModalProps> = ({
{t('Use Legacy Datasource Editor')}
</Button>
)}
</span>
<span className="float-right">
<Button
buttonSize="sm"
buttonStyle="primary"
@ -221,8 +208,16 @@ const DatasourceModal: FunctionComponent<DatasourceModalProps> = ({
{t('Cancel')}
</Button>
<Dialog ref={dialog} />
</span>
</Modal.Footer>
</>
}
responsive
>
<DatasourceEditor
showLoadingForImport
height={500}
datasource={currentDatasource}
onChange={onDatasourceChange}
/>
</StyledDatasourceModal>
);
};

View File

@ -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';