mirror of
https://github.com/grafana/grafana.git
synced 2024-11-26 02:40:26 -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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!vizPanel.parent) {
|
||||
return;
|
||||
@ -504,6 +522,20 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
|
||||
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
|
||||
*/
|
||||
|
@ -7,6 +7,7 @@ import { SceneComponentProps, SceneDebugger } from '@grafana/scenes';
|
||||
import { CustomScrollbar, useStyles2 } from '@grafana/ui';
|
||||
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 { 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 (
|
||||
<Page navModel={navModel} pageNav={pageNav} layout={PageLayoutType.Custom}>
|
||||
{editPanel && <editPanel.Component model={editPanel} />}
|
||||
@ -38,18 +62,7 @@ export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardS
|
||||
<CustomScrollbar autoHeightMin={'100%'}>
|
||||
<div className={styles.canvasContent}>
|
||||
<NavToolbarActions dashboard={model} />
|
||||
|
||||
{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>
|
||||
{model.isEmpty() ? emptyState : withPanels}
|
||||
</div>
|
||||
</CustomScrollbar>
|
||||
)}
|
||||
@ -86,6 +99,7 @@ function getStyles(theme: GrafanaTheme2) {
|
||||
background: theme.colors.background.canvas,
|
||||
zIndex: theme.zIndex.activePanel,
|
||||
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 {
|
||||
MultiValueVariable,
|
||||
SceneDataTransformer,
|
||||
@ -6,10 +7,13 @@ import {
|
||||
SceneObject,
|
||||
SceneQueryRunner,
|
||||
VizPanel,
|
||||
VizPanelMenu,
|
||||
} from '@grafana/scenes';
|
||||
import { initialIntervalVariableModelState } from 'app/features/variables/interval/reducer';
|
||||
|
||||
import { DashboardScene } from '../scene/DashboardScene';
|
||||
import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks';
|
||||
import { panelMenuBehavior } from '../scene/PanelMenuBehavior';
|
||||
|
||||
export function getVizPanelKeyForPanelId(panelId: number) {
|
||||
return `panel-${panelId}`;
|
||||
@ -187,3 +191,26 @@ export function getClosestVizPanel(sceneObject: SceneObject): VizPanel | null {
|
||||
export function isPanelClone(key: string) {
|
||||
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 { DashboardModel } from 'app/features/dashboard/state';
|
||||
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 { onCreateNewPanel as onCreateNewPanelScene } from 'app/features/dashboard-scene/utils/utils';
|
||||
import { useDispatch, useSelector } from 'app/types';
|
||||
|
||||
import { setInitialDatasource } from '../state/reducers';
|
||||
|
||||
export interface Props {
|
||||
dashboard: DashboardModel;
|
||||
dashboard: DashboardModel | DashboardScene;
|
||||
canCreate: boolean;
|
||||
}
|
||||
|
||||
@ -22,6 +24,20 @@ const DashboardEmpty = ({ dashboard, canCreate }: Props) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
const dispatch = useDispatch();
|
||||
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 (
|
||||
<Stack alignItems="center" justifyContent="center">
|
||||
@ -46,13 +62,7 @@ const DashboardEmpty = ({ dashboard, canCreate }: Props) => {
|
||||
size="lg"
|
||||
icon="plus"
|
||||
data-testid={selectors.pages.AddDashboard.itemButton('Create new panel button')}
|
||||
onClick={() => {
|
||||
const id = onCreateNewPanel(dashboard, initialDatasource);
|
||||
DashboardInteractions.emptyDashboardButtonClicked({ item: 'add_visualization' });
|
||||
|
||||
locationService.partial({ editPanel: id, firstPanel: true });
|
||||
dispatch(setInitialDatasource(undefined));
|
||||
}}
|
||||
onClick={onAddVisualization}
|
||||
disabled={!canCreate}
|
||||
>
|
||||
<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')}
|
||||
onClick={() => {
|
||||
DashboardInteractions.emptyDashboardButtonClicked({ item: 'import_from_library' });
|
||||
onAddLibraryPanel(dashboard);
|
||||
if (isDashboardScene) {
|
||||
// TODO: dashboard scene logic for adding a library panel
|
||||
} else {
|
||||
onAddLibraryPanel(dashboard);
|
||||
}
|
||||
}}
|
||||
disabled={!canCreate}
|
||||
>
|
||||
|
Loading…
Reference in New Issue
Block a user