diff --git a/superset/assets/spec/javascripts/components/AsyncSelect_spec.jsx b/superset/assets/spec/javascripts/components/AsyncSelect_spec.jsx
index df62c7fafd87e..e19f1c0f3b1d0 100644
--- a/superset/assets/spec/javascripts/components/AsyncSelect_spec.jsx
+++ b/superset/assets/spec/javascripts/components/AsyncSelect_spec.jsx
@@ -1,14 +1,22 @@
import React from 'react';
import Select from 'react-select';
import { shallow } from 'enzyme';
-import sinon from 'sinon';
+import fetchMock from 'fetch-mock';
import AsyncSelect from '../../../src/components/AsyncSelect';
describe('AsyncSelect', () => {
+ afterAll(fetchMock.reset);
+ afterEach(fetchMock.resetHistory);
+
+ const dataEndpoint = '/chart/api/read';
+ const dataGlob = 'glob:*/chart/api/read';
+ fetchMock.get(dataGlob, []);
+ fetchMock.resetHistory();
+
const mockedProps = {
- dataEndpoint: '/chart/api/read',
- onChange: sinon.spy(),
+ dataEndpoint,
+ onChange: () => {},
placeholder: 'Select...',
mutator: () => [
{ value: 1, label: 'main' },
@@ -16,6 +24,7 @@ describe('AsyncSelect', () => {
],
valueRenderer: opt => opt.label,
};
+
it('is valid element', () => {
expect(
React.isValidElement(),
@@ -30,52 +39,81 @@ describe('AsyncSelect', () => {
});
it('calls onChange on select change', () => {
+ const onChangeSpy = jest.fn();
const wrapper = shallow(
- ,
+ ,
);
+
wrapper.find(Select).simulate('change', { value: 1 });
- expect(mockedProps.onChange).toHaveProperty('callCount', 1);
+ expect(onChangeSpy.mock.calls).toHaveLength(1);
});
describe('auto select', () => {
- let server;
- beforeEach(() => {
- server = sinon.fakeServer.create();
- server.respondWith([
- 200, { 'Content-Type': 'application/json' }, JSON.stringify({}),
- ]);
- });
- afterEach(() => {
- server.restore();
+ it('should not call onChange if autoSelect=false', (done) => {
+ expect.assertions(2);
+
+ const onChangeSpy = jest.fn();
+ shallow(
+ ,
+ );
+
+ setTimeout(() => {
+ expect(fetchMock.calls(dataGlob)).toHaveLength(1);
+ expect(onChangeSpy.mock.calls).toHaveLength(0);
+ done();
+ });
});
- it('should be off by default', () => {
+
+ it('should auto select the first option if autoSelect=true', (done) => {
+ expect.assertions(3);
+
+ const onChangeSpy = jest.fn();
const wrapper = shallow(
- ,
+ ,
);
- wrapper.instance().fetchOptions();
- const spy = sinon.spy(wrapper.instance(), 'onChange');
- expect(spy.callCount).toBe(0);
+
+ setTimeout(() => {
+ expect(fetchMock.calls(dataGlob)).toHaveLength(1);
+ expect(onChangeSpy.mock.calls).toHaveLength(1);
+ expect(onChangeSpy).toBeCalledWith(wrapper.instance().state.options[0]);
+ done();
+ });
});
- it('should auto select first option', () => {
+
+ it('should not auto select when value prop is set and autoSelect=true', (done) => {
+ expect.assertions(3);
+
+ const onChangeSpy = jest.fn();
const wrapper = shallow(
- ,
+ ,
);
- const spy = sinon.spy(wrapper.instance(), 'onChange');
- server.respond();
- expect(spy.callCount).toBe(1);
- expect(spy.calledWith(wrapper.instance().state.options[0])).toBe(true);
+ setTimeout(() => {
+ expect(fetchMock.calls(dataGlob)).toHaveLength(1);
+ expect(onChangeSpy.mock.calls).toHaveLength(0);
+ expect(wrapper.find(Select)).toHaveLength(1);
+ done();
+ });
});
- it('should not auto select when value prop is set', () => {
- const wrapper = shallow(
- ,
+
+ it('should call onAsyncError if there is an error fetching options', (done) => {
+ expect.assertions(3);
+
+ const errorEndpoint = 'async/error/';
+ const errorGlob = 'glob:*async/error/';
+ fetchMock.get(errorGlob, { throws: 'error' });
+
+ const onAsyncError = jest.fn();
+ shallow(
+ ,
);
- const spy = sinon.spy(wrapper.instance(), 'onChange');
- wrapper.instance().fetchOptions();
- server.respond();
- expect(spy.callCount).toBe(0);
- expect(wrapper.find(Select)).toHaveLength(1);
+ setTimeout(() => {
+ expect(fetchMock.calls(errorGlob)).toHaveLength(1);
+ expect(onAsyncError.mock.calls).toHaveLength(1);
+ expect(onAsyncError).toBeCalledWith('error');
+ done();
+ });
});
});
});
diff --git a/superset/assets/spec/javascripts/profile/App_spec.jsx b/superset/assets/spec/javascripts/profile/App_spec.jsx
index 07945120a9334..089af0d482650 100644
--- a/superset/assets/spec/javascripts/profile/App_spec.jsx
+++ b/superset/assets/spec/javascripts/profile/App_spec.jsx
@@ -1,6 +1,6 @@
import React from 'react';
import { Col, Row, Tab } from 'react-bootstrap';
-import { mount } from 'enzyme';
+import { shallow } from 'enzyme';
import { user } from './fixtures';
import App from '../../../src/profile/components/App';
@@ -14,13 +14,15 @@ describe('App', () => {
React.isValidElement(),
).toBe(true);
});
+
it('renders 2 Col', () => {
- const wrapper = mount();
+ const wrapper = shallow();
expect(wrapper.find(Row)).toHaveLength(1);
expect(wrapper.find(Col)).toHaveLength(2);
});
+
it('renders 4 Tabs', () => {
- const wrapper = mount();
+ const wrapper = shallow();
expect(wrapper.find(Tab)).toHaveLength(4);
});
});
diff --git a/superset/assets/spec/javascripts/profile/CreatedContent_spec.jsx b/superset/assets/spec/javascripts/profile/CreatedContent_spec.jsx
index a341c3e47fa6a..4d7609935e122 100644
--- a/superset/assets/spec/javascripts/profile/CreatedContent_spec.jsx
+++ b/superset/assets/spec/javascripts/profile/CreatedContent_spec.jsx
@@ -1,25 +1,28 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { shallow } from 'enzyme';
+import thunk from 'redux-thunk';
+import configureStore from 'redux-mock-store';
+
import { user } from './fixtures';
import CreatedContent from '../../../src/profile/components/CreatedContent';
import TableLoader from '../../../src/components/TableLoader';
+// store needed for withToasts(TableLoader)
+const mockStore = configureStore([thunk]);
+const store = mockStore({});
describe('CreatedContent', () => {
const mockedProps = {
user,
};
- it('is valid', () => {
- expect(
- React.isValidElement(),
- ).toBe(true);
- });
+
it('renders 2 TableLoader', () => {
- const wrapper = mount();
+ const wrapper = shallow(, { context: { store } });
expect(wrapper.find(TableLoader)).toHaveLength(2);
});
+
it('renders 2 titles', () => {
- const wrapper = mount();
+ const wrapper = shallow(, { context: { store } });
expect(wrapper.find('h3')).toHaveLength(2);
});
});
diff --git a/superset/assets/spec/javascripts/profile/Favorites_spec.jsx b/superset/assets/spec/javascripts/profile/Favorites_spec.jsx
index 16efa908e41c1..1022208b2af7b 100644
--- a/superset/assets/spec/javascripts/profile/Favorites_spec.jsx
+++ b/superset/assets/spec/javascripts/profile/Favorites_spec.jsx
@@ -1,25 +1,28 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { shallow } from 'enzyme';
+import thunk from 'redux-thunk';
+import configureStore from 'redux-mock-store';
import { user } from './fixtures';
import Favorites from '../../../src/profile/components/Favorites';
import TableLoader from '../../../src/components/TableLoader';
+// store needed for withToasts(TableLoader)
+const mockStore = configureStore([thunk]);
+const store = mockStore({});
+
describe('Favorites', () => {
const mockedProps = {
user,
};
- it('is valid', () => {
- expect(
- React.isValidElement(),
- ).toBe(true);
- });
+
it('renders 2 TableLoader', () => {
- const wrapper = mount();
+ const wrapper = shallow(, { context: { store } });
expect(wrapper.find(TableLoader)).toHaveLength(2);
});
+
it('renders 2 titles', () => {
- const wrapper = mount();
+ const wrapper = shallow(, { context: { store } });
expect(wrapper.find('h3')).toHaveLength(2);
});
});
diff --git a/superset/assets/spec/javascripts/profile/RecentActivity_spec.jsx b/superset/assets/spec/javascripts/profile/RecentActivity_spec.jsx
index b6deeebdf76c6..20974519d604e 100644
--- a/superset/assets/spec/javascripts/profile/RecentActivity_spec.jsx
+++ b/superset/assets/spec/javascripts/profile/RecentActivity_spec.jsx
@@ -1,11 +1,10 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { shallow } from 'enzyme';
import { user } from './fixtures';
import RecentActivity from '../../../src/profile/components/RecentActivity';
import TableLoader from '../../../src/components/TableLoader';
-
describe('RecentActivity', () => {
const mockedProps = {
user,
@@ -15,8 +14,9 @@ describe('RecentActivity', () => {
React.isValidElement(),
).toBe(true);
});
+
it('renders a TableLoader', () => {
- const wrapper = mount();
+ const wrapper = shallow();
expect(wrapper.find(TableLoader)).toHaveLength(1);
});
});
diff --git a/superset/assets/spec/javascripts/welcome/DashboardTable_spec.jsx b/superset/assets/spec/javascripts/welcome/DashboardTable_spec.jsx
index a2ebf36771030..7192c3ed26add 100644
--- a/superset/assets/spec/javascripts/welcome/DashboardTable_spec.jsx
+++ b/superset/assets/spec/javascripts/welcome/DashboardTable_spec.jsx
@@ -1,30 +1,47 @@
import React from 'react';
import { mount } from 'enzyme';
-import sinon from 'sinon';
+import thunk from 'redux-thunk';
+import configureStore from 'redux-mock-store';
+import fetchMock from 'fetch-mock';
+import { Table } from 'reactable';
import DashboardTable from '../../../src/welcome/DashboardTable';
+import Loading from '../../../src/components/Loading';
-const $ = window.$ = require('jquery');
+// store needed for withToasts(TableLoader)
+const mockStore = configureStore([thunk]);
+const store = mockStore({});
+const dashboardsEndpoint = 'glob:*/dashboardasync/api/read*';
+const mockDashboards = [
+ { id: 1, url: 'url', dashboard_title: 'title' },
+];
+
+fetchMock.get(dashboardsEndpoint, { result: mockDashboards });
+
+function setup() {
+ // use mount because data fetching is triggered on mount
+ return mount(, { context: { store } });
+}
describe('DashboardTable', () => {
- const mockedProps = {};
- let stub;
- beforeEach(() => {
- stub = sinon.stub($, 'getJSON');
- });
- afterEach(() => {
- stub.restore();
- });
+ afterEach(fetchMock.resetHistory);
- it('is valid', () => {
- expect(
- React.isValidElement(),
- ).toBe(true);
+ it('renders a Loading initially', () => {
+ const wrapper = setup();
+ expect(wrapper.find(Loading)).toHaveLength(1);
});
- it('renders', () => {
- const wrapper = mount();
- expect(stub.callCount).toBe(1);
- expect(wrapper.find('img')).toHaveLength(1);
+
+ it('fetches dashboards and renders a Table', (done) => {
+ const wrapper = setup();
+
+ setTimeout(() => {
+ expect(fetchMock.calls(dashboardsEndpoint)).toHaveLength(1);
+ // there's a delay between response and updating state, so manually set it
+ // rather than adding a timeout which could introduce flakiness
+ wrapper.setState({ dashaboards: mockDashboards });
+ expect(wrapper.find(Table)).toHaveLength(1);
+ done();
+ });
});
});
diff --git a/superset/assets/src/common.js b/superset/assets/src/common.js
index fafc84e9a990f..f479d229c1598 100644
--- a/superset/assets/src/common.js
+++ b/superset/assets/src/common.js
@@ -21,10 +21,10 @@ $(document).ready(function () {
$('#language-picker a').click(function (ev) {
ev.preventDefault();
- const targetUrl = ev.currentTarget.href;
- $.ajax(targetUrl).then(() => {
- location.reload();
- });
+ SupersetClient.get({ endpoint: ev.currentTarget.href })
+ .then(() => {
+ location.reload();
+ });
});
});
diff --git a/superset/assets/src/components/AsyncSelect.jsx b/superset/assets/src/components/AsyncSelect.jsx
index e81a123515aba..4c2ae814b66fc 100644
--- a/superset/assets/src/components/AsyncSelect.jsx
+++ b/superset/assets/src/components/AsyncSelect.jsx
@@ -1,10 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
import Select from 'react-select';
+import { SupersetClient } from '@superset-ui/core';
import { t } from '../locales';
-const $ = window.$ = require('jquery');
-
const propTypes = {
dataEndpoint: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
@@ -32,31 +31,38 @@ class AsyncSelect extends React.PureComponent {
isLoading: false,
options: [],
};
+
+ this.onChange = this.onChange.bind(this);
}
+
componentDidMount() {
this.fetchOptions();
}
- onChange(opt) {
- this.props.onChange(opt);
+
+ onChange(option) {
+ this.props.onChange(option);
}
+
fetchOptions() {
this.setState({ isLoading: true });
- const mutator = this.props.mutator;
- $.get(this.props.dataEndpoint)
- .done((data) => {
- this.setState({ options: mutator ? mutator(data) : data, isLoading: false });
+ const { mutator, dataEndpoint } = this.props;
- if (!this.props.value && this.props.autoSelect && this.state.options.length) {
- this.onChange(this.state.options[0]);
+ return SupersetClient.get({ endpoint: dataEndpoint })
+ .then(({ json }) => {
+ const options = mutator ? mutator(json) : json;
+
+ this.setState({ options, isLoading: false });
+
+ if (!this.props.value && this.props.autoSelect && options.length > 0) {
+ this.onChange(options[0]);
}
})
- .fail((xhr) => {
- this.props.onAsyncError(xhr.responseText);
- })
- .always(() => {
+ .catch((error) => {
+ this.props.onAsyncError(error.error || error.statusText || error);
this.setState({ isLoading: false });
});
}
+
render() {
return (
@@ -65,7 +71,7 @@ class AsyncSelect extends React.PureComponent {
options={this.state.options}
value={this.props.value}
isLoading={this.state.isLoading}
- onChange={this.onChange.bind(this)}
+ onChange={this.onChange}
valueRenderer={this.props.valueRenderer}
{...this.props}
/>
diff --git a/superset/assets/src/components/TableLoader.jsx b/superset/assets/src/components/TableLoader.jsx
index 3f51ee92b109e..2f57ab80d19d4 100644
--- a/superset/assets/src/components/TableLoader.jsx
+++ b/superset/assets/src/components/TableLoader.jsx
@@ -1,7 +1,10 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Table, Tr, Td } from 'reactable';
-import $ from 'jquery';
+import { SupersetClient } from '@superset-ui/core';
+
+import withToasts from '../messageToasts/enhancers/withToasts';
+import { t } from '../locales';
import Loading from '../components/Loading';
import '../../stylesheets/reactable-pagination.css';
@@ -9,9 +12,10 @@ const propTypes = {
dataEndpoint: PropTypes.string.isRequired,
mutator: PropTypes.func,
columns: PropTypes.arrayOf(PropTypes.string),
+ addDangerToast: PropTypes.func.isRequired,
};
-export default class TableLoader extends React.PureComponent {
+class TableLoader extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
@@ -19,20 +23,34 @@ export default class TableLoader extends React.PureComponent {
data: [],
};
}
+
componentWillMount() {
- $.get(this.props.dataEndpoint, (data) => {
- let actualData = data;
- if (this.props.mutator) {
- actualData = this.props.mutator(data);
- }
- this.setState({ data: actualData, isLoading: false });
- });
+ const { dataEndpoint, mutator } = this.props;
+
+ SupersetClient.get({ endpoint: dataEndpoint })
+ .then(({ json }) => {
+ const data = mutator ? mutator(json) : json;
+ this.setState({ data, isLoading: false });
+ })
+ .catch(() => {
+ this.setState({ isLoading: false });
+ this.props.addDangerToast(t('An error occurred'));
+ });
}
+
render() {
if (this.state.isLoading) {
return
;
}
- const tableProps = Object.assign({}, this.props);
+
+ const {
+ addDangerToast,
+ addInfoToast,
+ addSuccessToast,
+ addWarningToast,
+ ...tableProps
+ } = this.props;
+
let { columns } = this.props;
if (!columns && this.state.data.length > 0) {
columns = Object.keys(this.state.data[0]).filter(col => col[0] !== '_');
@@ -70,4 +88,7 @@ export default class TableLoader extends React.PureComponent {
);
}
}
+
TableLoader.propTypes = propTypes;
+
+export default withToasts(TableLoader);
diff --git a/superset/assets/src/dashboard/components/HeaderActionsDropdown.jsx b/superset/assets/src/dashboard/components/HeaderActionsDropdown.jsx
index c04e2b42250f5..d012e3499a194 100644
--- a/superset/assets/src/dashboard/components/HeaderActionsDropdown.jsx
+++ b/superset/assets/src/dashboard/components/HeaderActionsDropdown.jsx
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import $ from 'jquery';
+import { SupersetClient } from '@superset-ui/core';
import { DropdownButton, MenuItem } from 'react-bootstrap';
import CssEditor from './CssEditor';
@@ -52,14 +52,20 @@ class HeaderActionsDropdown extends React.PureComponent {
componentWillMount() {
injectCustomCss(this.state.css);
- $.get('/csstemplateasyncmodelview/api/read', data => {
- const cssTemplates = data.result.map(row => ({
- value: row.template_name,
- css: row.css,
- label: row.template_name,
- }));
- this.setState({ cssTemplates });
- });
+ SupersetClient.get({ endpoint: '/csstemplateasyncmodelview/api/read' })
+ .then(({ json }) => {
+ const cssTemplates = json.result.map(row => ({
+ value: row.template_name,
+ css: row.css,
+ label: row.template_name,
+ }));
+ this.setState({ cssTemplates });
+ })
+ .catch(() => {
+ this.props.addDangerToast(
+ t('An error occurred while fetching available CSS templates'),
+ );
+ });
}
changeCss(css) {
diff --git a/superset/assets/src/modules/utils.js b/superset/assets/src/modules/utils.js
index 491fcf5318896..879f7e8f8a68e 100644
--- a/superset/assets/src/modules/utils.js
+++ b/superset/assets/src/modules/utils.js
@@ -1,6 +1,7 @@
/* eslint camelcase: 0 */
import d3 from 'd3';
import $ from 'jquery';
+import { SupersetClient } from '@superset-ui/core';
import { formatDate, UTC } from './dates';
const siFormatter = d3.format('.3s');
@@ -119,13 +120,15 @@ function showApiMessage(resp) {
}
export function toggleCheckbox(apiUrlPrefix, selector) {
- const apiUrl = apiUrlPrefix + $(selector)[0].checked;
- $.get(apiUrl).fail(function (xhr) {
- const resp = xhr.responseJSON;
- if (resp && resp.message) {
- showApiMessage(resp);
- }
- });
+ SupersetClient.get({ endpoint: apiUrlPrefix + $(selector)[0].checked })
+ .then(() => {})
+ .catch((response) => {
+ // @TODO utility function to read this
+ const resp = response.responseJSON;
+ if (resp && resp.message) {
+ showApiMessage(resp);
+ }
+ });
}
/**
diff --git a/superset/assets/src/profile/App.jsx b/superset/assets/src/profile/App.jsx
index 13146ec225306..3ad5bad015897 100644
--- a/superset/assets/src/profile/App.jsx
+++ b/superset/assets/src/profile/App.jsx
@@ -1,6 +1,12 @@
import React from 'react';
import { hot } from 'react-hot-loader';
+import thunk from 'redux-thunk';
+import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
+import { Provider } from 'react-redux';
+
import App from './components/App';
+import messageToastReducer from '../messageToasts/reducers';
+import { initEnhancer } from '../reduxUtils';
import { appSetup } from '../common';
import './main.css';
@@ -10,8 +16,21 @@ appSetup();
const profileViewContainer = document.getElementById('app');
const bootstrap = JSON.parse(profileViewContainer.getAttribute('data-bootstrap'));
+const store = createStore(
+ combineReducers({
+ messageToasts: messageToastReducer,
+ }),
+ {},
+ compose(
+ applyMiddleware(thunk),
+ initEnhancer(false),
+ ),
+);
+
const Application = () => (
-
+
+
+
);
export default hot(module)(Application);
diff --git a/superset/assets/src/profile/components/RecentActivity.jsx b/superset/assets/src/profile/components/RecentActivity.jsx
index 0a36fdaaa227b..3698680e6f245 100644
--- a/superset/assets/src/profile/components/RecentActivity.jsx
+++ b/superset/assets/src/profile/components/RecentActivity.jsx
@@ -33,4 +33,5 @@ export default class RecentActivity extends React.PureComponent {
);
}
}
+
RecentActivity.propTypes = propTypes;
diff --git a/superset/assets/src/visualizations/deckgl/Multi/Multi.jsx b/superset/assets/src/visualizations/deckgl/Multi/Multi.jsx
index 64c1ec61351d9..9bfc84fc4f560 100644
--- a/superset/assets/src/visualizations/deckgl/Multi/Multi.jsx
+++ b/superset/assets/src/visualizations/deckgl/Multi/Multi.jsx
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
-import $ from 'jquery';
+import { SupersetClient } from '@superset-ui/core';
+
import DeckGLContainer from '../DeckGLContainer';
import { getExploreLongUrl } from '../../../explore/exploreUtils';
import layerGenerators from '../layers';
@@ -47,19 +48,22 @@ class DeckMulti extends React.PureComponent {
},
};
- const url = getExploreLongUrl(subsliceCopy.form_data, 'json');
- $.get(url, (data) => {
- const layer = layerGenerators[subsliceCopy.form_data.viz_type](
- subsliceCopy.form_data,
- data,
- );
- this.setState({
- subSlicesLayers: {
- ...this.state.subSlicesLayers,
- [subsliceCopy.slice_id]: layer,
- },
- });
- });
+ SupersetClient.get({
+ endpoint: getExploreLongUrl(subsliceCopy.form_data, 'json'),
+ })
+ .then(({ json }) => {
+ const layer = layerGenerators[subsliceCopy.form_data.viz_type](
+ subsliceCopy.form_data,
+ json,
+ );
+ this.setState({
+ subSlicesLayers: {
+ ...this.state.subSlicesLayers,
+ [subsliceCopy.slice_id]: layer,
+ },
+ });
+ })
+ .catch(() => {});
});
}
@@ -67,7 +71,7 @@ class DeckMulti extends React.PureComponent {
const { payload, viewport, formData, setControlValue } = this.props;
const { subSlicesLayers } = this.state;
- const layers = Object.keys(subSlicesLayers).map(k => subSlicesLayers[k]);
+ const layers = Object.values(subSlicesLayers);
return (
(
-
+
+
+
);
export default hot(module)(App);
diff --git a/superset/assets/src/welcome/DashboardTable.jsx b/superset/assets/src/welcome/DashboardTable.jsx
index 0f4c744151464..29c2988763023 100644
--- a/superset/assets/src/welcome/DashboardTable.jsx
+++ b/superset/assets/src/welcome/DashboardTable.jsx
@@ -1,33 +1,40 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Table, Tr, Td, unsafe } from 'reactable';
+import { SupersetClient } from '@superset-ui/core';
+import withToasts from '../messageToasts/enhancers/withToasts';
+import { t } from '../locales';
+
import Loading from '../components/Loading';
import '../../stylesheets/reactable-pagination.css';
-const $ = window.$ = require('jquery');
-
const propTypes = {
search: PropTypes.string,
+ addDangerToast: PropTypes.func.isRequired,
};
-export default class DashboardTable extends React.PureComponent {
+class DashboardTable extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
- dashboards: false,
+ dashboards: [],
};
}
+
componentDidMount() {
- const url = (
- '/dashboardasync/api/read' +
- '?_oc_DashboardModelViewAsync=changed_on' +
- '&_od_DashboardModelViewAsync=desc');
- $.getJSON(url, (data) => {
- this.setState({ dashboards: data.result });
- });
+ SupersetClient.get({
+ endpoint: '/dashboardasync/api/read?_oc_DashboardModelViewAsync=changed_on&_od_DashboardModelViewAsync=desc',
+ })
+ .then(({ json }) => {
+ this.setState({ dashboards: json.result });
+ })
+ .catch(() => {
+ this.props.addDangerToast(t('An error occurred while fethching Dashboards'));
+ });
}
+
render() {
- if (this.state.dashboards) {
+ if (this.state.dashboards.length > 0) {
return (
);
}
+
return ;
}
}
DashboardTable.propTypes = propTypes;
+
+export default withToasts(DashboardTable);