mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
DashboardScene: Empty dashboard state (#82338)
* Organize * Refactor * Fix where settings were not showing up
This commit is contained in:
parent
f4d81a8480
commit
16f5220adc
@ -414,6 +414,24 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
|
|||||||
return this._initialState;
|
return this._initialState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addPanel(vizPanel: VizPanel): void {
|
||||||
|
// TODO: need logic for adding a panel when other panels exist
|
||||||
|
// This is the logic when dashboard is empty
|
||||||
|
this.setState({
|
||||||
|
body: new SceneGridLayout({
|
||||||
|
children: [
|
||||||
|
new SceneGridItem({
|
||||||
|
height: 10,
|
||||||
|
width: 10,
|
||||||
|
x: 0.2,
|
||||||
|
y: 0,
|
||||||
|
body: vizPanel,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public duplicatePanel(vizPanel: VizPanel) {
|
public duplicatePanel(vizPanel: VizPanel) {
|
||||||
if (!vizPanel.parent) {
|
if (!vizPanel.parent) {
|
||||||
return;
|
return;
|
||||||
@ -504,6 +522,20 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
|
|||||||
locationService.partial({ editview: 'settings' });
|
locationService.partial({ editview: 'settings' });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public isEmpty = (): boolean => {
|
||||||
|
const { body, viewPanelScene } = this.state;
|
||||||
|
|
||||||
|
if (!!viewPanelScene) {
|
||||||
|
return !!viewPanelScene.state.body;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body instanceof SceneFlexLayout || body instanceof SceneGridLayout) {
|
||||||
|
return body.state.children.length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Invalid body type');
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by the SceneQueryRunner to privide contextural parameters (tracking) props for the request
|
* Called by the SceneQueryRunner to privide contextural parameters (tracking) props for the request
|
||||||
*/
|
*/
|
||||||
|
@ -7,6 +7,7 @@ import { SceneComponentProps, SceneDebugger } from '@grafana/scenes';
|
|||||||
import { CustomScrollbar, useStyles2 } from '@grafana/ui';
|
import { CustomScrollbar, useStyles2 } from '@grafana/ui';
|
||||||
import { Page } from 'app/core/components/Page/Page';
|
import { Page } from 'app/core/components/Page/Page';
|
||||||
import { getNavModel } from 'app/core/selectors/navModel';
|
import { getNavModel } from 'app/core/selectors/navModel';
|
||||||
|
import DashboardEmpty from 'app/features/dashboard/dashgrid/DashboardEmpty';
|
||||||
import { useSelector } from 'app/types';
|
import { useSelector } from 'app/types';
|
||||||
|
|
||||||
import { DashboardScene } from './DashboardScene';
|
import { DashboardScene } from './DashboardScene';
|
||||||
@ -31,6 +32,29 @@ export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardS
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const emptyState = (
|
||||||
|
<>
|
||||||
|
<div className={styles.controls}>{showDebugger && <SceneDebugger scene={model} key={'scene-debugger'} />}</div>
|
||||||
|
<DashboardEmpty dashboard={model} canCreate={!!model.state.meta.canEdit} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const withPanels = (
|
||||||
|
<>
|
||||||
|
{controls && (
|
||||||
|
<div className={styles.controls}>
|
||||||
|
{controls.map((control) => (
|
||||||
|
<control.Component key={control.state.key} model={control} />
|
||||||
|
))}
|
||||||
|
{showDebugger && <SceneDebugger scene={model} key={'scene-debugger'} />}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className={cx(styles.body)}>
|
||||||
|
<bodyToRender.Component model={bodyToRender} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page navModel={navModel} pageNav={pageNav} layout={PageLayoutType.Custom}>
|
<Page navModel={navModel} pageNav={pageNav} layout={PageLayoutType.Custom}>
|
||||||
{editPanel && <editPanel.Component model={editPanel} />}
|
{editPanel && <editPanel.Component model={editPanel} />}
|
||||||
@ -38,18 +62,7 @@ export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardS
|
|||||||
<CustomScrollbar autoHeightMin={'100%'}>
|
<CustomScrollbar autoHeightMin={'100%'}>
|
||||||
<div className={styles.canvasContent}>
|
<div className={styles.canvasContent}>
|
||||||
<NavToolbarActions dashboard={model} />
|
<NavToolbarActions dashboard={model} />
|
||||||
|
{model.isEmpty() ? emptyState : withPanels}
|
||||||
{controls && (
|
|
||||||
<div className={styles.controls}>
|
|
||||||
{controls.map((control) => (
|
|
||||||
<control.Component key={control.state.key} model={control} />
|
|
||||||
))}
|
|
||||||
{showDebugger && <SceneDebugger scene={model} key={'scene-debugger'} />}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className={cx(styles.body)}>
|
|
||||||
<bodyToRender.Component model={bodyToRender} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</CustomScrollbar>
|
</CustomScrollbar>
|
||||||
)}
|
)}
|
||||||
@ -86,6 +99,7 @@ function getStyles(theme: GrafanaTheme2) {
|
|||||||
background: theme.colors.background.canvas,
|
background: theme.colors.background.canvas,
|
||||||
zIndex: theme.zIndex.activePanel,
|
zIndex: theme.zIndex.activePanel,
|
||||||
padding: theme.spacing(2, 0),
|
padding: theme.spacing(2, 0),
|
||||||
|
marginLeft: 'auto',
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { IntervalVariableModel } from '@grafana/data';
|
import { getDataSourceRef, IntervalVariableModel } from '@grafana/data';
|
||||||
|
import { getDataSourceSrv } from '@grafana/runtime';
|
||||||
import {
|
import {
|
||||||
MultiValueVariable,
|
MultiValueVariable,
|
||||||
SceneDataTransformer,
|
SceneDataTransformer,
|
||||||
@ -6,10 +7,13 @@ import {
|
|||||||
SceneObject,
|
SceneObject,
|
||||||
SceneQueryRunner,
|
SceneQueryRunner,
|
||||||
VizPanel,
|
VizPanel,
|
||||||
|
VizPanelMenu,
|
||||||
} from '@grafana/scenes';
|
} from '@grafana/scenes';
|
||||||
import { initialIntervalVariableModelState } from 'app/features/variables/interval/reducer';
|
import { initialIntervalVariableModelState } from 'app/features/variables/interval/reducer';
|
||||||
|
|
||||||
import { DashboardScene } from '../scene/DashboardScene';
|
import { DashboardScene } from '../scene/DashboardScene';
|
||||||
|
import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks';
|
||||||
|
import { panelMenuBehavior } from '../scene/PanelMenuBehavior';
|
||||||
|
|
||||||
export function getVizPanelKeyForPanelId(panelId: number) {
|
export function getVizPanelKeyForPanelId(panelId: number) {
|
||||||
return `panel-${panelId}`;
|
return `panel-${panelId}`;
|
||||||
@ -187,3 +191,26 @@ export function getClosestVizPanel(sceneObject: SceneObject): VizPanel | null {
|
|||||||
export function isPanelClone(key: string) {
|
export function isPanelClone(key: string) {
|
||||||
return key.includes('clone');
|
return key.includes('clone');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function onCreateNewPanel(dashboard: DashboardScene): number {
|
||||||
|
const vizPanel = new VizPanel({
|
||||||
|
title: 'Panel Title',
|
||||||
|
key: 'panel-1', // the first panel should always be panel-1
|
||||||
|
pluginId: 'timeseries',
|
||||||
|
titleItems: [new VizPanelLinks({ menu: new VizPanelLinksMenu({}) })],
|
||||||
|
menu: new VizPanelMenu({
|
||||||
|
$behaviors: [panelMenuBehavior],
|
||||||
|
}),
|
||||||
|
$data: new SceneDataTransformer({
|
||||||
|
$data: new SceneQueryRunner({
|
||||||
|
queries: [{ refId: 'A' }],
|
||||||
|
datasource: getDataSourceRef(getDataSourceSrv().getInstanceSettings(null)!),
|
||||||
|
}),
|
||||||
|
transformations: [],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
dashboard.addPanel(vizPanel);
|
||||||
|
const id = getPanelIdForVizPanel(vizPanel);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
@ -8,13 +8,15 @@ import { Button, useStyles2, Text, Box, Stack } from '@grafana/ui';
|
|||||||
import { Trans } from 'app/core/internationalization';
|
import { Trans } from 'app/core/internationalization';
|
||||||
import { DashboardModel } from 'app/features/dashboard/state';
|
import { DashboardModel } from 'app/features/dashboard/state';
|
||||||
import { onAddLibraryPanel, onCreateNewPanel, onImportDashboard } from 'app/features/dashboard/utils/dashboard';
|
import { onAddLibraryPanel, onCreateNewPanel, onImportDashboard } from 'app/features/dashboard/utils/dashboard';
|
||||||
|
import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene';
|
||||||
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||||
|
import { onCreateNewPanel as onCreateNewPanelScene } from 'app/features/dashboard-scene/utils/utils';
|
||||||
import { useDispatch, useSelector } from 'app/types';
|
import { useDispatch, useSelector } from 'app/types';
|
||||||
|
|
||||||
import { setInitialDatasource } from '../state/reducers';
|
import { setInitialDatasource } from '../state/reducers';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
dashboard: DashboardModel;
|
dashboard: DashboardModel | DashboardScene;
|
||||||
canCreate: boolean;
|
canCreate: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,6 +24,20 @@ const DashboardEmpty = ({ dashboard, canCreate }: Props) => {
|
|||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const initialDatasource = useSelector((state) => state.dashboard.initialDatasource);
|
const initialDatasource = useSelector((state) => state.dashboard.initialDatasource);
|
||||||
|
const isDashboardScene = dashboard instanceof DashboardScene;
|
||||||
|
|
||||||
|
const onAddVisualization = () => {
|
||||||
|
let id;
|
||||||
|
if (isDashboardScene) {
|
||||||
|
id = onCreateNewPanelScene(dashboard);
|
||||||
|
} else {
|
||||||
|
id = onCreateNewPanel(dashboard, initialDatasource);
|
||||||
|
dispatch(setInitialDatasource(undefined));
|
||||||
|
}
|
||||||
|
|
||||||
|
locationService.partial({ editPanel: id, firstPanel: true });
|
||||||
|
DashboardInteractions.emptyDashboardButtonClicked({ item: 'add_visualization' });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack alignItems="center" justifyContent="center">
|
<Stack alignItems="center" justifyContent="center">
|
||||||
@ -46,13 +62,7 @@ const DashboardEmpty = ({ dashboard, canCreate }: Props) => {
|
|||||||
size="lg"
|
size="lg"
|
||||||
icon="plus"
|
icon="plus"
|
||||||
data-testid={selectors.pages.AddDashboard.itemButton('Create new panel button')}
|
data-testid={selectors.pages.AddDashboard.itemButton('Create new panel button')}
|
||||||
onClick={() => {
|
onClick={onAddVisualization}
|
||||||
const id = onCreateNewPanel(dashboard, initialDatasource);
|
|
||||||
DashboardInteractions.emptyDashboardButtonClicked({ item: 'add_visualization' });
|
|
||||||
|
|
||||||
locationService.partial({ editPanel: id, firstPanel: true });
|
|
||||||
dispatch(setInitialDatasource(undefined));
|
|
||||||
}}
|
|
||||||
disabled={!canCreate}
|
disabled={!canCreate}
|
||||||
>
|
>
|
||||||
<Trans i18nKey="dashboard.empty.add-visualization-button">Add visualization</Trans>
|
<Trans i18nKey="dashboard.empty.add-visualization-button">Add visualization</Trans>
|
||||||
@ -104,7 +114,11 @@ const DashboardEmpty = ({ dashboard, canCreate }: Props) => {
|
|||||||
data-testid={selectors.pages.AddDashboard.itemButton('Add a panel from the panel library button')}
|
data-testid={selectors.pages.AddDashboard.itemButton('Add a panel from the panel library button')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
DashboardInteractions.emptyDashboardButtonClicked({ item: 'import_from_library' });
|
DashboardInteractions.emptyDashboardButtonClicked({ item: 'import_from_library' });
|
||||||
onAddLibraryPanel(dashboard);
|
if (isDashboardScene) {
|
||||||
|
// TODO: dashboard scene logic for adding a library panel
|
||||||
|
} else {
|
||||||
|
onAddLibraryPanel(dashboard);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
disabled={!canCreate}
|
disabled={!canCreate}
|
||||||
>
|
>
|
||||||
|
Loading…
Reference in New Issue
Block a user