Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
9875a0e
step 1: broken stuff!
rusackas Sep 10, 2020
aa4c523
first steps
pkdotson Sep 18, 2020
b79eced
more adding and slicing
pkdotson Sep 23, 2020
d75fd9c
step 1: broken stuff!
rusackas Sep 10, 2020
c0ec303
can now filter dashboards/charts for "Edited" tabs (filter by changed…
rusackas Sep 22, 2020
57148fc
more updates
pkdotson Oct 2, 2020
bcef846
update recent card
pkdotson Oct 2, 2020
23bb1a4
add icon
pkdotson Oct 2, 2020
0d153a1
Adding Expand Icon to Collapse component
rusackas Oct 2, 2020
90cbb62
more updates
pkdotson Oct 6, 2020
b5863d3
clean up code
pkdotson Oct 7, 2020
d99b0af
remove lock file
pkdotson Oct 8, 2020
fac22d9
remove consoles
pkdotson Oct 8, 2020
bd44cae
fixing subnav button height shift
rusackas Oct 9, 2020
beea872
lil' ascii arrows
rusackas Oct 10, 2020
ed9f380
update branch
pkdotson Oct 19, 2020
4f85f3d
update test part 1
pkdotson Oct 19, 2020
f02968d
remove consoles
pkdotson Oct 19, 2020
c1e0188
fix typescript
pkdotson Oct 20, 2020
b2099dc
add images and update emptystate
pkdotson Oct 21, 2020
ca65ce7
add changes
pkdotson Oct 21, 2020
51b8ed2
update chart card
pkdotson Oct 21, 2020
33bc305
fix css issues from rebase
pkdotson Oct 21, 2020
73430bd
add suggestions
pkdotson Oct 23, 2020
d21ff1b
more changes
pkdotson Oct 23, 2020
f9c0e17
update tests and clear typescript errors
pkdotson Oct 24, 2020
f25b325
Update superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx
pkdotson Oct 23, 2020
6affb81
update from comments
pkdotson Oct 26, 2020
f4e9e0c
more updates..
pkdotson Oct 26, 2020
f54d8d8
fix rebase
pkdotson Oct 26, 2020
ee02a5a
fix pesky type errors
pkdotson Oct 26, 2020
48a061d
test fixes
pkdotson Oct 26, 2020
5399ccc
lint fix
pkdotson Oct 27, 2020
5a335e3
Update superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_…
pkdotson Oct 27, 2020
3199924
Update superset-frontend/src/views/CRUD/welcome/EmptyState.tsx
pkdotson Oct 27, 2020
0b72ef8
Update superset-frontend/src/components/Menu/SubMenu.tsx
pkdotson Oct 27, 2020
7af5abb
Update superset-frontend/src/components/ListViewCard/index.tsx
pkdotson Oct 27, 2020
68d4081
Update superset-frontend/src/components/ListViewCard/index.tsx
pkdotson Oct 27, 2020
8af7bd2
add suggestions
pkdotson Oct 28, 2020
596471a
fix lint
pkdotson Oct 28, 2020
2f7744b
remove unused code
pkdotson Oct 28, 2020
ace8055
toast getrecentActivityobjs
pkdotson Oct 28, 2020
e3d94c4
add some suggestions
pkdotson Oct 29, 2020
ff22e81
remove types for now
pkdotson Oct 29, 2020
09ff645
cypress fix
pkdotson Oct 29, 2020
3af819d
remove unused type
pkdotson Oct 30, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added superset-frontend/images/empty-charts.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added superset-frontend/images/empty-dashboard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added superset-frontend/images/empty-queries.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added superset-frontend/images/star-circle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added superset-frontend/images/union.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import SubMenu from 'src/components/Menu/SubMenu';

const defaultProps = {
name: 'Title',
children: [
tabs: [
{
name: 'Page1',
label: 'Page1',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const mockCharts = [...new Array(3)].map((_, i) => ({
fetchMock.get(chartsInfoEndpoint, {
permissions: ['can_list', 'can_edit', 'can_delete'],
});

fetchMock.get(chartssOwnersEndpoint, {
result: [],
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* 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 { styledMount as mount } from 'spec/helpers/theming';
import thunk from 'redux-thunk';
import fetchMock from 'fetch-mock';

import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
import configureStore from 'redux-mock-store';
import ActivityTable from 'src/views/CRUD/welcome/ActivityTable';

const mockStore = configureStore([thunk]);
const store = mockStore({});

const chartsEndpoint = 'glob:*/api/v1/chart/?*';
const dashboardEndpoint = 'glob:*/api/v1/dashboard/?*';
const savedQueryEndpoint = 'glob:*/api/v1/saved_query/?*';

fetchMock.get(chartsEndpoint, {
result: [
{
slice_name: 'ChartyChart',
changed_on_utc: '24 Feb 2014 10:13:14',
url: '/fakeUrl/explore',
id: '4',
table: {},
},
],
});

fetchMock.get(dashboardEndpoint, {
result: [
{
dashboard_title: 'Dashboard_Test',
changed_on_utc: '24 Feb 2014 10:13:14',
url: '/fakeUrl/dashboard',
id: '3',
},
],
});

fetchMock.get(savedQueryEndpoint, {
result: [],
});

describe('ActivityTable', () => {
const activityProps = {
user: {
userId: '1',
},
activityFilter: 'Edited',
};
const wrapper = mount(<ActivityTable {...activityProps} />, {
context: { store },
});

beforeAll(async () => {
await waitForComponentToPaint(wrapper);
});

it('the component renders ', () => {
expect(wrapper.find(ActivityTable)).toExist();
});

it('calls batch method and renders ListViewCArd', async () => {
const chartCall = fetchMock.calls(/chart\/\?q/);
const dashboardCall = fetchMock.calls(/dashboard\/\?q/);
expect(chartCall).toHaveLength(2);
expect(dashboardCall).toHaveLength(2);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The home page fires 12 fetch requests on initial page load:

This would be quite unscalable for large Superset deployments.

Can we combine some of these or make them async? For example, the non-default tabs shouldn't render when they are not selected.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@junlincc Does it make sense to render less tabs in on first page load to cut down on these call. Our current implementation does batch call for recent activity, and signle calls for charts, dashboards and saved queries favorites.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ktmud I think there are a few ways we can approach the performance issues... potentially each of the four sections run queries and render separately, and there may be some queries we can combine if they use the same endpoint but different parameters Is the current state a dealbreaker for you, or would you be comfortable enough we merge this as-is then follow up with performance improvements in a new PR or two?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes please address performance issues separately after we get through most of the roadmap items....

});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* 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 { styledMount as mount } from 'spec/helpers/theming';
import thunk from 'redux-thunk';
import fetchMock from 'fetch-mock';
import configureStore from 'redux-mock-store';

import ChartTable from 'src/views/CRUD/welcome/ChartTable';
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';

const mockStore = configureStore([thunk]);
const store = mockStore({});

const chartsEndpoint = 'glob:*/api/v1/chart/?*';
const chartsInfoEndpoint = 'glob:*/api/v1/chart/_info*';

const mockCharts = [...new Array(3)].map((_, i) => ({
changed_on_utc: new Date().toISOString(),
created_by: 'super user',
id: i,
slice_name: `cool chart ${i}`,
url: 'url',
viz_type: 'bar',
datasource_title: `ds${i}`,
thumbnail_url: '',
}));

fetchMock.get(chartsEndpoint, {
result: mockCharts,
});

fetchMock.get(chartsInfoEndpoint, {
permissions: ['can_add', 'can_edit', 'can_delete'],
});

describe('ChartTable', () => {
const mockedProps = {
user: {
userId: '2',
},
};
const wrapper = mount(<ChartTable {...mockedProps} />, {
context: { store },
});
it('it renders', () => {
expect(wrapper.find(ChartTable)).toExist();
});

it('fetches chart favorites and renders chart cards ', async () => {
expect(fetchMock.calls(chartsEndpoint)).toHaveLength(1);
await waitForComponentToPaint(wrapper);
expect(wrapper.find('ChartCard')).toExist();
});

it('display EmptyState if there is no data', () => {
fetchMock.resetHistory();
const wrapper = mount(<ChartTable {...mockedProps} />, {
context: { store },
});
expect(wrapper.find('EmptyState')).toExist();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,48 +17,78 @@
* under the License.
*/
import React from 'react';
import { mount } from 'enzyme';
import { styledMount as mount } from 'spec/helpers/theming';
import thunk from 'redux-thunk';
import configureStore from 'redux-mock-store';
import fetchMock from 'fetch-mock';
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
import { act } from 'react-dom/test-utils';

import ListView from 'src/components/ListView';
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
import SubMenu from 'src/components/Menu/SubMenu';
import DashboardTable from 'src/views/CRUD/welcome/DashboardTable';
import DashboardCard from 'src/views/CRUD/dashboard/DashboardCard';

// store needed for withToasts(DashboardTable)
const mockStore = configureStore([thunk]);
const store = mockStore({});

const dashboardsEndpoint = 'glob:*/api/v1/dashboard/*';
const mockDashboards = [{ id: 1, url: 'url', dashboard_title: 'title' }];
const dashboardsEndpoint = 'glob:*/api/v1/dashboard/?*';
const dashboardInfoEndpoint = 'glob:*/api/v1/dashboard/_info*';
const mockDashboards = [
{
id: 1,
url: 'url',
dashboard_title: 'title',
changed_on_utc: '24 Feb 2014 10:13:14',
},
];

fetchMock.get(dashboardsEndpoint, { result: mockDashboards });
fetchMock.get(dashboardInfoEndpoint, {
permissions: ['can_list', 'can_edit', 'can_delete'],
});

function setup() {
// use mount because data fetching is triggered on mount
return mount(<DashboardTable />, {
describe('DashboardTable', () => {
const dashboardProps = {
dashboardFilter: 'Favorite',
user: {
userId: '2',
},
};
const wrapper = mount(<DashboardTable {...dashboardProps} />, {
context: { store },
wrappingComponent: ThemeProvider,
wrappingComponentProps: { theme: supersetTheme },
});
}

describe('DashboardTable', () => {
beforeEach(fetchMock.resetHistory);
beforeAll(async () => {
await waitForComponentToPaint(wrapper);
});

it('renders', () => {
expect(wrapper.find(DashboardTable)).toExist();
});

it('fetches dashboards and renders a ListView', () => {
return new Promise(done => {
const wrapper = setup();
it('render a submenu with clickable tabs and buttons', async () => {
expect(wrapper.find(SubMenu)).toExist();
expect(wrapper.find('MenuItem')).toHaveLength(2);
expect(wrapper.find('Button')).toHaveLength(4);
act(() => {
wrapper.find('MenuItem').at(1).simulate('click');
});
await waitForComponentToPaint(wrapper);
expect(fetchMock.calls(/dashboard\/\?q/)).toHaveLength(1);
});

it('fetches dashboards and renders a card', () => {
expect(fetchMock.calls(/dashboard\/\?q/)).toHaveLength(1);
wrapper.setState({ dashboards: mockDashboards });
expect(wrapper.find(DashboardCard)).toExist();
});

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({ dashboards: mockDashboards });
expect(wrapper.find(ListView)).toExist();
done();
});
it('display EmptyState if there is no data', () => {
fetchMock.resetHistory();
const wrapper = mount(<DashboardTable {...dashboardProps} />, {
context: { store },
});
expect(wrapper.find('EmptyState')).toExist();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* 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 { styledMount as mount } from 'spec/helpers/theming';
import EmptyState from 'src/views/CRUD/welcome/EmptyState';

describe('EmptyState', () => {
const variants = [
{
tab: 'Favorite',
tableName: 'DASHBOARDS',
},
{
tab: 'Mine',
tableName: 'DASHBOARDS',
},
{
tab: 'Favorite',
tableName: 'CHARTS',
},
{
tab: 'Mine',
tableName: 'CHARTS',
},
{
tab: 'Favorite',
tableName: 'SAVED_QUERIES',
},
{
tab: 'Mine',
tableName: 'SAVED_QUEREIS',
},
];
const recents = [
{
tab: 'Viewed',
tableName: 'RECENTS',
},
{
tab: 'Edited',
tableName: 'RECENTS',
},
{
tab: 'Created',
tableName: 'RECENTS',
},
];
variants.forEach(variant => {
it(`it renders an ${variant.tab} ${variant.tableName} empty state`, () => {
const wrapper = mount(<EmptyState {...variant} />);
expect(wrapper).toExist();
const textContainer = wrapper.find('.ant-empty-description');
expect(textContainer.text()).toEqual(
variant.tab === 'Favorite'
? "You don't have any favorites yet!"
: `No ${
variant.tableName === 'SAVED_QUERIES'
? 'saved queries'
: variant.tableName.toLowerCase()
} yet`,
);
expect(wrapper.find('button')).toHaveLength(1);
});
});
recents.forEach(recent => {
it(`it renders an ${recent.tab} ${recent.tableName} empty state`, () => {
const wrapper = mount(<EmptyState {...recent} />);
expect(wrapper).toExist();
const textContainer = wrapper.find('.ant-empty-description');
expect(wrapper.find('.ant-empty-image').children()).toHaveLength(1);
expect(textContainer.text()).toContain(
`Recently ${recent.tab.toLowerCase()} charts, dashboards, and saved queries will appear here`,
);
});
});
});
Loading