feat(homepage): conditionally render viewed tab and move examples to chart and dashboard table (#15792)

* test examples

* fix lint

* more lint

* last test

* add suggestions

* fix lint

* fix wrong localstor

* fix tests

* add suggestions

* Update superset-frontend/src/views/CRUD/types.ts

Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com>

* Update superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx

Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com>

* fix lint

* fix default tab

* fix test

Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com>
This commit is contained in:
Phillip Kelley-Dotson 2021-07-22 10:41:49 -07:00 committed by GitHub
parent 5e1c469f42
commit 04c0680f6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 130 additions and 76 deletions

View File

@ -26,6 +26,7 @@ export type FavoriteStatus = {
export enum TableTabTypes { export enum TableTabTypes {
FAVORITE = 'Favorite', FAVORITE = 'Favorite',
MINE = 'Mine', MINE = 'Mine',
EXAMPLES = 'Examples',
} }
export type Filters = { export type Filters = {
@ -42,6 +43,7 @@ export interface DashboardTableProps {
mine: Array<Dashboard>; mine: Array<Dashboard>;
showThumbnails?: boolean; showThumbnails?: boolean;
featureFlag?: boolean; featureFlag?: boolean;
examples: Array<Dashboard>;
} }
export interface Dashboard { export interface Dashboard {

View File

@ -219,17 +219,7 @@ export default function ActivityTable({
setInLocalStorage(HOMEPAGE_ACTIVITY_FILTER, SetTabType.VIEWED); setInLocalStorage(HOMEPAGE_ACTIVITY_FILTER, SetTabType.VIEWED);
}, },
}); });
} else {
tabs.unshift({
name: 'Examples',
label: t('Examples'),
onClick: () => {
setActiveChild('Examples');
setInLocalStorage(HOMEPAGE_ACTIVITY_FILTER, SetTabType.EXAMPLE);
},
});
} }
const renderActivity = () => const renderActivity = () =>
(activeChild !== 'Edited' ? activityData[activeChild] : editedObjs).map( (activeChild !== 'Edited' ? activityData[activeChild] : editedObjs).map(
(entity: ActivityObject) => { (entity: ActivityObject) => {

View File

@ -85,7 +85,7 @@ describe('ChartTable', () => {
} }
}); });
await waitForComponentToPaint(wrapper); await waitForComponentToPaint(wrapper);
expect(fetchMock.calls(chartsEndpoint)).toHaveLength(3); expect(fetchMock.calls(chartsEndpoint)).toHaveLength(1);
expect(wrapper.find('ChartCard')).toExist(); expect(wrapper.find('ChartCard')).toExist();
}); });

View File

@ -18,6 +18,7 @@
*/ */
import React, { useState, useMemo, useEffect } from 'react'; import React, { useState, useMemo, useEffect } from 'react';
import { t } from '@superset-ui/core'; import { t } from '@superset-ui/core';
import { filter } from 'lodash';
import { import {
useListViewResource, useListViewResource,
useChartEditModal, useChartEditModal,
@ -52,6 +53,7 @@ interface ChartTableProps {
user?: User; user?: User;
mine: Array<any>; mine: Array<any>;
showThumbnails: boolean; showThumbnails: boolean;
examples?: Array<object>;
} }
function ChartTable({ function ChartTable({
@ -60,10 +62,17 @@ function ChartTable({
addSuccessToast, addSuccessToast,
mine, mine,
showThumbnails, showThumbnails,
examples,
}: ChartTableProps) { }: ChartTableProps) {
const history = useHistory(); const history = useHistory();
const filterStore = getFromLocalStorage(HOMEPAGE_CHART_FILTER, null); const filterStore = getFromLocalStorage(HOMEPAGE_CHART_FILTER, null);
const initialFilter = filterStore || TableTabTypes.MINE; let initialFilter = filterStore || TableTabTypes.EXAMPLES;
if (!examples && filterStore === TableTabTypes.EXAMPLES) {
initialFilter = TableTabTypes.MINE;
}
const filteredExamples = filter(examples, obj => 'viz_type' in obj);
const { const {
state: { loading, resourceCollection: charts, bulkSelectEnabled }, state: { loading, resourceCollection: charts, bulkSelectEnabled },
@ -76,7 +85,7 @@ function ChartTable({
t('chart'), t('chart'),
addDangerToast, addDangerToast,
true, true,
initialFilter === 'Favorite' ? [] : mine, initialFilter === 'Mine' ? mine : filteredExamples,
[], [],
false, false,
); );
@ -96,9 +105,13 @@ function ChartTable({
const [chartFilter, setChartFilter] = useState(initialFilter); const [chartFilter, setChartFilter] = useState(initialFilter);
const [preparingExport, setPreparingExport] = useState<boolean>(false); const [preparingExport, setPreparingExport] = useState<boolean>(false);
const [loaded, setLoaded] = useState<boolean>(false);
useEffect(() => { useEffect(() => {
getData(chartFilter); if (loaded || chartFilter === 'Favorite') {
getData(chartFilter);
}
setLoaded(true);
}, [chartFilter]); }, [chartFilter]);
const handleBulkChartExport = (chartsToExport: Chart[]) => { const handleBulkChartExport = (chartsToExport: Chart[]) => {
@ -118,7 +131,7 @@ function ChartTable({
operator: 'rel_o_m', operator: 'rel_o_m',
value: `${user?.userId}`, value: `${user?.userId}`,
}); });
} else { } else if (filterName === 'Favorite') {
filters.push({ filters.push({
id: 'id', id: 'id',
operator: 'chart_is_favorite', operator: 'chart_is_favorite',
@ -128,6 +141,36 @@ function ChartTable({
return filters; return filters;
}; };
const menuTabs = [
{
name: 'Favorite',
label: t('Favorite'),
onClick: () => {
setChartFilter(TableTabTypes.FAVORITE);
setInLocalStorage(HOMEPAGE_CHART_FILTER, TableTabTypes.FAVORITE);
},
},
{
name: 'Mine',
label: t('Mine'),
onClick: () => {
setChartFilter(TableTabTypes.MINE);
setInLocalStorage(HOMEPAGE_CHART_FILTER, TableTabTypes.MINE);
},
},
];
if (examples) {
menuTabs.push({
name: 'Examples',
label: t('Examples'),
onClick: () => {
setChartFilter(TableTabTypes.EXAMPLES);
setInLocalStorage(HOMEPAGE_CHART_FILTER, TableTabTypes.EXAMPLES);
},
});
}
const getData = (filter: string) => const getData = (filter: string) =>
fetchData({ fetchData({
pageIndex: 0, pageIndex: 0,
@ -156,24 +199,7 @@ function ChartTable({
<SubMenu <SubMenu
activeChild={chartFilter} activeChild={chartFilter}
// eslint-disable-next-line react/no-children-prop // eslint-disable-next-line react/no-children-prop
tabs={[ tabs={menuTabs}
{
name: 'Favorite',
label: t('Favorite'),
onClick: () => {
setChartFilter('Favorite');
setInLocalStorage(HOMEPAGE_CHART_FILTER, TableTabTypes.FAVORITE);
},
},
{
name: 'Mine',
label: t('Mine'),
onClick: () => {
setChartFilter('Mine');
setInLocalStorage(HOMEPAGE_CHART_FILTER, TableTabTypes.MINE);
},
},
]}
buttons={[ buttons={[
{ {
name: ( name: (

View File

@ -72,9 +72,9 @@ describe('DashboardTable', () => {
it('render a submenu with clickable tabs and buttons', async () => { it('render a submenu with clickable tabs and buttons', async () => {
expect(wrapper.find('SubMenu')).toExist(); expect(wrapper.find('SubMenu')).toExist();
expect(wrapper.find('li.no-router')).toHaveLength(2); expect(wrapper.find('li.no-router')).toHaveLength(2);
expect(wrapper.find('Button')).toHaveLength(4); expect(wrapper.find('Button')).toHaveLength(6);
act(() => { act(() => {
const handler = wrapper.find('li.no-router a').at(1).prop('onClick'); const handler = wrapper.find('li.no-router a').at(0).prop('onClick');
if (handler) { if (handler) {
handler({} as any); handler({} as any);
} }

View File

@ -18,6 +18,7 @@
*/ */
import React, { useState, useMemo, useEffect } from 'react'; import React, { useState, useMemo, useEffect } from 'react';
import { SupersetClient, t } from '@superset-ui/core'; import { SupersetClient, t } from '@superset-ui/core';
import { filter } from 'lodash';
import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks'; import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks';
import { import {
Dashboard, Dashboard,
@ -54,10 +55,17 @@ function DashboardTable({
addSuccessToast, addSuccessToast,
mine, mine,
showThumbnails, showThumbnails,
examples,
}: DashboardTableProps) { }: DashboardTableProps) {
const history = useHistory(); const history = useHistory();
const filterStore = getFromLocalStorage(HOMEPAGE_DASHBOARD_FILTER, null); const filterStore = getFromLocalStorage(HOMEPAGE_DASHBOARD_FILTER, null);
const defaultFilter = filterStore || TableTabTypes.MINE; let defaultFilter = filterStore || TableTabTypes.EXAMPLES;
if (!examples && filterStore === TableTabTypes.EXAMPLES) {
defaultFilter = TableTabTypes.MINE;
}
const filteredExamples = filter(examples, obj => !('viz_type' in obj));
const { const {
state: { loading, resourceCollection: dashboards }, state: { loading, resourceCollection: dashboards },
@ -70,7 +78,7 @@ function DashboardTable({
t('dashboard'), t('dashboard'),
addDangerToast, addDangerToast,
true, true,
defaultFilter === 'Favorite' ? [] : mine, defaultFilter === 'Mine' ? mine : filteredExamples,
[], [],
false, false,
); );
@ -84,9 +92,13 @@ function DashboardTable({
const [editModal, setEditModal] = useState<Dashboard>(); const [editModal, setEditModal] = useState<Dashboard>();
const [dashboardFilter, setDashboardFilter] = useState(defaultFilter); const [dashboardFilter, setDashboardFilter] = useState(defaultFilter);
const [preparingExport, setPreparingExport] = useState<boolean>(false); const [preparingExport, setPreparingExport] = useState<boolean>(false);
const [loaded, setLoaded] = useState<boolean>(false);
useEffect(() => { useEffect(() => {
getData(dashboardFilter); if (loaded || dashboardFilter === 'Favorite') {
getData(dashboardFilter);
}
setLoaded(true);
}, [dashboardFilter]); }, [dashboardFilter]);
const handleBulkDashboardExport = (dashboardsToExport: Dashboard[]) => { const handleBulkDashboardExport = (dashboardsToExport: Dashboard[]) => {
@ -126,7 +138,7 @@ function DashboardTable({
operator: 'rel_m_m', operator: 'rel_m_m',
value: `${user?.userId}`, value: `${user?.userId}`,
}); });
} else { } else if (filterName === 'Favorite') {
filters.push({ filters.push({
id: 'id', id: 'id',
operator: 'dashboard_is_favorite', operator: 'dashboard_is_favorite',
@ -136,6 +148,36 @@ function DashboardTable({
return filters; return filters;
}; };
const menuTabs = [
{
name: 'Favorite',
label: t('Favorite'),
onClick: () => {
setDashboardFilter(TableTabTypes.FAVORITE);
setInLocalStorage(HOMEPAGE_DASHBOARD_FILTER, TableTabTypes.FAVORITE);
},
},
{
name: 'Mine',
label: t('Mine'),
onClick: () => {
setDashboardFilter(TableTabTypes.MINE);
setInLocalStorage(HOMEPAGE_DASHBOARD_FILTER, TableTabTypes.MINE);
},
},
];
if (examples) {
menuTabs.push({
name: 'Examples',
label: t('Examples'),
onClick: () => {
setDashboardFilter(TableTabTypes.EXAMPLES);
setInLocalStorage(HOMEPAGE_DASHBOARD_FILTER, TableTabTypes.EXAMPLES);
},
});
}
const getData = (filter: string) => const getData = (filter: string) =>
fetchData({ fetchData({
pageIndex: 0, pageIndex: 0,
@ -154,27 +196,7 @@ function DashboardTable({
<> <>
<SubMenu <SubMenu
activeChild={dashboardFilter} activeChild={dashboardFilter}
tabs={[ tabs={menuTabs}
{
name: 'Favorite',
label: t('Favorite'),
onClick: () => {
setDashboardFilter(TableTabTypes.FAVORITE);
setInLocalStorage(
HOMEPAGE_DASHBOARD_FILTER,
TableTabTypes.FAVORITE,
);
},
},
{
name: 'Mine',
label: t('Mine'),
onClick: () => {
setDashboardFilter(TableTabTypes.MINE);
setInLocalStorage(HOMEPAGE_DASHBOARD_FILTER, TableTabTypes.MINE);
},
},
]}
buttons={[ buttons={[
{ {
name: ( name: (

View File

@ -132,10 +132,10 @@ describe('Welcome', () => {
const savedQueryCall = fetchMock.calls(/saved_query\/\?q/); const savedQueryCall = fetchMock.calls(/saved_query\/\?q/);
const recentCall = fetchMock.calls(/superset\/recent_activity\/*/); const recentCall = fetchMock.calls(/superset\/recent_activity\/*/);
const dashboardCall = fetchMock.calls(/dashboard\/\?q/); const dashboardCall = fetchMock.calls(/dashboard\/\?q/);
expect(chartCall).toHaveLength(2); expect(chartCall).toHaveLength(1);
expect(recentCall).toHaveLength(1); expect(recentCall).toHaveLength(1);
expect(savedQueryCall).toHaveLength(1); expect(savedQueryCall).toHaveLength(1);
expect(dashboardCall).toHaveLength(2); expect(dashboardCall).toHaveLength(1);
}); });
}); });

View File

@ -54,6 +54,8 @@ export interface ActivityData {
Examples?: Array<object>; Examples?: Array<object>;
} }
const DEFAULT_TAB_ARR = ['2', '3'];
const WelcomeContainer = styled.div` const WelcomeContainer = styled.div`
background-color: ${({ theme }) => theme.colors.grayscale.light4}; background-color: ${({ theme }) => theme.colors.grayscale.light4};
.ant-row.menu { .ant-row.menu {
@ -114,11 +116,9 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
null, null,
); );
const [loadedCount, setLoadedCount] = useState(0); const [loadedCount, setLoadedCount] = useState(0);
const [activeState, setActiveState] = useState<Array<string>>([ const [activeState, setActiveState] = useState<Array<string>>(
'1', DEFAULT_TAB_ARR,
'2', );
'3',
]);
const userid = user.userId; const userid = user.userId;
const id = userid.toString(); const id = userid.toString();
@ -136,14 +136,15 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
if (res.viewed) { if (res.viewed) {
const filtered = reject(res.viewed, ['item_url', null]).map(r => r); const filtered = reject(res.viewed, ['item_url', null]).map(r => r);
data.Viewed = filtered; data.Viewed = filtered;
if (!activeTab) { if (!activeTab && data.Viewed) {
setActiveChild('Viewed'); setActiveChild('Viewed');
} else if (!activeTab && !data.Viewed) {
setActiveChild('Created');
} else setActiveChild(activeTab); } else setActiveChild(activeTab);
} else { } else {
if (!activeTab) setActiveChild('Created');
else setActiveChild(activeTab);
data.Examples = res.examples; data.Examples = res.examples;
if (activeTab === 'Viewed' || !activeTab) {
setActiveChild('Examples');
} else setActiveChild(activeTab);
} }
setActivityData(activityData => ({ ...activityData, ...data })); setActivityData(activityData => ({ ...activityData, ...data }));
}) })
@ -200,9 +201,14 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
}; };
useEffect(() => { useEffect(() => {
if (queryData?.length) { const defaultArr = DEFAULT_TAB_ARR;
setActiveState(['1', '2', '3', '4']); if (activityData?.Viewed) {
defaultArr.push('1');
} }
if (queryData?.length) {
defaultArr.push('4');
}
setActiveState(defaultArr);
setActivityData(activityData => ({ setActivityData(activityData => ({
...activityData, ...activityData,
Created: [ Created: [
@ -213,6 +219,8 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
})); }));
}, [chartData, queryData, dashboardData]); }, [chartData, queryData, dashboardData]);
const isRecentActivityLoading =
!activityData?.Examples && !activityData?.Viewed;
return ( return (
<WelcomeContainer> <WelcomeContainer>
<WelcomeNav> <WelcomeNav>
@ -243,21 +251,27 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
)} )}
</Collapse.Panel> </Collapse.Panel>
<Collapse.Panel header={t('Dashboards')} key="2"> <Collapse.Panel header={t('Dashboards')} key="2">
{!dashboardData ? ( {!dashboardData || isRecentActivityLoading ? (
<Loading position="inline" /> <Loading position="inline" />
) : ( ) : (
<DashboardTable <DashboardTable
user={user} user={user}
mine={dashboardData} mine={dashboardData}
showThumbnails={checked} showThumbnails={checked}
examples={activityData?.Examples}
/> />
)} )}
</Collapse.Panel> </Collapse.Panel>
<Collapse.Panel header={t('Charts')} key="3"> <Collapse.Panel header={t('Charts')} key="3">
{!chartData ? ( {!chartData || isRecentActivityLoading ? (
<Loading position="inline" /> <Loading position="inline" />
) : ( ) : (
<ChartTable showThumbnails={checked} user={user} mine={chartData} /> <ChartTable
showThumbnails={checked}
user={user}
mine={chartData}
examples={activityData?.Examples}
/>
)} )}
</Collapse.Panel> </Collapse.Panel>
<Collapse.Panel header={t('Saved queries')} key="4"> <Collapse.Panel header={t('Saved queries')} key="4">