parent
7ee8d114d2
commit
42ab57850d
|
|
@ -188,6 +188,7 @@ module.exports = {
|
|||
],
|
||||
'jest/consistent-test-it': 'error',
|
||||
'no-only-tests/no-only-tests': 'error',
|
||||
'@typescript-eslint/no-non-null-assertion': 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ describe('Visualization > Line', () => {
|
|||
it('should show validator error when no metric', () => {
|
||||
const formData = { ...LINE_CHART_DEFAULTS, metrics: [] };
|
||||
cy.visitChartByParams(JSON.stringify(formData));
|
||||
cy.get('.alert-warning').contains(`"Metrics" cannot be empty`);
|
||||
cy.get('.ant-alert-warning').contains(`"Metrics" cannot be empty`);
|
||||
});
|
||||
|
||||
it('should preload mathjs', () => {
|
||||
|
|
@ -43,7 +43,7 @@ describe('Visualization > Line', () => {
|
|||
it('should not show validator error when metric added', () => {
|
||||
const formData = { ...LINE_CHART_DEFAULTS, metrics: [] };
|
||||
cy.visitChartByParams(JSON.stringify(formData));
|
||||
cy.get('.alert-warning').contains(`"Metrics" cannot be empty`);
|
||||
cy.get('.ant-alert-warning').contains(`"Metrics" cannot be empty`);
|
||||
cy.get('.text-danger').contains('Metrics');
|
||||
|
||||
cy.get('[data-test=metrics]')
|
||||
|
|
@ -58,14 +58,14 @@ describe('Visualization > Line', () => {
|
|||
cy.get('[data-test="AdhocMetricEdit#save"]').contains('Save').click();
|
||||
|
||||
cy.get('.text-danger').should('not.exist');
|
||||
cy.get('.alert-warning').should('not.exist');
|
||||
cy.get('.ant-alert-warning').should('not.exist');
|
||||
});
|
||||
|
||||
it('should allow negative values in Y bounds', () => {
|
||||
cy.get('#controlSections-tab-display').click();
|
||||
cy.get('span').contains('Y Axis Bounds').scrollIntoView();
|
||||
cy.get('input[placeholder="Min"]').type('-0.1', { delay: 100 });
|
||||
cy.get('.alert-warning').should('not.exist');
|
||||
cy.get('.ant-alert-warning').should('not.exist');
|
||||
});
|
||||
|
||||
it('should allow type to search color schemes', () => {
|
||||
|
|
|
|||
|
|
@ -52,5 +52,5 @@ export function styledShallow(
|
|||
theme: supersetTheme,
|
||||
...options?.wrappingComponentProps,
|
||||
},
|
||||
}).dive();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -296,11 +296,7 @@ describe('ListView', () => {
|
|||
});
|
||||
|
||||
it('allows disabling bulkSelect', () => {
|
||||
wrapper
|
||||
.find('[data-test="bulk-select-controls"]')
|
||||
.at(0)
|
||||
.props()
|
||||
.onDismiss();
|
||||
wrapper.find('[data-test="bulk-select-controls"]').at(0).props().onClose();
|
||||
expect(mockedProps.disableBulkSelect).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import { mount } from 'enzyme';
|
|||
|
||||
import ModalTrigger from 'src/components/ModalTrigger';
|
||||
import RefreshIntervalModal from 'src/dashboard/components/RefreshIntervalModal';
|
||||
import { Alert } from 'react-bootstrap';
|
||||
import Alert from 'src/components/Alert';
|
||||
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
|
||||
|
||||
const getMountWrapper = props =>
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ import { styledMount as mount } from 'spec/helpers/theming';
|
|||
import { act } from 'react-dom/test-utils';
|
||||
import { ReactWrapper } from 'enzyme';
|
||||
import { Provider } from 'react-redux';
|
||||
import Alert from 'react-bootstrap/lib/Alert';
|
||||
import { FilterConfigModal } from 'src/dashboard/components/nativeFilters/FilterConfigModal/FilterConfigModal';
|
||||
import Alert from 'src/components/Alert';
|
||||
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
|
||||
import { mockStore } from 'spec/fixtures/mockStore';
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { Alert } from 'react-bootstrap';
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Toast from 'src/messageToasts/components/Toast';
|
||||
|
|
@ -31,20 +30,19 @@ const props = {
|
|||
const setup = overrideProps => mount(<Toast {...props} {...overrideProps} />);
|
||||
|
||||
describe('Toast', () => {
|
||||
it('should render an Alert', () => {
|
||||
it('should render', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(Alert)).toExist();
|
||||
expect(wrapper.find('[data-test="toast-container"]')).toExist();
|
||||
});
|
||||
|
||||
it('should render toastText within the alert', () => {
|
||||
it('should render toastText within the div', () => {
|
||||
const wrapper = setup();
|
||||
const alert = wrapper.find(Alert);
|
||||
|
||||
expect(alert.childAt(0).childAt(1).text()).toBe(props.toast.text);
|
||||
const container = wrapper.find('[data-test="toast-container"]');
|
||||
expect(container.hostNodes().childAt(1).text()).toBe(props.toast.text);
|
||||
});
|
||||
|
||||
it('should call onCloseToast upon alert dismissal', async () => {
|
||||
await act(
|
||||
it('should call onCloseToast upon toast dismissal', async () =>
|
||||
act(
|
||||
() =>
|
||||
new Promise(done => {
|
||||
const onCloseToast = id => {
|
||||
|
|
@ -53,13 +51,7 @@ describe('Toast', () => {
|
|||
};
|
||||
|
||||
const wrapper = setup({ onCloseToast });
|
||||
const handleClosePress = wrapper.find('[label="Close alert"]').props()
|
||||
.onClick;
|
||||
|
||||
const alertProps = wrapper.find(Alert).props();
|
||||
expect(alertProps.onDismiss).toBe(handleClosePress);
|
||||
handleClosePress(); // there is a timeout for onCloseToast to be called
|
||||
wrapper.find('[data-test="close-button"]').props().onClick();
|
||||
}),
|
||||
);
|
||||
});
|
||||
));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,10 +18,13 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { styledMount } from 'spec/helpers/theming';
|
||||
import { Provider } from 'react-redux';
|
||||
import sinon from 'sinon';
|
||||
import { Alert } from 'react-bootstrap';
|
||||
import Alert from 'src/components/Alert';
|
||||
import ProgressBar from 'src/common/components/ProgressBar';
|
||||
|
||||
import configureStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import FilterableTable from 'src/components/FilterableTable/FilterableTable';
|
||||
import ExploreResultsButton from 'src/SqlLab/components/ExploreResultsButton';
|
||||
import ResultSet from 'src/SqlLab/components/ResultSet';
|
||||
|
|
@ -33,8 +36,12 @@ import {
|
|||
queries,
|
||||
runningQuery,
|
||||
stoppedQuery,
|
||||
initialState,
|
||||
} from './fixtures';
|
||||
|
||||
const mockStore = configureStore([thunk]);
|
||||
const store = mockStore(initialState);
|
||||
|
||||
describe('ResultSet', () => {
|
||||
const clearQuerySpy = sinon.spy();
|
||||
const fetchQuerySpy = sinon.spy();
|
||||
|
|
@ -105,17 +112,18 @@ describe('ResultSet', () => {
|
|||
expect(wrapper.find(ExploreResultsButton)).toExist();
|
||||
});
|
||||
it('should render empty results', () => {
|
||||
const wrapper = shallow(<ResultSet {...mockedProps} />);
|
||||
const emptyResults = {
|
||||
...queries[0],
|
||||
results: {
|
||||
data: [],
|
||||
},
|
||||
const props = {
|
||||
...mockedProps,
|
||||
query: { ...mockedProps.query, results: { data: [] } },
|
||||
};
|
||||
wrapper.setProps({ query: emptyResults });
|
||||
const wrapper = styledMount(
|
||||
<Provider store={store}>
|
||||
<ResultSet {...props} />
|
||||
</Provider>,
|
||||
);
|
||||
expect(wrapper.find(FilterableTable)).not.toExist();
|
||||
expect(wrapper.find(Alert)).toExist();
|
||||
expect(wrapper.find(Alert).shallow().text()).toBe(
|
||||
expect(wrapper.find(Alert).render().text()).toBe(
|
||||
'The query returned no data',
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -81,12 +81,12 @@ describe('SouthPane', () => {
|
|||
let wrapper;
|
||||
|
||||
it('should render offline when the state is offline', () => {
|
||||
wrapper = getWrapper();
|
||||
wrapper = getWrapper().dive();
|
||||
wrapper.setProps({ offline: true });
|
||||
expect(wrapper.childAt(0).text()).toBe(STATUS_OPTIONS.offline);
|
||||
});
|
||||
it('should pass latest query down to ResultSet component', () => {
|
||||
wrapper = getWrapper();
|
||||
wrapper = getWrapper().dive();
|
||||
expect(wrapper.find(ResultSet)).toExist();
|
||||
expect(wrapper.find(ResultSet).props().query.id).toEqual(
|
||||
mockedProps.latestQueryId,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
import React, { useMemo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Alert } from 'react-bootstrap';
|
||||
import Alert from 'src/components/Alert';
|
||||
import { t } from '@superset-ui/core';
|
||||
|
||||
import TableView from 'src/components/TableView';
|
||||
|
|
@ -61,9 +61,11 @@ const EstimateQueryCostButton = props => {
|
|||
const renderModalBody = () => {
|
||||
if (props.queryCostEstimate.error !== null) {
|
||||
return (
|
||||
<Alert key="query-estimate-error" bsStyle="danger">
|
||||
{props.queryCostEstimate.error}
|
||||
</Alert>
|
||||
<Alert
|
||||
key="query-estimate-error"
|
||||
type="error"
|
||||
message={props.queryCostEstimate.error}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (props.queryCostEstimate.completed) {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { Alert } from 'react-bootstrap';
|
||||
import Alert from 'src/components/Alert';
|
||||
import { t } from '@superset-ui/core';
|
||||
import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls';
|
||||
import shortid from 'shortid';
|
||||
|
|
@ -102,25 +102,32 @@ class ExploreResultsButton extends React.PureComponent {
|
|||
|
||||
renderTimeoutWarning() {
|
||||
return (
|
||||
<Alert bsStyle="warning">
|
||||
{t(
|
||||
'This query took %s seconds to run, ',
|
||||
Math.round(this.getQueryDuration()),
|
||||
) +
|
||||
t(
|
||||
'and the explore view times out at %s seconds ',
|
||||
this.props.timeout,
|
||||
) +
|
||||
t(
|
||||
'following this flow will most likely lead to your query timing out. ',
|
||||
) +
|
||||
t(
|
||||
'We recommend your summarize your data further before following that flow. ',
|
||||
) +
|
||||
t('If activated you can use the ')}
|
||||
<strong>CREATE TABLE AS </strong>
|
||||
{t('feature to store a summarized data set that you can then explore.')}
|
||||
</Alert>
|
||||
<Alert
|
||||
type="warning"
|
||||
message={
|
||||
<>
|
||||
{t(
|
||||
'This query took %s seconds to run, ',
|
||||
Math.round(this.getQueryDuration()),
|
||||
) +
|
||||
t(
|
||||
'and the explore view times out at %s seconds ',
|
||||
this.props.timeout,
|
||||
) +
|
||||
t(
|
||||
'following this flow will most likely lead to your query timing out. ',
|
||||
) +
|
||||
t(
|
||||
'We recommend your summarize your data further before following that flow. ',
|
||||
) +
|
||||
t('If activated you can use the ')}
|
||||
<strong>CREATE TABLE AS </strong>
|
||||
{t(
|
||||
'feature to store a summarized data set that you can then explore.',
|
||||
)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Alert } from 'react-bootstrap';
|
||||
import Alert from 'src/components/Alert';
|
||||
import { t } from '@superset-ui/core';
|
||||
|
||||
import QueryTable from './QueryTable';
|
||||
|
|
@ -49,7 +49,7 @@ const QueryHistory = props => {
|
|||
/>
|
||||
);
|
||||
}
|
||||
return <Alert bsStyle="info">{t('No query history yet...')}</Alert>;
|
||||
return <Alert type="info" message={t('No query history yet...')} />;
|
||||
};
|
||||
QueryHistory.propTypes = propTypes;
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React, { CSSProperties } from 'react';
|
||||
import { Alert } from 'react-bootstrap';
|
||||
import ButtonGroup from 'src/components/ButtonGroup';
|
||||
import Alert from 'src/components/Alert';
|
||||
import ProgressBar from 'src/common/components/ProgressBar';
|
||||
import moment from 'moment';
|
||||
import { RadioChangeEvent } from 'antd/lib/radio';
|
||||
|
|
@ -498,7 +498,7 @@ export default class ResultSet extends React.PureComponent<
|
|||
}
|
||||
|
||||
if (query.state === 'stopped') {
|
||||
return <Alert bsStyle="warning">Query was stopped</Alert>;
|
||||
return <Alert type="warning" message={t('Query was stopped')} />;
|
||||
}
|
||||
if (query.state === 'failed') {
|
||||
return (
|
||||
|
|
@ -522,31 +522,36 @@ export default class ResultSet extends React.PureComponent<
|
|||
}
|
||||
return (
|
||||
<div>
|
||||
<Alert bsStyle="info">
|
||||
{t(object)} [
|
||||
<strong>
|
||||
{tempSchema ? `${tempSchema}.` : ''}
|
||||
{tempTable}
|
||||
</strong>
|
||||
] {t('was created')}
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
buttonSize="small"
|
||||
className="m-r-5"
|
||||
onClick={() => this.popSelectStar(tempSchema, tempTable)}
|
||||
>
|
||||
{t('Query in a new tab')}
|
||||
</Button>
|
||||
<ExploreCtasResultsButton
|
||||
// @ts-ignore Redux types are difficult to work with, ignoring for now
|
||||
table={tempTable}
|
||||
schema={tempSchema}
|
||||
dbId={exploreDBId}
|
||||
database={this.props.database}
|
||||
actions={this.props.actions}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
</Alert>
|
||||
<Alert
|
||||
type="info"
|
||||
message={
|
||||
<>
|
||||
{t(object)} [
|
||||
<strong>
|
||||
{tempSchema ? `${tempSchema}.` : ''}
|
||||
{tempTable}
|
||||
</strong>
|
||||
] {t('was created')}
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
buttonSize="small"
|
||||
className="m-r-5"
|
||||
onClick={() => this.popSelectStar(tempSchema, tempTable)}
|
||||
>
|
||||
{t('Query in a new tab')}
|
||||
</Button>
|
||||
<ExploreCtasResultsButton
|
||||
// @ts-ignore Redux types are difficult to work with, ignoring for now
|
||||
table={tempTable}
|
||||
schema={tempSchema}
|
||||
dbId={exploreDBId}
|
||||
database={this.props.database}
|
||||
actions={this.props.actions}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -578,7 +583,7 @@ export default class ResultSet extends React.PureComponent<
|
|||
}
|
||||
if (data && data.length === 0) {
|
||||
return (
|
||||
<Alert bsStyle="warning">{t('The query returned no data')}</Alert>
|
||||
<Alert type="warning" message={t('The query returned no data')} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -642,7 +647,7 @@ export default class ResultSet extends React.PureComponent<
|
|||
<div>{!progressBar && <Loading position="normal" />}</div>
|
||||
<QueryStateLabel query={query} />
|
||||
<div>
|
||||
{progressMsg && <Alert bsStyle="success">{progressMsg}</Alert>}
|
||||
{progressMsg && <Alert type="success" message={progressMsg} />}
|
||||
</div>
|
||||
<div>{progressBar}</div>
|
||||
<div>{trackingUrl}</div>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import shortid from 'shortid';
|
||||
import { Alert } from 'react-bootstrap';
|
||||
import Alert from 'src/components/Alert';
|
||||
import Tabs from 'src/common/components/Tabs';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
|
|
@ -120,9 +120,12 @@ export class SouthPane extends React.PureComponent {
|
|||
!latestQuery.results
|
||||
) {
|
||||
results = (
|
||||
<Alert bsStyle="warning">
|
||||
{t('No stored results found, you need to re-run your query')}
|
||||
</Alert>
|
||||
<Alert
|
||||
type="warning"
|
||||
message={t(
|
||||
'No stored results found, you need to re-run your query',
|
||||
)}
|
||||
/>
|
||||
);
|
||||
} else if (
|
||||
Date.now() - latestQuery.startDttm <=
|
||||
|
|
@ -142,7 +145,7 @@ export class SouthPane extends React.PureComponent {
|
|||
}
|
||||
} else {
|
||||
results = (
|
||||
<Alert bsStyle="info">{t('Run a query to display results here')}</Alert>
|
||||
<Alert type="info" message={t('Run a query to display results here')} />
|
||||
);
|
||||
}
|
||||
const dataPreviewTabs = props.dataPreviewQueries.map(query => (
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { Alert } from 'react-bootstrap';
|
||||
import Alert from 'src/components/Alert';
|
||||
import { styled, logging } from '@superset-ui/core';
|
||||
|
||||
import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
|
||||
|
|
@ -193,9 +193,11 @@ class Chart extends React.PureComponent {
|
|||
}
|
||||
if (errorMessage) {
|
||||
return (
|
||||
<Alert data-test="alert-warning" bsStyle="warning">
|
||||
{errorMessage}
|
||||
</Alert>
|
||||
<Alert
|
||||
data-test="alert-warning"
|
||||
message={errorMessage}
|
||||
type="warning"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ import { DropDownProps } from 'antd/lib/dropdown';
|
|||
*/
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
export {
|
||||
Alert,
|
||||
AutoComplete,
|
||||
Avatar,
|
||||
Button,
|
||||
|
|
@ -57,6 +56,7 @@ export {
|
|||
Tooltip,
|
||||
Input as AntdInput,
|
||||
} from 'antd';
|
||||
export { default as Alert, AlertProps } from 'antd/lib/alert';
|
||||
export { TreeProps } from 'antd/lib/tree';
|
||||
export { FormInstance } from 'antd/lib/form';
|
||||
export { RadioChangeEvent } from 'antd/lib/radio';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
/**
|
||||
* 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 Alert, { AlertProps } from './index';
|
||||
|
||||
type AlertType = Pick<AlertProps, 'type'>;
|
||||
type AlertTypeValue = AlertType[keyof AlertType];
|
||||
|
||||
const types: AlertTypeValue[] = ['info', 'error', 'warning', 'success'];
|
||||
|
||||
const smallText = 'Lorem ipsum dolor sit amet';
|
||||
|
||||
const bigText =
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. ' +
|
||||
'Nam id porta neque, a vehicula orci. Maecenas rhoncus elit sit amet ' +
|
||||
'purus convallis placerat in at nunc. Nulla nec viverra augue.';
|
||||
|
||||
export default {
|
||||
title: 'Alert',
|
||||
component: Alert,
|
||||
};
|
||||
|
||||
export const AlertGallery = () => (
|
||||
<>
|
||||
{types.map(type => (
|
||||
<div key={type} style={{ marginBottom: 40, width: 600 }}>
|
||||
<h4>{type}</h4>
|
||||
<Alert
|
||||
type={type}
|
||||
showIcon
|
||||
closable
|
||||
message={bigText}
|
||||
style={{ marginBottom: 20 }}
|
||||
/>
|
||||
<Alert
|
||||
type={type}
|
||||
showIcon
|
||||
message={smallText}
|
||||
description={bigText}
|
||||
closable
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
AlertGallery.story = {
|
||||
parameters: {
|
||||
actions: {
|
||||
disabled: true,
|
||||
},
|
||||
controls: {
|
||||
disabled: true,
|
||||
},
|
||||
knobs: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const InteractiveAlert = (args: AlertProps) => <Alert {...args} />;
|
||||
|
||||
InteractiveAlert.args = {
|
||||
closable: true,
|
||||
type: 'info',
|
||||
message: smallText,
|
||||
description: bigText,
|
||||
showIcon: true,
|
||||
};
|
||||
|
||||
InteractiveAlert.argTypes = {
|
||||
onClose: { action: 'onClose' },
|
||||
type: {
|
||||
control: { type: 'select', options: types },
|
||||
},
|
||||
};
|
||||
|
||||
InteractiveAlert.story = {
|
||||
parameters: {
|
||||
knobs: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/**
|
||||
* 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 { render, screen } from 'spec/helpers/testing-library';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import Alert, { AlertProps } from 'src/components/Alert';
|
||||
|
||||
type AlertType = Pick<AlertProps, 'type'>;
|
||||
type AlertTypeValue = AlertType[keyof AlertType];
|
||||
|
||||
test('renders with default props', () => {
|
||||
render(<Alert message="Message" />);
|
||||
expect(screen.getByRole('alert')).toHaveTextContent('Message');
|
||||
expect(screen.queryByLabelText(`info icon`)).toBeInTheDocument();
|
||||
expect(screen.queryByLabelText('close icon')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders each type', () => {
|
||||
const types: AlertTypeValue[] = ['info', 'error', 'warning', 'success'];
|
||||
types.forEach(type => {
|
||||
render(<Alert type={type} message="Message" />);
|
||||
expect(screen.queryByLabelText(`${type} icon`)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test('renders without close button', () => {
|
||||
render(<Alert message="Message" closable={false} />);
|
||||
expect(screen.queryByLabelText('close icon')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('disappear when closed', () => {
|
||||
render(<Alert message="Message" />);
|
||||
userEvent.click(screen.queryByLabelText('close icon')!);
|
||||
expect(screen.queryByRole('alert')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders without icon', () => {
|
||||
const type = 'info';
|
||||
render(<Alert type={type} message="Message" showIcon={false} />);
|
||||
expect(screen.queryByLabelText(`${type} icon`)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders message', () => {
|
||||
render(<Alert message="Message" />);
|
||||
expect(screen.getByRole('alert')).toHaveTextContent('Message');
|
||||
});
|
||||
|
||||
test('renders message and description', () => {
|
||||
render(<Alert message="Message" description="Description" />);
|
||||
expect(screen.getByRole('alert')).toHaveTextContent('Message');
|
||||
expect(screen.getByRole('alert')).toHaveTextContent('Description');
|
||||
});
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
* 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, { PropsWithChildren } from 'react';
|
||||
import {
|
||||
Alert as AntdAlert,
|
||||
AlertProps as AntdAlertProps,
|
||||
} from 'src/common/components';
|
||||
import { useTheme } from '@superset-ui/core';
|
||||
import Icon, { IconName } from 'src/components/Icon';
|
||||
|
||||
export type AlertProps = PropsWithChildren<AntdAlertProps>;
|
||||
|
||||
export default function Alert(props: AlertProps) {
|
||||
const {
|
||||
type = 'info',
|
||||
description,
|
||||
showIcon = true,
|
||||
closable = true,
|
||||
children,
|
||||
} = props;
|
||||
|
||||
const theme = useTheme();
|
||||
const { colors, typography } = theme;
|
||||
const { alert, error, info, success } = colors;
|
||||
|
||||
let baseColor = info;
|
||||
let iconName: IconName = 'info-solid';
|
||||
if (type === 'error') {
|
||||
baseColor = error;
|
||||
iconName = 'error-solid';
|
||||
} else if (type === 'warning') {
|
||||
baseColor = alert;
|
||||
iconName = 'alert-solid';
|
||||
} else if (type === 'success') {
|
||||
baseColor = success;
|
||||
iconName = 'circle-check-solid';
|
||||
}
|
||||
|
||||
return (
|
||||
<AntdAlert
|
||||
role="alert"
|
||||
showIcon={showIcon}
|
||||
icon={<Icon name={iconName} aria-label={`${type} icon`} />}
|
||||
closeText={closable && <Icon name="x-small" aria-label="close icon" />}
|
||||
css={{
|
||||
padding: '6px 10px',
|
||||
alignItems: 'flex-start',
|
||||
border: 0,
|
||||
backgroundColor: baseColor.light2,
|
||||
'& .ant-alert-icon': {
|
||||
marginRight: 10,
|
||||
},
|
||||
'& .ant-alert-message': {
|
||||
color: baseColor.dark2,
|
||||
fontSize: typography.sizes.m,
|
||||
fontWeight: description
|
||||
? typography.weights.bold
|
||||
: typography.weights.normal,
|
||||
},
|
||||
'& .ant-alert-description': {
|
||||
color: baseColor.dark2,
|
||||
fontSize: typography.sizes.m,
|
||||
},
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</AntdAlert>
|
||||
);
|
||||
}
|
||||
|
|
@ -43,7 +43,9 @@ describe('Checkbox', () => {
|
|||
const shallowWrapper = shallow(
|
||||
<Checkbox style={{}} checked={false} onChange={() => true} />,
|
||||
);
|
||||
expect(shallowWrapper.dive().dive().find(CheckboxUnchecked)).toExist();
|
||||
expect(
|
||||
shallowWrapper.dive().dive().dive().find(CheckboxUnchecked),
|
||||
).toExist();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -52,7 +54,9 @@ describe('Checkbox', () => {
|
|||
const shallowWrapper = shallow(
|
||||
<Checkbox style={{}} checked onChange={() => true} />,
|
||||
);
|
||||
expect(shallowWrapper.dive().dive().find(CheckboxChecked)).toExist();
|
||||
expect(
|
||||
shallowWrapper.dive().dive().dive().find(CheckboxChecked),
|
||||
).toExist();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@
|
|||
*/
|
||||
import { t, styled } from '@superset-ui/core';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Alert } from 'react-bootstrap';
|
||||
import { Empty } from 'src/common/components';
|
||||
import Alert from 'src/components/Alert';
|
||||
import { ReactComponent as EmptyImage } from 'images/empty.svg';
|
||||
import cx from 'classnames';
|
||||
import Button from 'src/components/Button';
|
||||
|
|
@ -91,15 +91,12 @@ const ListViewStyles = styled.div`
|
|||
const BulkSelectWrapper = styled(Alert)`
|
||||
border-radius: 0;
|
||||
margin-bottom: 0;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
padding-right: ${({ theme }) => theme.gridUnit * 9}px;
|
||||
color: #3d3d3d;
|
||||
background-color: ${({ theme }) => theme.colors.primary.light4};
|
||||
|
||||
.selectedCopy {
|
||||
display: inline-block;
|
||||
padding: ${({ theme }) => theme.gridUnit * 4}px 0;
|
||||
padding: ${({ theme }) => theme.gridUnit * 2}px 0;
|
||||
}
|
||||
|
||||
.deselect-all {
|
||||
|
|
@ -117,10 +114,6 @@ const BulkSelectWrapper = styled(Alert)`
|
|||
vertical-align: middle;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.close {
|
||||
margin: ${({ theme }) => theme.gridUnit * 4}px 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const bulkSelectColumnConfig = {
|
||||
|
|
@ -330,40 +323,47 @@ function ListView<T extends object = any>({
|
|||
{bulkSelectEnabled && (
|
||||
<BulkSelectWrapper
|
||||
data-test="bulk-select-controls"
|
||||
bsStyle="info"
|
||||
onDismiss={disableBulkSelect}
|
||||
>
|
||||
<div className="selectedCopy" data-test="bulk-select-copy">
|
||||
{renderBulkSelectCopy(selectedFlatRows)}
|
||||
</div>
|
||||
{Boolean(selectedFlatRows.length) && (
|
||||
type="info"
|
||||
closable
|
||||
showIcon={false}
|
||||
onClose={disableBulkSelect}
|
||||
message={
|
||||
<>
|
||||
<span
|
||||
data-test="bulk-select-deselect-all"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className="deselect-all"
|
||||
onClick={() => toggleAllRowsSelected(false)}
|
||||
>
|
||||
{t('Deselect all')}
|
||||
</span>
|
||||
<div className="divider" />
|
||||
{bulkActions.map(action => (
|
||||
<Button
|
||||
data-test="bulk-select-action"
|
||||
key={action.key}
|
||||
buttonStyle={action.type}
|
||||
cta
|
||||
onClick={() =>
|
||||
action.onSelect(selectedFlatRows.map(r => r.original))
|
||||
}
|
||||
>
|
||||
{action.name}
|
||||
</Button>
|
||||
))}
|
||||
<div className="selectedCopy" data-test="bulk-select-copy">
|
||||
{renderBulkSelectCopy(selectedFlatRows)}
|
||||
</div>
|
||||
{Boolean(selectedFlatRows.length) && (
|
||||
<>
|
||||
<span
|
||||
data-test="bulk-select-deselect-all"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className="deselect-all"
|
||||
onClick={() => toggleAllRowsSelected(false)}
|
||||
>
|
||||
{t('Deselect all')}
|
||||
</span>
|
||||
<div className="divider" />
|
||||
{bulkActions.map(action => (
|
||||
<Button
|
||||
data-test="bulk-select-action"
|
||||
key={action.key}
|
||||
buttonStyle={action.type}
|
||||
cta
|
||||
onClick={() =>
|
||||
action.onSelect(
|
||||
selectedFlatRows.map(r => r.original),
|
||||
)
|
||||
}
|
||||
>
|
||||
{action.name}
|
||||
</Button>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</BulkSelectWrapper>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{viewMode === 'card' && (
|
||||
<CardCollection
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
import React, { RefObject } from 'react';
|
||||
import Select from 'src/components/Select';
|
||||
import { t, styled } from '@superset-ui/core';
|
||||
import { Alert } from 'react-bootstrap';
|
||||
import Alert from 'src/components/Alert';
|
||||
import Button from 'src/components/Button';
|
||||
|
||||
import ModalTrigger from 'src/components/ModalTrigger';
|
||||
|
|
@ -124,11 +124,16 @@ class RefreshIntervalModal extends React.PureComponent<
|
|||
/>
|
||||
{showRefreshWarning && (
|
||||
<RefreshWarningContainer>
|
||||
<Alert bsStyle="warning">
|
||||
<div>{refreshWarning}</div>
|
||||
<br />
|
||||
<strong>{t('Are you sure you want to proceed?')}</strong>
|
||||
</Alert>
|
||||
<Alert
|
||||
type="warning"
|
||||
message={
|
||||
<>
|
||||
<div>{refreshWarning}</div>
|
||||
<br />
|
||||
<strong>{t('Are you sure you want to proceed?')}</strong>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</RefreshWarningContainer>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -17,48 +17,9 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { styled, t } from '@superset-ui/core';
|
||||
import Alert from 'react-bootstrap/lib/Alert';
|
||||
import { t } from '@superset-ui/core';
|
||||
import Alert from 'src/components/Alert';
|
||||
import Button from 'src/components/Button';
|
||||
import Icon from 'src/components/Icon';
|
||||
|
||||
const StyledAlert = styled(Alert)`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: ${({ theme }) => theme.gridUnit * 2}px;
|
||||
padding: ${({ theme }) => theme.gridUnit * 2}px;
|
||||
`;
|
||||
|
||||
const StyledTextContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: left;
|
||||
margin-right: ${({ theme }) => theme.gridUnit}px;
|
||||
`;
|
||||
|
||||
const StyledTitleBox = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const StyledAlertTitle = styled.span`
|
||||
font-weight: ${({ theme }) => theme.typography.weights.bold};
|
||||
`;
|
||||
|
||||
const StyledAlertText = styled.p`
|
||||
margin-left: ${({ theme }) => theme.gridUnit * 9}px;
|
||||
`;
|
||||
|
||||
const StyledButtonsContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
`;
|
||||
|
||||
const StyledAlertIcon = styled(Icon)`
|
||||
color: ${({ theme }) => theme.colors.alert.base};
|
||||
margin-right: ${({ theme }) => theme.gridUnit * 3}px;
|
||||
`;
|
||||
|
||||
export interface ConfirmationAlertProps {
|
||||
title: string;
|
||||
|
|
@ -74,32 +35,35 @@ export function CancelConfirmationAlert({
|
|||
children,
|
||||
}: ConfirmationAlertProps) {
|
||||
return (
|
||||
<StyledAlert bsStyle="warning" key="alert">
|
||||
<StyledTextContainer>
|
||||
<StyledTitleBox>
|
||||
<StyledAlertIcon name="alert-solid" />
|
||||
<StyledAlertTitle>{title}</StyledAlertTitle>
|
||||
</StyledTitleBox>
|
||||
<StyledAlertText>{children}</StyledAlertText>
|
||||
</StyledTextContainer>
|
||||
<StyledButtonsContainer>
|
||||
<Button
|
||||
key="submit"
|
||||
buttonSize="small"
|
||||
buttonStyle="primary"
|
||||
onClick={onConfirm}
|
||||
>
|
||||
{t('Yes, cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
key="cancel"
|
||||
buttonSize="small"
|
||||
buttonStyle="secondary"
|
||||
onClick={onDismiss}
|
||||
>
|
||||
{t('Keep editing')}
|
||||
</Button>
|
||||
</StyledButtonsContainer>
|
||||
</StyledAlert>
|
||||
<Alert
|
||||
type="warning"
|
||||
key="alert"
|
||||
message={title}
|
||||
css={{
|
||||
textAlign: 'left',
|
||||
'& .ant-alert-action': { alignSelf: 'center' },
|
||||
}}
|
||||
description={children}
|
||||
action={
|
||||
<div css={{ display: 'flex' }}>
|
||||
<Button
|
||||
key="submit"
|
||||
buttonSize="small"
|
||||
buttonStyle="primary"
|
||||
onClick={onConfirm}
|
||||
>
|
||||
{t('Yes, cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
key="cancel"
|
||||
buttonSize="small"
|
||||
buttonStyle="secondary"
|
||||
onClick={onDismiss}
|
||||
>
|
||||
{t('Keep editing')}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ import React, {
|
|||
useEffect,
|
||||
useCallback,
|
||||
} from 'react';
|
||||
import { Alert, FormControl, FormControlProps } from 'react-bootstrap';
|
||||
import { FormControl, FormControlProps } from 'react-bootstrap';
|
||||
import Alert from 'src/components/Alert';
|
||||
import { SupersetClient, t, styled } from '@superset-ui/core';
|
||||
import TableView, { EmptyWrapperType } from 'src/components/TableView';
|
||||
import StyledModal from 'src/common/components/Modal';
|
||||
|
|
@ -246,9 +247,14 @@ const ChangeDatasourceModal: FunctionComponent<ChangeDatasourceModalProps> = ({
|
|||
<>
|
||||
{!confirmChange && (
|
||||
<>
|
||||
<Alert bsStyle="warning">
|
||||
<strong>{t('Warning!')}</strong> {CHANGE_WARNING_MSG}
|
||||
</Alert>
|
||||
<Alert
|
||||
type="warning"
|
||||
message={
|
||||
<>
|
||||
<strong>{t('Warning!')}</strong> {CHANGE_WARNING_MSG}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<div>
|
||||
<FormControl
|
||||
inputRef={ref => {
|
||||
|
|
|
|||
|
|
@ -18,8 +18,9 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Alert, Col, Well } from 'react-bootstrap';
|
||||
import { Col, Well } from 'react-bootstrap';
|
||||
import { Radio } from 'src/common/components/Radio';
|
||||
import Alert from 'src/components/Alert';
|
||||
import Badge from 'src/common/components/Badge';
|
||||
import shortid from 'shortid';
|
||||
import { styled, SupersetClient, t, supersetTheme } from '@superset-ui/core';
|
||||
|
|
@ -830,11 +831,17 @@ class DatasourceEditor extends React.PureComponent {
|
|||
renderErrors() {
|
||||
if (this.state.errors.length > 0) {
|
||||
return (
|
||||
<Alert bsStyle="danger">
|
||||
{this.state.errors.map(err => (
|
||||
<div key={err}>{err}</div>
|
||||
))}
|
||||
</Alert>
|
||||
<Alert
|
||||
css={theme => ({ marginBottom: theme.gridUnit * 4 })}
|
||||
type="error"
|
||||
message={
|
||||
<>
|
||||
{this.state.errors.map(err => (
|
||||
<div key={err}>{err}</div>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
|
@ -970,14 +977,19 @@ class DatasourceEditor extends React.PureComponent {
|
|||
return (
|
||||
<DatasourceContainer>
|
||||
{this.renderErrors()}
|
||||
<div className="m-t-10">
|
||||
<Alert bsStyle="warning">
|
||||
<strong>{t('Be careful.')} </strong>
|
||||
{t(
|
||||
'Changing these settings will affect all charts using this dataset, including charts owned by other people.',
|
||||
)}
|
||||
</Alert>
|
||||
</div>
|
||||
<Alert
|
||||
css={theme => ({ marginBottom: theme.gridUnit * 4 })}
|
||||
type="warning"
|
||||
message={
|
||||
<>
|
||||
{' '}
|
||||
<strong>{t('Be careful.')} </strong>
|
||||
{t(
|
||||
'Changing these settings will affect all charts using this dataset, including charts owned by other people.',
|
||||
)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Tabs
|
||||
fullWidth={false}
|
||||
id="table-tabs"
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React, { FunctionComponent, useState, useRef } from 'react';
|
||||
import { Alert } from 'react-bootstrap';
|
||||
import Alert from 'src/components/Alert';
|
||||
import Button from 'src/components/Button';
|
||||
import { styled, t, SupersetClient } from '@superset-ui/core';
|
||||
|
||||
|
|
@ -138,22 +138,21 @@ const DatasourceModal: FunctionComponent<DatasourceModalProps> = ({
|
|||
setErrors(err);
|
||||
};
|
||||
|
||||
const closeDialog = () => {
|
||||
dialog.current?.destroy();
|
||||
};
|
||||
|
||||
const renderSaveDialog = () => (
|
||||
<div>
|
||||
<Alert bsStyle="warning" className="pointer" onClick={closeDialog}>
|
||||
<div>
|
||||
<i className="fa fa-exclamation-triangle" />{' '}
|
||||
{t(`The dataset configuration exposed here
|
||||
<Alert
|
||||
css={theme => ({
|
||||
marginTop: theme.gridUnit * 4,
|
||||
marginBottom: theme.gridUnit * 4,
|
||||
})}
|
||||
type="warning"
|
||||
showIcon
|
||||
message={t(`The dataset configuration exposed here
|
||||
affects all the charts using this dataset.
|
||||
Be mindful that changing settings
|
||||
here may affect other charts
|
||||
in undesirable ways.`)}
|
||||
</div>
|
||||
</Alert>
|
||||
/>
|
||||
{t('Are you sure you want to save and apply changes?')}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -21,11 +21,11 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { Alert } from 'react-bootstrap';
|
||||
import { t, styled, getChartControlPanelRegistry } from '@superset-ui/core';
|
||||
|
||||
import Tabs from 'src/common/components/Tabs';
|
||||
import { Collapse } from 'src/common/components';
|
||||
import Alert from 'src/components/Alert';
|
||||
import { PluginContext } from 'src/components/DynamicPlugins';
|
||||
import Loading from 'src/components/Loading';
|
||||
import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls';
|
||||
|
|
@ -278,17 +278,12 @@ class ControlPanelsContainer extends React.Component {
|
|||
return (
|
||||
<Styles>
|
||||
{this.props.alert && (
|
||||
<Alert bsStyle="warning">
|
||||
{this.props.alert}
|
||||
<i
|
||||
role="button"
|
||||
aria-label="Remove alert"
|
||||
tabIndex={0}
|
||||
className="fa fa-close pull-right"
|
||||
onClick={this.removeAlert}
|
||||
style={{ cursor: 'pointer' }}
|
||||
/>
|
||||
</Alert>
|
||||
<Alert
|
||||
type="warning"
|
||||
message={this.props.alert}
|
||||
closable
|
||||
onClose={this.removeAlert}
|
||||
/>
|
||||
)}
|
||||
<ControlPanelsTabs
|
||||
id="controlSections"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@
|
|||
*/
|
||||
/* eslint camelcase: 0 */
|
||||
import React from 'react';
|
||||
import { Alert, FormControl, FormGroup } from 'react-bootstrap';
|
||||
import { FormControl, FormGroup } from 'react-bootstrap';
|
||||
import Alert from 'src/components/Alert';
|
||||
import { JsonObject, t, styled } from '@superset-ui/core';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { Radio } from 'src/common/components/Radio';
|
||||
|
|
@ -206,17 +207,22 @@ class SaveModal extends React.Component<SaveModalProps, SaveModalState> {
|
|||
>
|
||||
<div data-test="save-modal-body">
|
||||
{(this.state.alert || this.props.alert) && (
|
||||
<Alert>
|
||||
{this.state.alert ? this.state.alert : this.props.alert}
|
||||
<i
|
||||
role="button"
|
||||
aria-label="Remove alert"
|
||||
tabIndex={0}
|
||||
className="fa fa-close pull-right"
|
||||
onClick={this.removeAlert.bind(this)}
|
||||
style={{ cursor: 'pointer' }}
|
||||
/>
|
||||
</Alert>
|
||||
<Alert
|
||||
type="warning"
|
||||
message={
|
||||
<>
|
||||
{this.state.alert ? this.state.alert : this.props.alert}
|
||||
<i
|
||||
role="button"
|
||||
aria-label="Remove alert"
|
||||
tabIndex={0}
|
||||
className="fa fa-close pull-right"
|
||||
onClick={this.removeAlert.bind(this)}
|
||||
style={{ cursor: 'pointer' }}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<FormGroup data-test="radio-group">
|
||||
<Radio
|
||||
|
|
|
|||
|
|
@ -16,16 +16,15 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { Alert } from 'react-bootstrap';
|
||||
import { styled } from '@superset-ui/core';
|
||||
import cx from 'classnames';
|
||||
import Interweave from 'interweave';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import Icon from 'src/components/Icon';
|
||||
import Icon, { IconName } from 'src/components/Icon';
|
||||
import { ToastType } from 'src/messageToasts/constants';
|
||||
import { ToastMeta } from '../types';
|
||||
|
||||
const ToastContianer = styled.div`
|
||||
const ToastContainer = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
|
@ -35,6 +34,10 @@ const ToastContianer = styled.div`
|
|||
}
|
||||
`;
|
||||
|
||||
const StyledIcon = styled(Icon)`
|
||||
min-width: ${({ theme }) => theme.gridUnit * 5}px;
|
||||
`;
|
||||
|
||||
interface ToastPresenterProps {
|
||||
toast: ToastMeta;
|
||||
onCloseToast: (id: string) => void;
|
||||
|
|
@ -73,26 +76,34 @@ export default function Toast({ toast, onCloseToast }: ToastPresenterProps) {
|
|||
};
|
||||
}, [handleClosePress, toast.duration]);
|
||||
|
||||
let iconName: IconName = 'circle-check-solid';
|
||||
let className = 'toast--success';
|
||||
if (toast.toastType === ToastType.WARNING) {
|
||||
iconName = 'warning-solid';
|
||||
className = 'toast--warning';
|
||||
} else if (toast.toastType === ToastType.DANGER) {
|
||||
iconName = 'error-solid';
|
||||
className = 'toast--danger';
|
||||
} else if (toast.toastType === ToastType.INFO) {
|
||||
iconName = 'info-solid';
|
||||
className = 'toast--info';
|
||||
}
|
||||
|
||||
return (
|
||||
<Alert
|
||||
onDismiss={handleClosePress}
|
||||
bsClass={cx(
|
||||
'alert',
|
||||
'toast',
|
||||
visible && 'toast--visible',
|
||||
toast.toastType === ToastType.SUCCESS && 'toast--success',
|
||||
toast.toastType === ToastType.WARNING && 'toast--warning',
|
||||
toast.toastType === ToastType.DANGER && 'toast--danger',
|
||||
)}
|
||||
<ToastContainer
|
||||
className={cx('alert', 'toast', visible && 'toast--visible', className)}
|
||||
data-test="toast-container"
|
||||
>
|
||||
<ToastContianer>
|
||||
{toast.toastType === ToastType.SUCCESS && (
|
||||
<Icon name="circle-check-solid" />
|
||||
)}
|
||||
{toast.toastType === ToastType.WARNING ||
|
||||
(toast.toastType === ToastType.DANGER && <Icon name="error-solid" />)}
|
||||
<Interweave content={toast.text} />
|
||||
</ToastContianer>
|
||||
</Alert>
|
||||
<StyledIcon name={iconName} />
|
||||
<Interweave content={toast.text} />
|
||||
<i
|
||||
className="fa fa-close pull-right pointer"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={handleClosePress}
|
||||
aria-label="Close"
|
||||
data-test="close-button"
|
||||
/>
|
||||
</ToastContainer>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,9 @@ const StyledToastPresenter = styled.div`
|
|||
max-width: 600px;
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
right: -110px;
|
||||
transform: translate(-50%, 0);
|
||||
right: 0px;
|
||||
margin-right: 50px;
|
||||
margin-bottom: 50px;
|
||||
z-index: ${({ theme }) => theme.zIndex.max};
|
||||
|
||||
.toast {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@
|
|||
@processing-color: #66bcfe;
|
||||
@error-color: #e04355;
|
||||
@highlight-color: #e04355;
|
||||
@warning-color: #fbc700;
|
||||
@normal-color: #d9d9d9;
|
||||
@white: #fff;
|
||||
@black: #000;
|
||||
|
|
|
|||
Loading…
Reference in New Issue