You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by di...@apache.org on 2024/05/22 13:54:22 UTC
(superset) branch geido/poc/dashboard-progressive-ui created (now a9c52ae938)
This is an automated email from the ASF dual-hosted git repository.
diegopucci pushed a change to branch geido/poc/dashboard-progressive-ui
in repository https://gitbox.apache.org/repos/asf/superset.git
at a9c52ae938 POC
This branch includes the following new commits:
new a9c52ae938 POC
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(superset) 01/01: POC
Posted by di...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
diegopucci pushed a commit to branch geido/poc/dashboard-progressive-ui
in repository https://gitbox.apache.org/repos/asf/superset.git
commit a9c52ae938e65ea7581a0868cce25ed7041cb982
Author: Diego Pucci <di...@gmail.com>
AuthorDate: Wed May 22 16:54:06 2024 +0300
POC
---
.../src/components/Chart/chartReducer.ts | 8 ++-
superset-frontend/src/dashboard/actions/hydrate.js | 61 ++++++++++++++++++++++
.../src/dashboard/containers/DashboardPage.tsx | 20 +++++--
.../src/dashboard/reducers/dashboardLayout.js | 8 ++-
.../src/dashboard/reducers/sliceEntities.js | 7 ++-
superset-frontend/src/explore/store.js | 2 +-
.../src/hooks/apiResources/dashboards.ts | 20 +++++++
7 files changed, 117 insertions(+), 9 deletions(-)
diff --git a/superset-frontend/src/components/Chart/chartReducer.ts b/superset-frontend/src/components/Chart/chartReducer.ts
index 997c5bf7b7..ab53d93473 100644
--- a/superset-frontend/src/components/Chart/chartReducer.ts
+++ b/superset-frontend/src/components/Chart/chartReducer.ts
@@ -19,7 +19,7 @@
/* eslint camelcase: 0 */
import { t } from '@superset-ui/core';
import { omit } from 'lodash';
-import { HYDRATE_DASHBOARD } from 'src/dashboard/actions/hydrate';
+import { HYDRATE_DASHBOARD, HYDRATE_LAYOUT } from 'src/dashboard/actions/hydrate';
import { DatasourcesAction } from 'src/dashboard/actions/datasources';
import { ChartState } from 'src/explore/types';
import { getFormDataFromControls } from 'src/explore/controlUtils';
@@ -192,7 +192,11 @@ export default function chartReducer(
delete charts[key];
return charts;
}
- if (action.type === HYDRATE_DASHBOARD || action.type === HYDRATE_EXPLORE) {
+ if (
+ action.type === HYDRATE_DASHBOARD ||
+ action.type === HYDRATE_EXPLORE ||
+ action.type === HYDRATE_LAYOUT
+ ) {
return { ...action.data.charts };
}
if (action.type === DatasourcesAction.SetDatasources) {
diff --git a/superset-frontend/src/dashboard/actions/hydrate.js b/superset-frontend/src/dashboard/actions/hydrate.js
index 7898ad0929..f3e2a4c2d8 100644
--- a/superset-frontend/src/dashboard/actions/hydrate.js
+++ b/superset-frontend/src/dashboard/actions/hydrate.js
@@ -56,6 +56,8 @@ import updateComponentParentsList from '../util/updateComponentParentsList';
import { FilterBarOrientation } from '../types';
export const HYDRATE_DASHBOARD = 'HYDRATE_DASHBOARD';
+export const HYDRATE_LAYOUT = 'HYDRATE_LAYOUT';
+
export const hydrateDashboard =
({ history, dashboard, charts, dataMask, activeTabs }) =>
@@ -329,3 +331,62 @@ export const hydrateDashboard =
},
});
};
+
+export const hydrateLayout =
+ positionData =>
+ (dispatch) => {
+ const layout =
+ positionData && Object.keys(positionData).length > 0
+ ? positionData
+ : getEmptyLayout();
+
+ const charts = {};
+ const slices = {};
+
+ Object.values(layout).forEach(layoutComponent => {
+ if (layoutComponent.type === CHART_TYPE) {
+ charts[layoutComponent.meta.chartId] = {
+ id: layoutComponent.meta.chartId,
+ form_data: {},
+ latestQueryFormData: {},
+ chartStatus: 'loading',
+ chartAlert: null,
+ queriesResponse: null,
+ queryController: null,
+ sliceFormData: null,
+ lastRendered: 0,
+ triggerQuery: false,
+ chartUpdateStartTime: 0,
+ chartUpdateEndTime: null,
+ chartStackTrace: null,
+ };
+ slices[layoutComponent.meta.chartId] = {
+ slice_id: layoutComponent.meta.chartId,
+ slice_url: '',
+ slice_name: layoutComponent.meta.sliceName,
+ form_data: {},
+ viz_type: '',
+ datasource: '',
+ description: '',
+ description_markeddown: '',
+ owners: [],
+ modified: '',
+ changed_on: 0,
+ };
+ }
+ });
+
+ return dispatch({
+ type: HYDRATE_LAYOUT,
+ data: {
+ layout,
+ charts,
+ sliceEntities: {
+ slices,
+ isLoading: true,
+ errorMessage: null,
+ lastUpdated: 0,
+ },
+ },
+ });
+ };
\ No newline at end of file
diff --git a/superset-frontend/src/dashboard/containers/DashboardPage.tsx b/superset-frontend/src/dashboard/containers/DashboardPage.tsx
index d9fe9191b5..aa6f8bfda3 100644
--- a/superset-frontend/src/dashboard/containers/DashboardPage.tsx
+++ b/superset-frontend/src/dashboard/containers/DashboardPage.tsx
@@ -33,8 +33,9 @@ import {
useDashboard,
useDashboardCharts,
useDashboardDatasets,
+ useDelayedDashboardCharts,
} from 'src/hooks/apiResources';
-import { hydrateDashboard } from 'src/dashboard/actions/hydrate';
+import { hydrateDashboard, hydrateLayout } from 'src/dashboard/actions/hydrate';
import { setDatasources } from 'src/dashboard/actions/datasources';
import injectCustomCss from 'src/dashboard/util/injectCustomCss';
@@ -90,14 +91,17 @@ export const DashboardPage: FC<PageProps> = ({ idOrSlug }: PageProps) => {
const { addDangerToast } = useToasts();
const { result: dashboard, error: dashboardApiError } =
useDashboard(idOrSlug);
- const { result: charts, error: chartsApiError } =
- useDashboardCharts(idOrSlug);
+
+ const res = useDelayedDashboardCharts(idOrSlug, 5000);
+ const { result: charts, error: chartsApiError } = res || {};
+
const {
result: datasets,
error: datasetsApiError,
status,
} = useDashboardDatasets(idOrSlug);
const isDashboardHydrated = useRef(false);
+ const isLayoutHydrated = useRef(false);
const error = dashboardApiError || chartsApiError;
const readyToRender = Boolean(dashboard && charts);
@@ -169,6 +173,13 @@ export const DashboardPage: FC<PageProps> = ({ idOrSlug }: PageProps) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [readyToRender]);
+ useEffect(() => {
+ if (dashboard && !readyToRender && !isLayoutHydrated.current) {
+ dispatch(hydrateLayout(dashboard.position_data));
+ isLayoutHydrated.current = true;
+ }
+ }, [dashboard, dispatch, readyToRender]);
+
useEffect(() => {
if (dashboard_title) {
document.title = dashboard_title;
@@ -211,7 +222,8 @@ export const DashboardPage: FC<PageProps> = ({ idOrSlug }: PageProps) => {
}, [addDangerToast, datasets, datasetsApiError, dispatch]);
if (error) throw error; // caught in error boundary
- if (!readyToRender || !hasDashboardInfoInitiated) return <Loading />;
+
+ if (!dashboard || !hasDashboardInfoInitiated) return <Loading />;
return (
<>
diff --git a/superset-frontend/src/dashboard/reducers/dashboardLayout.js b/superset-frontend/src/dashboard/reducers/dashboardLayout.js
index 6ba297be91..16d970e4a7 100644
--- a/superset-frontend/src/dashboard/reducers/dashboardLayout.js
+++ b/superset-frontend/src/dashboard/reducers/dashboardLayout.js
@@ -43,7 +43,7 @@ import {
DASHBOARD_TITLE_CHANGED,
} from '../actions/dashboardLayout';
-import { HYDRATE_DASHBOARD } from '../actions/hydrate';
+import { HYDRATE_DASHBOARD, HYDRATE_LAYOUT } from '../actions/hydrate';
export function recursivelyDeleteChildren(
componentId,
@@ -85,6 +85,12 @@ const actionHandlers = {
};
},
+ [HYDRATE_LAYOUT](state, action) {
+ return {
+ ...action.data.layout,
+ };
+ },
+
[UPDATE_COMPONENTS](state, action) {
const {
payload: { nextComponents },
diff --git a/superset-frontend/src/dashboard/reducers/sliceEntities.js b/superset-frontend/src/dashboard/reducers/sliceEntities.js
index 1a065b11fa..4c347e9575 100644
--- a/superset-frontend/src/dashboard/reducers/sliceEntities.js
+++ b/superset-frontend/src/dashboard/reducers/sliceEntities.js
@@ -24,7 +24,7 @@ import {
ADD_SLICES,
SET_SLICES,
} from '../actions/sliceEntities';
-import { HYDRATE_DASHBOARD } from '../actions/hydrate';
+import { HYDRATE_DASHBOARD, HYDRATE_LAYOUT } from '../actions/hydrate';
export const initSliceEntities = {
slices: {},
@@ -43,6 +43,11 @@ export default function sliceEntitiesReducer(
...action.data.sliceEntities,
};
},
+ [HYDRATE_LAYOUT]() {
+ return {
+ ...action.data.sliceEntities,
+ };
+ },
[FETCH_ALL_SLICES_STARTED]() {
return {
...state,
diff --git a/superset-frontend/src/explore/store.js b/superset-frontend/src/explore/store.js
index 239884bcde..346b040946 100644
--- a/superset-frontend/src/explore/store.js
+++ b/superset-frontend/src/explore/store.js
@@ -61,7 +61,7 @@ export function getControlsState(state, inputFormData) {
}
export function applyDefaultFormData(inputFormData) {
- const datasourceType = inputFormData.datasource.split('__')[1];
+ const datasourceType = inputFormData?.datasource?.split('__')[1];
const vizType = inputFormData.viz_type;
const controlsState = getAllControlsState(
vizType,
diff --git a/superset-frontend/src/hooks/apiResources/dashboards.ts b/superset-frontend/src/hooks/apiResources/dashboards.ts
index 61896ba130..b8c50b1be5 100644
--- a/superset-frontend/src/hooks/apiResources/dashboards.ts
+++ b/superset-frontend/src/hooks/apiResources/dashboards.ts
@@ -19,6 +19,7 @@
import { Dashboard, Datasource, EmbeddedDashboard } from 'src/dashboard/types';
import { Chart } from 'src/types/Chart';
+import { useEffect, useState } from 'react';
import { useApiV1Resource, useTransformedResource } from './apiResources';
export const useDashboard = (idOrSlug: string | number) =>
@@ -39,6 +40,25 @@ export const useDashboard = (idOrSlug: string | number) =>
export const useDashboardCharts = (idOrSlug: string | number) =>
useApiV1Resource<Chart[]>(`/api/v1/dashboard/${idOrSlug}/charts`);
+export const useDelayedDashboardCharts = (
+ idOrSlug: string | number,
+ delay: number,
+) => {
+ const [delayed, setDelayed] = useState(true);
+
+ useEffect(() => {
+ const timer = setTimeout(() => {
+ setDelayed(false);
+ }, delay);
+
+ return () => clearTimeout(timer); // Cleanup the timer on component unmount
+ }, [delay]);
+
+ const res = useDashboardCharts(idOrSlug);
+
+ return delayed ? undefined : res;
+};
+
// gets the datasets for a dashboard
// important: this endpoint only returns the fields in the dataset
// that are necessary for rendering the given dashboard