diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.test.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.test.tsx index 40da4a158a8..27b924b5936 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.test.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.test.tsx @@ -25,10 +25,17 @@ import { DashboardDataDTO } from 'app/types/dashboard'; import { PanelInspectDrawer } from '../../inspect/PanelInspectDrawer'; import { PanelTimeRange, PanelTimeRangeState } from '../../scene/panel-timerange/PanelTimeRange'; +import { DashboardLayoutManager } from '../../scene/types/DashboardLayoutManager'; +import { transformSaveModelSchemaV2ToScene } from '../../serialization/transformSaveModelSchemaV2ToScene'; import { transformSaveModelToScene } from '../../serialization/transformSaveModelToScene'; import { findVizPanelByKey } from '../../utils/utils'; import { buildPanelEditScene } from '../PanelEditor'; -import { testDashboard, panelWithTransformations, panelWithQueriesOnly } from '../testfiles/testDashboard'; +import { + testDashboard, + panelWithTransformations, + panelWithQueriesOnly, + testDashboardV2, +} from '../testfiles/testDashboard'; import { PanelDataQueriesTab, PanelDataQueriesTabRendered } from './PanelDataQueriesTab'; @@ -824,6 +831,78 @@ describe('PanelDataQueriesTab', () => { expect(queriesTab.state.dsSettings?.uid).toBe('gdev-testdata'); }); }); + + describe('V2 schema behavior - panel datasource undefined but queries have datasource', () => { + it('should load datasource from first query for V2 panel with prometheus datasource', async () => { + // panel-1 has a query with prometheus datasource + const { queriesTab } = await setupV2Scene('panel-1'); + + // V2 panels have undefined panel-level datasource for non-mixed panels + expect(queriesTab.queryRunner.state.datasource).toBeUndefined(); + + // But the query has its own datasource + expect(queriesTab.queryRunner.state.queries[0].datasource).toEqual({ + type: 'grafana-prometheus-datasource', + uid: 'gdev-prometheus', + }); + + // Should load the datasource from the first query + expect(queriesTab.state.datasource?.uid).toBe('gdev-prometheus'); + expect(queriesTab.state.dsSettings?.uid).toBe('gdev-prometheus'); + }); + + it('should load datasource from first query for V2 panel with testdata datasource', async () => { + // panel-2 has a query with testdata datasource + const { queriesTab } = await setupV2Scene('panel-2'); + + // V2 panels have undefined panel-level datasource for non-mixed panels + expect(queriesTab.queryRunner.state.datasource).toBeUndefined(); + + // But the query has its own datasource + expect(queriesTab.queryRunner.state.queries[0].datasource).toEqual({ + type: 'grafana-testdata-datasource', + uid: 'gdev-testdata', + }); + + // Should load the datasource from the first query + expect(queriesTab.state.datasource?.uid).toBe('gdev-testdata'); + expect(queriesTab.state.dsSettings?.uid).toBe('gdev-testdata'); + }); + + it('should fall back to last used datasource when V2 query has no explicit datasource', async () => { + store.exists.mockReturnValue(true); + store.getObject.mockImplementation((key: string, def: unknown) => { + if (key === PANEL_EDIT_LAST_USED_DATASOURCE) { + return { + dashboardUid: 'v2-dashboard-uid', + datasourceUid: 'gdev-testdata', + }; + } + return def; + }); + + // panel-3 has a query with NO explicit datasource (datasource.name is undefined) + const { queriesTab } = await setupV2Scene('panel-3'); + + // V2 panel with no explicit datasource on query should fall back to last used + expect(queriesTab.state.datasource?.uid).toBe('gdev-testdata'); + expect(queriesTab.state.dsSettings?.uid).toBe('gdev-testdata'); + }); + + it('should use panel-level datasource when available (V1 behavior preserved)', async () => { + const { queriesTab } = await setupScene('panel-1'); + + // V1 panels have panel-level datasource set + expect(queriesTab.queryRunner.state.datasource).toEqual({ + uid: 'gdev-testdata', + type: 'grafana-testdata-datasource', + }); + + // Should use the panel-level datasource + expect(queriesTab.state.datasource?.uid).toBe('gdev-testdata'); + expect(queriesTab.state.dsSettings?.uid).toBe('gdev-testdata'); + }); + }); }); }); @@ -844,3 +923,24 @@ async function setupScene(panelId: string) { return { panel, scene: dashboard, queriesTab }; } + +// Setup V2 scene - uses transformSaveModelSchemaV2ToScene +async function setupV2Scene(panelKey: string) { + const dashboard = transformSaveModelSchemaV2ToScene(testDashboardV2); + + const vizPanels = (dashboard.state.body as DashboardLayoutManager).getVizPanels(); + const panel = vizPanels.find((p) => p.state.key === panelKey)!; + + const panelEditor = buildPanelEditScene(panel); + dashboard.setState({ editPanel: panelEditor }); + + deactivators.push(dashboard.activate()); + deactivators.push(panelEditor.activate()); + + const queriesTab = panelEditor.state.dataPane!.state.tabs[0] as PanelDataQueriesTab; + deactivators.push(queriesTab.activate()); + + await Promise.resolve(); + + return { panel, scene: dashboard, queriesTab }; +} diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.tsx index d70fbf9c9c4..8bfa97461d3 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.tsx @@ -86,6 +86,17 @@ export class PanelDataQueriesTab extends SceneObjectBase = { + kind: 'DashboardWithAccessInfo', + metadata: { + name: 'v2-dashboard-uid', + namespace: 'default', + labels: {}, + generation: 1, + resourceVersion: '1', + creationTimestamp: new Date().toISOString(), + }, + spec: { + title: 'V2 Test Dashboard', + description: 'Test dashboard for V2 schema', + tags: [], + cursorSync: 'Off', + liveNow: false, + editable: true, + preload: false, + links: [], + variables: [], + annotations: [], + timeSettings: { + from: 'now-6h', + to: 'now', + autoRefresh: '', + autoRefreshIntervals: ['5s', '10s', '30s', '1m', '5m', '15m', '30m', '1h', '2h', '1d'], + fiscalYearStartMonth: 0, + hideTimepicker: false, + timezone: '', + weekStart: undefined, + quickRanges: [], + }, + elements: { + 'panel-1': { + kind: 'Panel', + spec: { + id: 1, + title: 'Panel with Prometheus datasource', + description: '', + links: [], + data: { + kind: 'QueryGroup', + spec: { + queries: [ + { + kind: 'PanelQuery', + spec: { + refId: 'A', + hidden: false, + query: { + kind: 'DataQuery', + version: defaultDataQueryKind().version, + group: 'grafana-prometheus-datasource', + datasource: { + name: 'gdev-prometheus', + }, + spec: { + expr: 'up', + }, + }, + }, + }, + ], + transformations: [], + queryOptions: {}, + }, + }, + vizConfig: { + kind: 'VizConfig', + group: 'timeseries', + version: '1.0.0', + spec: { + options: {}, + fieldConfig: { + defaults: {}, + overrides: [], + }, + }, + }, + }, + }, + 'panel-2': { + kind: 'Panel', + spec: { + id: 2, + title: 'Panel with TestData datasource', + description: '', + links: [], + data: { + kind: 'QueryGroup', + spec: { + queries: [ + { + kind: 'PanelQuery', + spec: { + refId: 'A', + hidden: false, + query: { + kind: 'DataQuery', + version: defaultDataQueryKind().version, + group: 'grafana-testdata-datasource', + datasource: { + name: 'gdev-testdata', + }, + spec: { + scenarioId: 'random_walk', + }, + }, + }, + }, + ], + transformations: [], + queryOptions: {}, + }, + }, + vizConfig: { + kind: 'VizConfig', + group: 'timeseries', + version: '1.0.0', + spec: { + options: {}, + fieldConfig: { + defaults: {}, + overrides: [], + }, + }, + }, + }, + }, + 'panel-3': { + kind: 'Panel', + spec: { + id: 3, + title: 'Panel with no datasource on query', + description: '', + links: [], + data: { + kind: 'QueryGroup', + spec: { + queries: [ + { + kind: 'PanelQuery', + spec: { + refId: 'A', + hidden: false, + query: { + kind: 'DataQuery', + version: defaultDataQueryKind().version, + group: 'grafana-testdata-datasource', + // No datasource.name - simulates panel with no explicit datasource + spec: {}, + }, + }, + }, + ], + transformations: [], + queryOptions: {}, + }, + }, + vizConfig: { + kind: 'VizConfig', + group: 'timeseries', + version: '1.0.0', + spec: { + options: {}, + fieldConfig: { + defaults: {}, + overrides: [], + }, + }, + }, + }, + }, + }, + layout: { + kind: 'GridLayout', + spec: { + items: [ + { + kind: 'GridLayoutItem', + spec: { + x: 0, + y: 0, + width: 12, + height: 8, + element: { kind: 'ElementReference', name: 'panel-1' }, + }, + }, + { + kind: 'GridLayoutItem', + spec: { + x: 12, + y: 0, + width: 12, + height: 8, + element: { kind: 'ElementReference', name: 'panel-2' }, + }, + }, + { + kind: 'GridLayoutItem', + spec: { + x: 0, + y: 8, + width: 12, + height: 8, + element: { kind: 'ElementReference', name: 'panel-3' }, + }, + }, + ], + }, + }, + }, + access: { + url: '/d/v2-dashboard-uid', + slug: 'v2-test-dashboard', + }, + apiVersion: 'v2', +};