mirror of
https://github.com/apache/superset.git
synced 2026-04-09 11:25:23 +00:00
[dashboard] New, list view (react) (#8845)
* adds dashboard listview component * use new api * use json over rison * lint * adds seperate dashboard list view * edit and delete actions * fix lint ignore * fix common_bootstrap_payload is now a function * fix license * fix pylint * isort * fix tests * lint * lint ts * fix js tests * fix double import from bad rebase * fix indent error * lookup permissions * generic permission lookup * get tslint to pass * adds js specs * lint * fix rebase * lint * lint again * fix type errors preventing build * adds more specs * fix tslint error * fix null check * remove unecessary code * use translations provided by api * more translations * linting * fix spec * i18n * fix register order
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* 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 { mount } from 'enzyme';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { MenuItem, Pagination } from 'react-bootstrap';
|
||||
|
||||
import ListView from 'src/components/ListView/ListView';
|
||||
|
||||
describe('ListView', () => {
|
||||
const mockedProps = {
|
||||
title: 'Data Table',
|
||||
columns: [
|
||||
{
|
||||
accessor: 'id',
|
||||
Header: 'ID',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
accessor: 'name',
|
||||
Header: 'Name',
|
||||
filterable: true,
|
||||
},
|
||||
],
|
||||
data: [
|
||||
{ id: 1, name: 'data 1' },
|
||||
{ id: 2, name: 'data 2' },
|
||||
],
|
||||
count: 2,
|
||||
pageSize: 1,
|
||||
fetchData: jest.fn(() => []),
|
||||
loading: false,
|
||||
filterTypes: {
|
||||
id: [],
|
||||
name: [{ name: 'sw', label: 'Starts With' }],
|
||||
},
|
||||
};
|
||||
const wrapper = mount(<ListView {...mockedProps} />);
|
||||
|
||||
afterEach(() => {
|
||||
mockedProps.fetchData.mockClear();
|
||||
});
|
||||
|
||||
it('calls fetchData on mount', () => {
|
||||
expect(wrapper.find(ListView)).toHaveLength(1);
|
||||
expect(mockedProps.fetchData.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"filters": Object {},
|
||||
"pageIndex": 0,
|
||||
"pageSize": 1,
|
||||
"sortBy": Array [],
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('calls fetchData on sort', () => {
|
||||
wrapper
|
||||
.find('[data-test="sort-header"]')
|
||||
.first()
|
||||
.simulate('click');
|
||||
expect(mockedProps.fetchData.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"filters": Object {},
|
||||
"pageIndex": 0,
|
||||
"pageSize": 1,
|
||||
"sortBy": Array [
|
||||
Object {
|
||||
"desc": false,
|
||||
"id": "id",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('calls fetchData on filter', () => {
|
||||
act(() => {
|
||||
wrapper
|
||||
.find('.dropdown-toggle')
|
||||
.children('button')
|
||||
.props()
|
||||
.onClick();
|
||||
|
||||
wrapper
|
||||
.find(MenuItem)
|
||||
.props()
|
||||
.onSelect({ id: 'name', Header: 'name' });
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
act(() => {
|
||||
wrapper.find('.filter-inputs input[type="text"]').prop('onChange')({
|
||||
currentTarget: { value: 'foo' },
|
||||
});
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
act(() => {
|
||||
wrapper
|
||||
.find('[data-test="apply-filters"]')
|
||||
.last()
|
||||
.prop('onClick')();
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
expect(mockedProps.fetchData.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"filters": Object {
|
||||
"name": Object {
|
||||
"filterId": "sw",
|
||||
"filterValue": "foo",
|
||||
},
|
||||
},
|
||||
"pageIndex": 0,
|
||||
"pageSize": 1,
|
||||
"sortBy": Array [
|
||||
Object {
|
||||
"desc": false,
|
||||
"id": "id",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('calls fetchData on page change', () => {
|
||||
act(() => {
|
||||
wrapper.find(Pagination).prop('onSelect')(2);
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
expect(mockedProps.fetchData.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"filters": Object {
|
||||
"name": Object {
|
||||
"filterId": "sw",
|
||||
"filterValue": "foo",
|
||||
},
|
||||
},
|
||||
"pageIndex": 1,
|
||||
"pageSize": 1,
|
||||
"sortBy": Array [
|
||||
Object {
|
||||
"desc": false,
|
||||
"id": "id",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
@@ -68,7 +68,7 @@ describe('ExploreChartHeader', () => {
|
||||
it('should updateChartTitleOrSaveSlice for existed slice', () => {
|
||||
const newTitle = 'New Chart Title';
|
||||
wrapper.instance().updateChartTitleOrSaveSlice(newTitle);
|
||||
expect(stub.call.length).toEqual(1);
|
||||
expect(stub.call).toHaveLength(1);
|
||||
expect(stub).toHaveBeenCalledWith(mockProps.slice.form_data, {
|
||||
action: 'overwrite',
|
||||
slice_name: newTitle,
|
||||
@@ -79,7 +79,7 @@ describe('ExploreChartHeader', () => {
|
||||
const newTitle = 'New Chart Title';
|
||||
wrapper.setProps({ slice: undefined });
|
||||
wrapper.instance().updateChartTitleOrSaveSlice(newTitle);
|
||||
expect(stub.call.length).toEqual(1);
|
||||
expect(stub.call).toHaveLength(1);
|
||||
expect(stub).toHaveBeenCalledWith(mockProps.form_data, {
|
||||
action: 'saveas',
|
||||
slice_name: newTitle,
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 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 { mount } from 'enzyme';
|
||||
import thunk from 'redux-thunk';
|
||||
import configureStore from 'redux-mock-store';
|
||||
import fetchMock from 'fetch-mock';
|
||||
|
||||
import DashboardList from 'src/views/dashboardList/DashboardList';
|
||||
import ListView from 'src/components/ListView/ListView';
|
||||
|
||||
// store needed for withToasts(DashboardTable)
|
||||
const mockStore = configureStore([thunk]);
|
||||
const store = mockStore({});
|
||||
|
||||
const dashboardsInfoEndpoint = 'glob:*/api/v1/dashboard/_info*';
|
||||
const dashboardsEndpoint = 'glob:*/api/v1/dashboard/?*';
|
||||
|
||||
const mockDashboards = [...new Array(3)].map((_, i) => ({
|
||||
id: i,
|
||||
url: 'url',
|
||||
dashboard_title: `title ${i}`,
|
||||
changed_by_name: 'user',
|
||||
changed_by_url: 'changed_by_url',
|
||||
changed_by_fk: 1,
|
||||
published: true,
|
||||
changed_on: new Date().toISOString(),
|
||||
}));
|
||||
|
||||
fetchMock.get(dashboardsInfoEndpoint, {
|
||||
permissions: ['can_list', 'can_edit'],
|
||||
filters: [],
|
||||
});
|
||||
fetchMock.get(dashboardsEndpoint, {
|
||||
result: mockDashboards,
|
||||
dashboard_count: 3,
|
||||
});
|
||||
|
||||
describe('DashboardList', () => {
|
||||
const mockedProps = {};
|
||||
const wrapper = mount(<DashboardList {...mockedProps} />, {
|
||||
context: { store },
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
expect(wrapper.find(DashboardList)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('renders a ListView', () => {
|
||||
expect(wrapper.find(ListView)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('fetches info', () => {
|
||||
const callsI = fetchMock.calls(/dashboard\/_info/);
|
||||
expect(callsI).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('fetches data', () => {
|
||||
wrapper.update();
|
||||
const callsD = fetchMock.calls(/dashboard\/\?q/);
|
||||
expect(callsD).toHaveLength(1);
|
||||
expect(callsD[0][0]).toMatchInlineSnapshot(
|
||||
`"/http//localhost/api/v1/dashboard/?q={%22order_column%22:%22changed_on%22,%22order_direction%22:%22desc%22,%22page%22:0,%22page_size%22:25}"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -21,16 +21,16 @@ import { mount } from 'enzyme';
|
||||
import thunk from 'redux-thunk';
|
||||
import configureStore from 'redux-mock-store';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import { Table } from 'reactable-arc';
|
||||
|
||||
import ListView from 'src/components/ListView/ListView';
|
||||
import DashboardTable from '../../../src/welcome/DashboardTable';
|
||||
import Loading from '../../../src/components/Loading';
|
||||
|
||||
// store needed for withToasts(TableLoader)
|
||||
// store needed for withToasts(DashboardTable)
|
||||
const mockStore = configureStore([thunk]);
|
||||
const store = mockStore({});
|
||||
|
||||
const dashboardsEndpoint = 'glob:*/dashboardasync/api/read*';
|
||||
const dashboardsEndpoint = 'glob:*/api/v1/dashboard/*';
|
||||
const mockDashboards = [{ id: 1, url: 'url', dashboard_title: 'title' }];
|
||||
|
||||
fetchMock.get(dashboardsEndpoint, { result: mockDashboards });
|
||||
@@ -48,7 +48,7 @@ describe('DashboardTable', () => {
|
||||
expect(wrapper.find(Loading)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('fetches dashboards and renders a Table', done => {
|
||||
it('fetches dashboards and renders a ListView', done => {
|
||||
const wrapper = setup();
|
||||
|
||||
setTimeout(() => {
|
||||
@@ -56,7 +56,7 @@ describe('DashboardTable', () => {
|
||||
// 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);
|
||||
expect(wrapper.find(ListView)).toHaveLength(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user