mirror of https://github.com/apache/superset.git
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:
parent
5e1c469f42
commit
04c0680f6e
|
@ -26,6 +26,7 @@ export type FavoriteStatus = {
|
|||
export enum TableTabTypes {
|
||||
FAVORITE = 'Favorite',
|
||||
MINE = 'Mine',
|
||||
EXAMPLES = 'Examples',
|
||||
}
|
||||
|
||||
export type Filters = {
|
||||
|
@ -42,6 +43,7 @@ export interface DashboardTableProps {
|
|||
mine: Array<Dashboard>;
|
||||
showThumbnails?: boolean;
|
||||
featureFlag?: boolean;
|
||||
examples: Array<Dashboard>;
|
||||
}
|
||||
|
||||
export interface Dashboard {
|
||||
|
|
|
@ -219,17 +219,7 @@ export default function ActivityTable({
|
|||
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 = () =>
|
||||
(activeChild !== 'Edited' ? activityData[activeChild] : editedObjs).map(
|
||||
(entity: ActivityObject) => {
|
||||
|
|
|
@ -85,7 +85,7 @@ describe('ChartTable', () => {
|
|||
}
|
||||
});
|
||||
await waitForComponentToPaint(wrapper);
|
||||
expect(fetchMock.calls(chartsEndpoint)).toHaveLength(3);
|
||||
expect(fetchMock.calls(chartsEndpoint)).toHaveLength(1);
|
||||
expect(wrapper.find('ChartCard')).toExist();
|
||||
});
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
import React, { useState, useMemo, useEffect } from 'react';
|
||||
import { t } from '@superset-ui/core';
|
||||
import { filter } from 'lodash';
|
||||
import {
|
||||
useListViewResource,
|
||||
useChartEditModal,
|
||||
|
@ -52,6 +53,7 @@ interface ChartTableProps {
|
|||
user?: User;
|
||||
mine: Array<any>;
|
||||
showThumbnails: boolean;
|
||||
examples?: Array<object>;
|
||||
}
|
||||
|
||||
function ChartTable({
|
||||
|
@ -60,10 +62,17 @@ function ChartTable({
|
|||
addSuccessToast,
|
||||
mine,
|
||||
showThumbnails,
|
||||
examples,
|
||||
}: ChartTableProps) {
|
||||
const history = useHistory();
|
||||
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 {
|
||||
state: { loading, resourceCollection: charts, bulkSelectEnabled },
|
||||
|
@ -76,7 +85,7 @@ function ChartTable({
|
|||
t('chart'),
|
||||
addDangerToast,
|
||||
true,
|
||||
initialFilter === 'Favorite' ? [] : mine,
|
||||
initialFilter === 'Mine' ? mine : filteredExamples,
|
||||
[],
|
||||
false,
|
||||
);
|
||||
|
@ -96,9 +105,13 @@ function ChartTable({
|
|||
|
||||
const [chartFilter, setChartFilter] = useState(initialFilter);
|
||||
const [preparingExport, setPreparingExport] = useState<boolean>(false);
|
||||
const [loaded, setLoaded] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
getData(chartFilter);
|
||||
if (loaded || chartFilter === 'Favorite') {
|
||||
getData(chartFilter);
|
||||
}
|
||||
setLoaded(true);
|
||||
}, [chartFilter]);
|
||||
|
||||
const handleBulkChartExport = (chartsToExport: Chart[]) => {
|
||||
|
@ -118,7 +131,7 @@ function ChartTable({
|
|||
operator: 'rel_o_m',
|
||||
value: `${user?.userId}`,
|
||||
});
|
||||
} else {
|
||||
} else if (filterName === 'Favorite') {
|
||||
filters.push({
|
||||
id: 'id',
|
||||
operator: 'chart_is_favorite',
|
||||
|
@ -128,6 +141,36 @@ function ChartTable({
|
|||
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) =>
|
||||
fetchData({
|
||||
pageIndex: 0,
|
||||
|
@ -156,24 +199,7 @@ function ChartTable({
|
|||
<SubMenu
|
||||
activeChild={chartFilter}
|
||||
// eslint-disable-next-line react/no-children-prop
|
||||
tabs={[
|
||||
{
|
||||
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);
|
||||
},
|
||||
},
|
||||
]}
|
||||
tabs={menuTabs}
|
||||
buttons={[
|
||||
{
|
||||
name: (
|
||||
|
|
|
@ -72,9 +72,9 @@ describe('DashboardTable', () => {
|
|||
it('render a submenu with clickable tabs and buttons', async () => {
|
||||
expect(wrapper.find('SubMenu')).toExist();
|
||||
expect(wrapper.find('li.no-router')).toHaveLength(2);
|
||||
expect(wrapper.find('Button')).toHaveLength(4);
|
||||
expect(wrapper.find('Button')).toHaveLength(6);
|
||||
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) {
|
||||
handler({} as any);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
import React, { useState, useMemo, useEffect } from 'react';
|
||||
import { SupersetClient, t } from '@superset-ui/core';
|
||||
import { filter } from 'lodash';
|
||||
import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks';
|
||||
import {
|
||||
Dashboard,
|
||||
|
@ -54,10 +55,17 @@ function DashboardTable({
|
|||
addSuccessToast,
|
||||
mine,
|
||||
showThumbnails,
|
||||
examples,
|
||||
}: DashboardTableProps) {
|
||||
const history = useHistory();
|
||||
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 {
|
||||
state: { loading, resourceCollection: dashboards },
|
||||
|
@ -70,7 +78,7 @@ function DashboardTable({
|
|||
t('dashboard'),
|
||||
addDangerToast,
|
||||
true,
|
||||
defaultFilter === 'Favorite' ? [] : mine,
|
||||
defaultFilter === 'Mine' ? mine : filteredExamples,
|
||||
[],
|
||||
false,
|
||||
);
|
||||
|
@ -84,9 +92,13 @@ function DashboardTable({
|
|||
const [editModal, setEditModal] = useState<Dashboard>();
|
||||
const [dashboardFilter, setDashboardFilter] = useState(defaultFilter);
|
||||
const [preparingExport, setPreparingExport] = useState<boolean>(false);
|
||||
const [loaded, setLoaded] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
getData(dashboardFilter);
|
||||
if (loaded || dashboardFilter === 'Favorite') {
|
||||
getData(dashboardFilter);
|
||||
}
|
||||
setLoaded(true);
|
||||
}, [dashboardFilter]);
|
||||
|
||||
const handleBulkDashboardExport = (dashboardsToExport: Dashboard[]) => {
|
||||
|
@ -126,7 +138,7 @@ function DashboardTable({
|
|||
operator: 'rel_m_m',
|
||||
value: `${user?.userId}`,
|
||||
});
|
||||
} else {
|
||||
} else if (filterName === 'Favorite') {
|
||||
filters.push({
|
||||
id: 'id',
|
||||
operator: 'dashboard_is_favorite',
|
||||
|
@ -136,6 +148,36 @@ function DashboardTable({
|
|||
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) =>
|
||||
fetchData({
|
||||
pageIndex: 0,
|
||||
|
@ -154,27 +196,7 @@ function DashboardTable({
|
|||
<>
|
||||
<SubMenu
|
||||
activeChild={dashboardFilter}
|
||||
tabs={[
|
||||
{
|
||||
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);
|
||||
},
|
||||
},
|
||||
]}
|
||||
tabs={menuTabs}
|
||||
buttons={[
|
||||
{
|
||||
name: (
|
||||
|
|
|
@ -132,10 +132,10 @@ describe('Welcome', () => {
|
|||
const savedQueryCall = fetchMock.calls(/saved_query\/\?q/);
|
||||
const recentCall = fetchMock.calls(/superset\/recent_activity\/*/);
|
||||
const dashboardCall = fetchMock.calls(/dashboard\/\?q/);
|
||||
expect(chartCall).toHaveLength(2);
|
||||
expect(chartCall).toHaveLength(1);
|
||||
expect(recentCall).toHaveLength(1);
|
||||
expect(savedQueryCall).toHaveLength(1);
|
||||
expect(dashboardCall).toHaveLength(2);
|
||||
expect(dashboardCall).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -54,6 +54,8 @@ export interface ActivityData {
|
|||
Examples?: Array<object>;
|
||||
}
|
||||
|
||||
const DEFAULT_TAB_ARR = ['2', '3'];
|
||||
|
||||
const WelcomeContainer = styled.div`
|
||||
background-color: ${({ theme }) => theme.colors.grayscale.light4};
|
||||
.ant-row.menu {
|
||||
|
@ -114,11 +116,9 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
|
|||
null,
|
||||
);
|
||||
const [loadedCount, setLoadedCount] = useState(0);
|
||||
const [activeState, setActiveState] = useState<Array<string>>([
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
]);
|
||||
const [activeState, setActiveState] = useState<Array<string>>(
|
||||
DEFAULT_TAB_ARR,
|
||||
);
|
||||
const userid = user.userId;
|
||||
const id = userid.toString();
|
||||
|
||||
|
@ -136,14 +136,15 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
|
|||
if (res.viewed) {
|
||||
const filtered = reject(res.viewed, ['item_url', null]).map(r => r);
|
||||
data.Viewed = filtered;
|
||||
if (!activeTab) {
|
||||
if (!activeTab && data.Viewed) {
|
||||
setActiveChild('Viewed');
|
||||
} else if (!activeTab && !data.Viewed) {
|
||||
setActiveChild('Created');
|
||||
} else setActiveChild(activeTab);
|
||||
} else {
|
||||
if (!activeTab) setActiveChild('Created');
|
||||
else setActiveChild(activeTab);
|
||||
data.Examples = res.examples;
|
||||
if (activeTab === 'Viewed' || !activeTab) {
|
||||
setActiveChild('Examples');
|
||||
} else setActiveChild(activeTab);
|
||||
}
|
||||
setActivityData(activityData => ({ ...activityData, ...data }));
|
||||
})
|
||||
|
@ -200,9 +201,14 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (queryData?.length) {
|
||||
setActiveState(['1', '2', '3', '4']);
|
||||
const defaultArr = DEFAULT_TAB_ARR;
|
||||
if (activityData?.Viewed) {
|
||||
defaultArr.push('1');
|
||||
}
|
||||
if (queryData?.length) {
|
||||
defaultArr.push('4');
|
||||
}
|
||||
setActiveState(defaultArr);
|
||||
setActivityData(activityData => ({
|
||||
...activityData,
|
||||
Created: [
|
||||
|
@ -213,6 +219,8 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
|
|||
}));
|
||||
}, [chartData, queryData, dashboardData]);
|
||||
|
||||
const isRecentActivityLoading =
|
||||
!activityData?.Examples && !activityData?.Viewed;
|
||||
return (
|
||||
<WelcomeContainer>
|
||||
<WelcomeNav>
|
||||
|
@ -243,21 +251,27 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
|
|||
)}
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel header={t('Dashboards')} key="2">
|
||||
{!dashboardData ? (
|
||||
{!dashboardData || isRecentActivityLoading ? (
|
||||
<Loading position="inline" />
|
||||
) : (
|
||||
<DashboardTable
|
||||
user={user}
|
||||
mine={dashboardData}
|
||||
showThumbnails={checked}
|
||||
examples={activityData?.Examples}
|
||||
/>
|
||||
)}
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel header={t('Charts')} key="3">
|
||||
{!chartData ? (
|
||||
{!chartData || isRecentActivityLoading ? (
|
||||
<Loading position="inline" />
|
||||
) : (
|
||||
<ChartTable showThumbnails={checked} user={user} mine={chartData} />
|
||||
<ChartTable
|
||||
showThumbnails={checked}
|
||||
user={user}
|
||||
mine={chartData}
|
||||
examples={activityData?.Examples}
|
||||
/>
|
||||
)}
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel header={t('Saved queries')} key="4">
|
||||
|
|
Loading…
Reference in New Issue