mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
DashboardScene: Fix issues with dashboard empty state (#85406)
Fix Tests Make sure edit mode is on when adding panel/library panel Co-authored-by: kay delaney <kay@grafana.com>
This commit is contained in:
parent
368fec9b97
commit
fa9e139123
@ -8,10 +8,13 @@ import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
|
||||
import { PanelProps } from '@grafana/data';
|
||||
import { getPanelPlugin } from '@grafana/data/test/__mocks__/pluginMocks';
|
||||
import { config, getPluginLinkExtensions, locationService, setPluginImportUtils } from '@grafana/runtime';
|
||||
import { VizPanel } from '@grafana/scenes';
|
||||
import { Dashboard } from '@grafana/schema';
|
||||
import { getRouteComponentProps } from 'app/core/navigation/__mocks__/routeProps';
|
||||
import { DashboardLoaderSrv, setDashboardLoaderSrv } from 'app/features/dashboard/services/DashboardLoaderSrv';
|
||||
|
||||
import { dashboardSceneGraph } from '../utils/dashboardSceneGraph';
|
||||
|
||||
import { DashboardScenePage, Props } from './DashboardScenePage';
|
||||
import { getDashboardScenePageStateManager } from './DashboardScenePageStateManager';
|
||||
|
||||
@ -195,11 +198,43 @@ describe('DashboardScenePage', () => {
|
||||
expect(await screen.findByTitle('Panel B')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Shows empty state when dashboard is empty', async () => {
|
||||
loadDashboardMock.mockResolvedValue({ dashboard: { panels: [] }, meta: {} });
|
||||
setup();
|
||||
describe('empty state', () => {
|
||||
it('Shows empty state when dashboard is empty', async () => {
|
||||
loadDashboardMock.mockResolvedValue({ dashboard: { panels: [] }, meta: {} });
|
||||
setup();
|
||||
|
||||
expect(await screen.findByText('Start your new dashboard by adding a visualization')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Start your new dashboard by adding a visualization')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows and hides empty state when panels are added and removed', async () => {
|
||||
setup();
|
||||
|
||||
await waitForDashbordToRender();
|
||||
|
||||
expect(await screen.queryByText('Start your new dashboard by adding a visualization')).not.toBeInTheDocument();
|
||||
|
||||
// Hacking a bit, accessing private cache property to get access to the underlying DashboardScene object
|
||||
const dashboardScenesCache = getDashboardScenePageStateManager()['cache'];
|
||||
const dashboard = dashboardScenesCache['my-dash-uid'];
|
||||
const panels = dashboardSceneGraph.getVizPanels(dashboard);
|
||||
|
||||
act(() => {
|
||||
dashboard.removePanel(panels[0]);
|
||||
});
|
||||
expect(await screen.queryByText('Start your new dashboard by adding a visualization')).not.toBeInTheDocument();
|
||||
|
||||
act(() => {
|
||||
dashboard.removePanel(panels[1]);
|
||||
});
|
||||
expect(await screen.findByText('Start your new dashboard by adding a visualization')).toBeInTheDocument();
|
||||
|
||||
act(() => {
|
||||
dashboard.addPanel(new VizPanel({ title: 'Panel Added', key: 'panel-4', pluginId: 'timeseries' }));
|
||||
});
|
||||
|
||||
expect(await screen.findByTitle('Panel Added')).toBeInTheDocument();
|
||||
expect(await screen.queryByText('Start your new dashboard by adding a visualization')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -283,11 +283,15 @@ describe('DashboardScene', () => {
|
||||
});
|
||||
|
||||
it('Should create and add a new panel to the dashboard', () => {
|
||||
scene.exitEditMode({ skipConfirm: true });
|
||||
expect(scene.state.isEditing).toBe(false);
|
||||
|
||||
scene.onCreateNewPanel();
|
||||
|
||||
const body = scene.state.body as SceneGridLayout;
|
||||
const gridItem = body.state.children[0] as DashboardGridItem;
|
||||
|
||||
expect(scene.state.isEditing).toBe(true);
|
||||
expect(body.state.children.length).toBe(6);
|
||||
expect(gridItem.state.body!.state.key).toBe('panel-7');
|
||||
});
|
||||
@ -298,6 +302,7 @@ describe('DashboardScene', () => {
|
||||
const body = scene.state.body as SceneGridLayout;
|
||||
const gridRow = body.state.children[0] as SceneGridRow;
|
||||
|
||||
expect(scene.state.isEditing).toBe(true);
|
||||
expect(body.state.children.length).toBe(4);
|
||||
expect(gridRow.state.key).toBe('panel-7');
|
||||
expect(gridRow.state.children[0].state.key).toBe('griditem-1');
|
||||
@ -509,11 +514,15 @@ describe('DashboardScene', () => {
|
||||
});
|
||||
|
||||
it('Should create a new add library panel widget', () => {
|
||||
scene.exitEditMode({ skipConfirm: true });
|
||||
expect(scene.state.isEditing).toBe(false);
|
||||
|
||||
scene.onCreateLibPanelWidget();
|
||||
|
||||
const body = scene.state.body as SceneGridLayout;
|
||||
const gridItem = body.state.children[0] as DashboardGridItem;
|
||||
|
||||
expect(scene.state.isEditing).toBe(true);
|
||||
expect(body.state.children.length).toBe(6);
|
||||
expect(gridItem.state.body!.state.key).toBe('panel-7');
|
||||
expect(gridItem.state.y).toBe(0);
|
||||
|
@ -740,6 +740,10 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
|
||||
throw new Error('Trying to add a panel in a layout that is not SceneGridLayout');
|
||||
}
|
||||
|
||||
if (!this.state.isEditing) {
|
||||
this.onEnterEditMode();
|
||||
}
|
||||
|
||||
const sceneGridLayout = this.state.body;
|
||||
|
||||
const panelId = dashboardSceneGraph.getNextPanelId(this);
|
||||
@ -767,6 +771,10 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> {
|
||||
}
|
||||
|
||||
public onCreateNewPanel(): number {
|
||||
if (!this.state.isEditing) {
|
||||
this.onEnterEditMode();
|
||||
}
|
||||
|
||||
const vizPanel = getDefaultVizPanel(this);
|
||||
|
||||
this.addPanel(vizPanel);
|
||||
|
@ -62,7 +62,10 @@ export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardS
|
||||
</div>
|
||||
)}
|
||||
<CustomScrollbar autoHeightMin={'100%'} className={styles.scrollbarContainer}>
|
||||
<div className={styles.canvasContent}>{isEmpty ? emptyState : withPanels}</div>
|
||||
<div className={styles.canvasContent}>
|
||||
<>{isEmpty && emptyState}</>
|
||||
{withPanels}
|
||||
</div>
|
||||
</CustomScrollbar>
|
||||
</div>
|
||||
)}
|
||||
|
@ -26,7 +26,6 @@ import {
|
||||
UserActionEvent,
|
||||
GroupByVariable,
|
||||
AdHocFiltersVariable,
|
||||
SceneFlexLayout,
|
||||
} from '@grafana/scenes';
|
||||
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
||||
import { trackDashboardLoaded } from 'app/features/dashboard/utils/tracking';
|
||||
@ -52,6 +51,7 @@ import { createPanelDataProvider } from '../utils/createPanelDataProvider';
|
||||
import { DashboardInteractions } from '../utils/interactions';
|
||||
import {
|
||||
getCurrentValueForOldIntervalModel,
|
||||
getDashboardSceneFor,
|
||||
getIntervalsFromQueryString,
|
||||
getVizPanelKeyForPanelId,
|
||||
} from '../utils/utils';
|
||||
@ -285,6 +285,7 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel)
|
||||
body: new SceneGridLayout({
|
||||
isLazy: true,
|
||||
children: createSceneObjectsForPanels(oldModel.panels),
|
||||
$behaviors: [trackIfEmpty],
|
||||
}),
|
||||
$timeRange: new SceneTimeRange({
|
||||
from: oldModel.time.from,
|
||||
@ -303,7 +304,6 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel)
|
||||
registerDashboardMacro,
|
||||
registerDashboardSceneTracking(oldModel),
|
||||
registerPanelInteractionsReporter,
|
||||
trackIfIsEmpty,
|
||||
new behaviors.LiveNowTimer(oldModel.liveNow),
|
||||
],
|
||||
$data: new DashboardDataLayerSet({ annotationLayers, alertStatesLayer }),
|
||||
@ -570,21 +570,6 @@ function registerPanelInteractionsReporter(scene: DashboardScene) {
|
||||
});
|
||||
}
|
||||
|
||||
export function trackIfIsEmpty(parent: DashboardScene) {
|
||||
updateIsEmpty(parent);
|
||||
|
||||
parent.state.body.subscribeToState(() => {
|
||||
updateIsEmpty(parent);
|
||||
});
|
||||
}
|
||||
|
||||
function updateIsEmpty(parent: DashboardScene) {
|
||||
const { body } = parent.state;
|
||||
if (body instanceof SceneFlexLayout || body instanceof SceneGridLayout) {
|
||||
parent.setState({ isEmpty: body.state.children.length === 0 });
|
||||
}
|
||||
}
|
||||
|
||||
const convertSnapshotData = (snapshotData: DataFrameDTO[]): DataFrameJSON[] => {
|
||||
return snapshotData.map((data) => {
|
||||
return {
|
||||
@ -619,3 +604,17 @@ export const convertOldSnapshotToScenesSnapshot = (panel: PanelModel) => {
|
||||
panel.snapshotData = [];
|
||||
}
|
||||
};
|
||||
|
||||
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();
|
||||
};
|
||||
}
|
||||
|
@ -7,7 +7,11 @@ import { config, locationService } from '@grafana/runtime';
|
||||
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 {
|
||||
onAddLibraryPanel as onAddLibraryPanelImpl,
|
||||
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 { useDispatch, useSelector } from 'app/types';
|
||||
@ -37,6 +41,15 @@ const DashboardEmpty = ({ dashboard, canCreate }: Props) => {
|
||||
DashboardInteractions.emptyDashboardButtonClicked({ item: 'add_visualization' });
|
||||
};
|
||||
|
||||
const onAddLibraryPanel = () => {
|
||||
DashboardInteractions.emptyDashboardButtonClicked({ item: 'import_from_library' });
|
||||
if (dashboard instanceof DashboardScene) {
|
||||
dashboard.onCreateLibPanelWidget();
|
||||
} else {
|
||||
onAddLibraryPanelImpl(dashboard);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack alignItems="center" justifyContent="center">
|
||||
<div className={styles.wrapper}>
|
||||
@ -110,14 +123,7 @@ const DashboardEmpty = ({ dashboard, canCreate }: Props) => {
|
||||
icon="plus"
|
||||
fill="outline"
|
||||
data-testid={selectors.pages.AddDashboard.itemButton('Add a panel from the panel library button')}
|
||||
onClick={() => {
|
||||
DashboardInteractions.emptyDashboardButtonClicked({ item: 'import_from_library' });
|
||||
if (dashboard instanceof DashboardScene) {
|
||||
dashboard.onCreateLibPanelWidget();
|
||||
} else {
|
||||
onAddLibraryPanel(dashboard);
|
||||
}
|
||||
}}
|
||||
onClick={onAddLibraryPanel}
|
||||
disabled={!canCreate}
|
||||
>
|
||||
<Trans i18nKey="dashboard.empty.add-library-panel-button">Add library panel</Trans>
|
||||
|
Loading…
Reference in New Issue
Block a user