Compare commits

...
6 changed files with 90 additions and 67 deletions
@@ -3,7 +3,7 @@ import { test, expect } from '@grafana/plugin-e2e';
test.use({
featureToggles: {
newVizSuggestions: true,
externalVizSuggestions: false,
externalVizSuggestions: true,
},
viewport: {
width: 800,
@@ -174,5 +174,40 @@ test.describe(
'time series to be rendered inside the panel'
).toBeVisible();
});
test('should not apply suggestion if you navigate back to the dashboard for a new panel', async ({
page,
selectors,
gotoDashboardPage,
}) => {
// New dashboard
const dashboardPage = await gotoDashboardPage({});
// Press the empty-state Create new panel button
await dashboardPage
.getByGrafanaSelector(selectors.pages.AddDashboard.itemButton('Create new panel button'))
.click();
await expect(dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.General.content)).toBeVisible();
// Verify we see suggestions on load (after closing the data source picker)
await page.getByRole('button', { name: 'Close', exact: true }).click({ force: true });
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.VisualizationPreview.card('Line chart')),
'line chart suggestion to be rendered'
).toBeVisible();
// Select a visualization
await dashboardPage.getByGrafanaSelector(selectors.components.VisualizationPreview.card('Table')).click();
await expect(page.getByRole('grid').getByRole('row').first(), 'table row to be rendered').toBeVisible();
// Verify that navigating back to the dashboard cancels the suggestion and restores the line chart.
await dashboardPage
.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton)
.click();
await expect(
page.locator('[data-viz-panel-key="panel-1"]').locator('.uplot'),
'time series to be rendered inside the panel'
).toBeVisible();
});
}
);
@@ -27,7 +27,7 @@ export const AddToDashboardFormExposedComponent = (props: Partial<Props<Absolute
buildPanel={
props.buildPanel ??
(() => ({
type: 'timeseries',
type: 'timeseries', // TODO: should this be `getDefaultPluginId()`?
title: t('dashboard-scene.add-to-dashboard-form-exposed.title.new-panel', 'New panel'),
targets: [],
}))
@@ -13,7 +13,6 @@ import {
SceneObjectState,
SceneObjectStateChangedEvent,
SceneQueryRunner,
sceneGraph,
sceneUtils,
VizPanel,
} from '@grafana/scenes';
@@ -21,8 +20,6 @@ import { Panel } from '@grafana/schema';
import { OptionFilter } from 'app/features/dashboard/components/PanelEditor/OptionsPaneOptions';
import { getLastUsedDatasourceFromStorage } from 'app/features/dashboard/utils/dashboard';
import { saveLibPanel } from 'app/features/library-panels/state/api';
import { getAllSuggestions } from 'app/features/panel/suggestions/getAllSuggestions';
import { hasData } from 'app/features/panel/suggestions/utils';
import { DashboardEditActionEvent } from '../edit-pane/shared';
import { DashboardSceneChangeTracker } from '../saving/DashboardSceneChangeTracker';
@@ -34,6 +31,7 @@ import { vizPanelToPanel } from '../serialization/transformSceneToSaveModel';
import {
activateSceneObjectAndParentTree,
getDashboardSceneFor,
getDefaultVizPanel,
getLibraryPanelBehavior,
getPanelIdForVizPanel,
} from '../utils/utils';
@@ -91,12 +89,9 @@ export class PanelEditor extends SceneObjectBase<PanelEditorState> {
// Need to clear selection here since selection is activated when panel edit mode is entered through the panel actions menu. This causes sidebar panel editor to be open when exiting panel edit mode
dashboard.state.editPane.clearSelection();
if (panel.state.pluginId === UNCONFIGURED_PANEL_PLUGIN_ID) {
if (config.featureToggles.newVizSuggestions) {
this._autoSelectVisualization(panel);
} else {
panel.changePluginType('timeseries');
}
// this will be deleted when suggestions is fully rolled out.
if (panel.state.pluginId === UNCONFIGURED_PANEL_PLUGIN_ID && !config.featureToggles.newVizSuggestions) {
panel.changePluginType('timeseries');
}
this._subs.add(
@@ -119,28 +114,6 @@ export class PanelEditor extends SceneObjectBase<PanelEditorState> {
};
}
private async _autoSelectVisualization(panel: VizPanel) {
const dataObject = sceneGraph.getData(panel);
this._subs.add(
dataObject.subscribeToState(async () => {
const { data } = dataObject.state;
if (hasData(data) && panel.state.pluginId === UNCONFIGURED_PANEL_PLUGIN_ID) {
const { suggestions } = await getAllSuggestions(data);
if (suggestions.length > 0) {
const defaultFirstSuggestion = suggestions[0];
await panel.changePluginType(
defaultFirstSuggestion.pluginId,
defaultFirstSuggestion.options,
defaultFirstSuggestion.fieldConfig
);
}
}
})
);
}
private commitChanges() {
if (!this.state.isDirty && !this._changesHaveBeenMade) {
// Nothing to commit
@@ -152,7 +125,6 @@ export class PanelEditor extends SceneObjectBase<PanelEditorState> {
const originalState = this._layoutItemState!;
this.setState({ editPreview: undefined });
this.state.optionsPane?.setState({ editPreviewRef: undefined });
// Temp fix for old edit mode
if (this._layoutItem instanceof DashboardGridItem && !config.featureToggles.dashboardNewLayouts) {
@@ -242,7 +214,6 @@ export class PanelEditor extends SceneObjectBase<PanelEditorState> {
// First time initialization
if (this.state.isInitializing) {
this.setOriginalState(this.state.panelRef);
this._setupChangeDetection();
this._updateDataPane(plugin);
@@ -255,17 +226,13 @@ export class PanelEditor extends SceneObjectBase<PanelEditorState> {
})
);
const isUnconfigured = Boolean(
config.featureToggles.newVizSuggestions && panel.state.pluginId === UNCONFIGURED_PANEL_PLUGIN_ID
);
// Setup options pane
const optionsPane = new PanelOptionsPane({
panelRef: this.state.panelRef,
editPreviewRef: this.state.editPreview?.getRef(),
searchQuery: '',
listMode: OptionFilter.All,
isVizPickerOpen: isUnconfigured,
isVizPickerOpen: this.state.isNewPanel,
isNewPanel: this.state.isNewPanel,
});
@@ -274,23 +241,23 @@ export class PanelEditor extends SceneObjectBase<PanelEditorState> {
isInitializing: false,
});
this._subs.add(
this.subscribeToState((newState, oldState) => {
if (newState.editPreview !== oldState.editPreview) {
optionsPane.setState({ editPreviewRef: newState.editPreview?.getRef() });
}
})
);
this._subs.add(
optionsPane.subscribeToState((newState, oldState) => {
if (newState.isVizPickerOpen !== oldState.isVizPickerOpen) {
const panel = this.state.panelRef.resolve();
let editPreview: VizPanel | undefined;
if (newState.isVizPickerOpen) {
// we just "pick" timeseries, viz type will likely be overridden by Suggestions.
const editPreviewBuilder = PanelBuilders.timeseries()
.setTitle(panel.state.title)
.setDescription(panel.state.description);
if (panel.state.$data) {
editPreviewBuilder.setData(new DataProviderSharer({ source: panel.state.$data.getRef() }));
}
editPreview = editPreviewBuilder.build();
const panel = this.state.panelRef.resolve();
const editPreview = PanelEditor.buildEditPreview(panel);
this.setState({ editPreview });
} else {
this.setState({ editPreview: undefined });
}
this.setState({ editPreview });
optionsPane.setState({ editPreviewRef: editPreview?.getRef() });
}
})
);
@@ -442,6 +409,16 @@ export class PanelEditor extends SceneObjectBase<PanelEditorState> {
.build(),
});
};
public static buildEditPreview(panel: VizPanel): VizPanel {
const editPreview = getDefaultVizPanel();
editPreview.setState({
title: panel.state.title,
description: panel.state.description,
$data: panel.state.$data ? new DataProviderSharer({ source: panel.state.$data.getRef() }) : undefined,
});
return editPreview;
}
}
export function buildPanelEditScene(panel: VizPanel, isNewPanel = false): PanelEditor {
@@ -449,5 +426,6 @@ export function buildPanelEditScene(panel: VizPanel, isNewPanel = false): PanelE
isInitializing: true,
panelRef: panel.getRef(),
isNewPanel,
editPreview: isNewPanel ? PanelEditor.buildEditPreview(panel) : undefined,
});
}
@@ -8,6 +8,7 @@ import { findEditPanel, getLibraryPanelBehavior } from '../utils/utils';
import { DashboardScene, DashboardSceneState } from './DashboardScene';
import { LibraryPanelBehavior } from './LibraryPanelBehavior';
import { UNCONFIGURED_PANEL_PLUGIN_ID } from './UnconfiguredPanel';
import { DefaultGridLayoutManager } from './layout-default/DefaultGridLayoutManager';
export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler {
@@ -91,7 +92,7 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler {
return;
}
update.editPanel = buildPanelEditScene(panel);
update.editPanel = buildPanelEditScene(panel, panel.state.pluginId === UNCONFIGURED_PANEL_PLUGIN_ID);
} else if (editPanel && values.editPanel === null) {
update.editPanel = undefined;
}
@@ -126,7 +127,9 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler {
private _waitForLibPanelToLoadBeforeEnteringPanelEdit(panel: VizPanel, libPanel: LibraryPanelBehavior) {
const sub = libPanel.subscribeToState((state) => {
if (state.isLoaded) {
this._scene.setState({ editPanel: buildPanelEditScene(panel) });
this._scene.setState({
editPanel: buildPanelEditScene(panel, panel.state.pluginId === UNCONFIGURED_PANEL_PLUGIN_ID),
});
sub.unsubscribe();
}
});
@@ -482,7 +482,7 @@ export function buildGridItemForPanel(panel: PanelModel): DashboardGridItem {
key: getVizPanelKeyForPanelId(panel.id),
title: panel.title?.substring(0, 5000),
description: panel.description,
pluginId: panel.type ?? 'timeseries',
pluginId: panel.type ?? 'timeseries', // TODO: should this be `getDefaultPluginId()`?
options: panel.options ?? {},
fieldConfig: panel.fieldConfig,
pluginVersion: panel.pluginVersion,
@@ -279,17 +279,22 @@ export function getClosestVizPanel(sceneObject: SceneObject): VizPanel | null {
return null;
}
export function getDefaultPluginId(): string {
return config.featureToggles.dashboardNewLayouts || config.featureToggles.newVizSuggestions
? UNCONFIGURED_PANEL_PLUGIN_ID
: 'timeseries';
}
export function getDefaultVizPanel(): VizPanel {
const defaultPluginId =
config.featureToggles.dashboardNewLayouts || config.featureToggles.newVizSuggestions
? UNCONFIGURED_PANEL_PLUGIN_ID
: 'timeseries';
const defaultPluginId = getDefaultPluginId();
const newPanelTitle =
config.featureToggles.newVizSuggestions && defaultPluginId === UNCONFIGURED_PANEL_PLUGIN_ID
? ''
: t('dashboard.new-panel-title', 'New panel');
const datasourceSettings = getDataSourceSrv().getInstanceSettings(null);
return new VizPanel({
title: newPanelTitle,
pluginId: defaultPluginId,
@@ -307,14 +312,16 @@ export function getDefaultVizPanel(): VizPanel {
headerActions: new VizPanelHeaderActions({
hideGroupByAction: !config.featureToggles.panelGroupBy,
}),
$data: new SceneDataTransformer({
$data: new SceneQueryRunner({
queries: [{ refId: 'A' }],
datasource: getDataSourceRef(getDataSourceSrv().getInstanceSettings(null)!),
$behaviors: [new DashboardDatasourceBehaviour({})],
}),
transformations: [],
}),
$data: datasourceSettings
? new SceneDataTransformer({
$data: new SceneQueryRunner({
queries: [{ refId: 'A' }],
datasource: getDataSourceRef(datasourceSettings),
$behaviors: [new DashboardDatasourceBehaviour({})],
}),
transformations: [],
})
: undefined,
});
}