Dashboard: Simpify is empty handling (#100189)

* Dashboard: Simpify is empty handling

* remove unused imports

* Update

* Update

* Update

* add code for other layouts

---------

Co-authored-by: Victor Marin <victor.marin@grafana.com>
This commit is contained in:
Torkel Ödegaard 2025-02-06 15:30:54 +01:00 committed by GitHub
parent ae33d49036
commit f1eac34b54
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 45 additions and 40 deletions

View File

@ -128,8 +128,6 @@ export interface DashboardSceneState extends SceneObjectState {
editPanel?: PanelEditor;
/** Scene object that handles the current drawer or modal */
overlay?: SceneObject;
/** The dashboard doesn't have panels */
isEmpty?: boolean;
/** Kiosk mode */
kioskMode?: KioskMode;
/** Share view */

View File

@ -5,7 +5,6 @@ import { PageLayoutType } from '@grafana/data';
import { SceneComponentProps } from '@grafana/scenes';
import { Page } from 'app/core/components/Page/Page';
import { getNavModel } from 'app/core/selectors/navModel';
import DashboardEmpty from 'app/features/dashboard/dashgrid/DashboardEmpty';
import { useSelector } from 'app/types';
import { DashboardEditPaneSplitter } from '../edit-pane/DashboardEditPaneSplitter';
@ -15,7 +14,7 @@ import { PanelSearchLayout } from './PanelSearchLayout';
import { DashboardAngularDeprecationBanner } from './angular/DashboardAngularDeprecationBanner';
export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardScene>) {
const { controls, overlay, editview, editPanel, isEmpty, viewPanelScene, panelSearch, panelsPerRow, isEditing } =
const { controls, overlay, editview, editPanel, viewPanelScene, panelSearch, panelsPerRow, isEditing } =
model.useState();
const { type } = useParams();
const location = useLocation();
@ -56,9 +55,6 @@ export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardS
return (
<>
<DashboardAngularDeprecationBanner dashboard={model} key="angular-deprecation-banner" />
{isEmpty && (
<DashboardEmpty dashboard={model} canCreate={!!model.state.meta.canEdit} key="dashboard-empty-state" />
)}
<bodyToRender.Component model={bodyToRender} />
</>
);

View File

@ -9,9 +9,11 @@ import {
sceneUtils,
SceneComponentProps,
SceneGridItemLike,
useSceneObjectState,
} from '@grafana/scenes';
import { GRID_COLUMN_COUNT } from 'app/core/constants';
import { t } from 'app/core/internationalization';
import DashboardEmpty from 'app/features/dashboard/dashgrid/DashboardEmpty';
import { isClonedKey, joinCloneKeys } from '../../utils/clone';
import { dashboardSceneGraph } from '../../utils/dashboardSceneGraph';
@ -22,6 +24,7 @@ import {
NEW_PANEL_WIDTH,
getVizPanelKeyForPanelId,
getGridItemKeyForPanelId,
getDashboardSceneFor,
} from '../../utils/utils';
import { DashboardLayoutManager } from '../types/DashboardLayoutManager';
@ -447,6 +450,16 @@ export class DefaultGridLayoutManager
}
public static Component = ({ model }: SceneComponentProps<DefaultGridLayoutManager>) => {
const { children } = useSceneObjectState(model.state.grid, { shouldActivateOrKeepAlive: true });
const dashboard = getDashboardSceneFor(model);
// If we are top level layout and have no children, show empty state
if (model.parent === dashboard && children.length === 0) {
return (
<DashboardEmpty dashboard={dashboard} canCreate={!!dashboard.state.meta.canEdit} key="dashboard-empty-state" />
);
}
return <model.state.grid.Component model={model.state.grid} />;
};
}

View File

@ -1,8 +1,16 @@
import { SelectableValue } from '@grafana/data';
import { SceneComponentProps, SceneCSSGridLayout, SceneObjectBase, SceneObjectState, VizPanel } from '@grafana/scenes';
import {
SceneComponentProps,
SceneCSSGridLayout,
SceneObjectBase,
SceneObjectState,
useSceneObjectState,
VizPanel,
} from '@grafana/scenes';
import { Select } from '@grafana/ui';
import { t } from 'app/core/internationalization';
import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor';
import DashboardEmpty from 'app/features/dashboard/dashgrid/DashboardEmpty';
import { dashboardSceneGraph } from '../../utils/dashboardSceneGraph';
import { getDashboardSceneFor, getGridItemKeyForPanelId, getVizPanelKeyForPanelId } from '../../utils/utils';
@ -140,6 +148,16 @@ export class ResponsiveGridLayoutManager
public activateRepeaters(): void {}
public static Component = ({ model }: SceneComponentProps<ResponsiveGridLayoutManager>) => {
const { children } = useSceneObjectState(model.state.layout, { shouldActivateOrKeepAlive: true });
const dashboard = getDashboardSceneFor(model);
// If we are top level layout and have no children, show empty state
if (model.parent === dashboard && children.length === 0) {
return (
<DashboardEmpty dashboard={dashboard} canCreate={!!dashboard.state.meta.canEdit} key="dashboard-empty-state" />
);
}
return <model.state.layout.Component model={model.state.layout} />;
};
}

View File

@ -12,8 +12,10 @@ import {
} from '@grafana/scenes';
import { useStyles2 } from '@grafana/ui';
import { t } from 'app/core/internationalization';
import DashboardEmpty from 'app/features/dashboard/dashgrid/DashboardEmpty';
import { isClonedKey } from '../../utils/clone';
import { getDashboardSceneFor } from '../../utils/utils';
import { DashboardScene } from '../DashboardScene';
import { DashboardGridItem } from '../layout-default/DashboardGridItem';
import { DefaultGridLayoutManager } from '../layout-default/DefaultGridLayoutManager';
@ -195,6 +197,14 @@ export class RowsLayoutManager extends SceneObjectBase<RowsLayoutManagerState> i
public static Component = ({ model }: SceneComponentProps<RowsLayoutManager>) => {
const { rows } = model.useState();
const styles = useStyles2(getStyles);
const dashboard = getDashboardSceneFor(model);
// If we are top level layout and have no children, show empty state
if (model.parent === dashboard && rows.length === 0) {
return (
<DashboardEmpty dashboard={dashboard} canCreate={!!dashboard.state.meta.canEdit} key="dashboard-empty-state" />
);
}
return (
<div className={styles.wrapper}>

View File

@ -88,7 +88,7 @@ import { RowRepeaterBehavior } from '../scene/layout-default/RowRepeaterBehavior
import { RowActions } from '../scene/layout-default/row-actions/RowActions';
import { setDashboardPanelContext } from '../scene/setDashboardPanelContext';
import { preserveDashboardSceneStateInLocalStorage } from '../utils/dashboardSessionState';
import { getDashboardSceneFor, getIntervalsFromQueryString, getVizPanelKeyForPanelId } from '../utils/utils';
import { getIntervalsFromQueryString, getVizPanelKeyForPanelId } from '../utils/utils';
import { GRID_ROW_HEIGHT } from './const';
import { SnapshotVariable } from './custom-variables/SnapshotVariable';
@ -181,7 +181,6 @@ export function transformSaveModelSchemaV2ToScene(dto: DashboardWithAccessInfo<D
grid: new SceneGridLayout({
isLazy: !(dashboard.preload || contextSrv.user.authenticatedBy === 'render'),
children: createSceneGridLayoutForItems(dashboard),
$behaviors: [trackIfEmpty],
}),
}),
$timeRange: new SceneTimeRange({
@ -404,20 +403,6 @@ function buildVizPanel(panel: PanelKind): VizPanel {
return new VizPanel(vizPanelState);
}
function trackIfEmpty(grid: SceneGridLayout) {
getDashboardSceneFor(grid).setState({ isEmpty: grid.state.children.length === 0 });
const sub = grid.subscribeToState((n, p) => {
if (n.children.length !== p.children.length || n.children !== p.children) {
getDashboardSceneFor(grid).setState({ isEmpty: n.children.length === 0 });
}
});
return () => {
sub.unsubscribe();
};
}
function getPanelDataSource(panel: PanelKind): DataSourceRef | undefined {
if (!panel.spec.data?.spec.queries?.length) {
return undefined;

View File

@ -50,7 +50,7 @@ import { setDashboardPanelContext } from '../scene/setDashboardPanelContext';
import { createPanelDataProvider } from '../utils/createPanelDataProvider';
import { preserveDashboardSceneStateInLocalStorage } from '../utils/dashboardSessionState';
import { DashboardInteractions } from '../utils/interactions';
import { getDashboardSceneFor, getVizPanelKeyForPanelId } from '../utils/utils';
import { getVizPanelKeyForPanelId } from '../utils/utils';
import { createVariablesForDashboard, createVariablesForSnapshot } from '../utils/variables';
import { getAngularPanelMigrationHandler } from './angularMigration';
@ -267,7 +267,6 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel,
grid: new SceneGridLayout({
isLazy: !(dto.preload || contextSrv.user.authenticatedBy === 'render'),
children: createSceneObjectsForPanels(oldModel.panels),
$behaviors: [trackIfEmpty],
}),
}),
$timeRange: new SceneTimeRange({
@ -426,20 +425,6 @@ export const convertOldSnapshotToScenesSnapshot = (panel: PanelModel) => {
}
};
function trackIfEmpty(grid: SceneGridLayout) {
getDashboardSceneFor(grid).setState({ isEmpty: grid.state.children.length === 0 });
const sub = grid.subscribeToState((n, p) => {
if (n.children.length !== p.children.length || n.children !== p.children) {
getDashboardSceneFor(grid).setState({ isEmpty: n.children.length === 0 });
}
});
return () => {
sub.unsubscribe();
};
}
function getDashboardInteractionCallback(uid: string, title: string) {
return (e: SceneInteractionProfileEvent) => {
let interactionType = '';