mirror of
https://github.com/grafana/grafana.git
synced 2025-02-10 23:55:47 -06:00
DashboardScene: Panel menu tracking, adding explore menu action and unit tests (#74867)
* DashboardScene: Panel menu updates, adding explore action * DashboardScene: Panel menu updates, adding explore action * Fix test * Update test
This commit is contained in:
parent
3fdf96d241
commit
ed3fb71f7b
@ -339,7 +339,7 @@ abstract class DataSourceApi<
|
||||
|
||||
getVersion?(optionalOptions?: any): Promise<string>;
|
||||
|
||||
interpolateVariablesInQueries?(queries: TQuery[], scopedVars: ScopedVars | {}): TQuery[];
|
||||
interpolateVariablesInQueries?(queries: TQuery[], scopedVars: ScopedVars): TQuery[];
|
||||
|
||||
/**
|
||||
* An annotation processor allows explicit control for how annotations are managed.
|
||||
|
@ -11,7 +11,6 @@ import { ShareModal } from 'app/features/dashboard/components/ShareModal';
|
||||
import { DashboardModel } from 'app/features/dashboard/state';
|
||||
|
||||
import { getTimeSrv } from '../../features/dashboard/services/TimeSrv';
|
||||
import { getDatasourceSrv } from '../../features/plugins/datasource_srv';
|
||||
import {
|
||||
RemovePanelEvent,
|
||||
ShiftTimeEvent,
|
||||
@ -261,8 +260,9 @@ export class KeybindingSrv {
|
||||
this.bindWithPanelId('p x', async (panelId) => {
|
||||
const panel = dashboard.getPanelById(panelId)!;
|
||||
const url = await getExploreUrl({
|
||||
panel,
|
||||
datasourceSrv: getDatasourceSrv(),
|
||||
queries: panel.targets,
|
||||
dsRef: panel.datasource,
|
||||
scopedVars: panel.scopedVars,
|
||||
timeRange: getTimeSrv().timeRange(),
|
||||
});
|
||||
|
||||
|
@ -71,31 +71,20 @@ describe('state functions', () => {
|
||||
|
||||
describe('getExploreUrl', () => {
|
||||
const args = {
|
||||
panel: {
|
||||
getSavedId: () => 1,
|
||||
targets: [
|
||||
{ refId: 'A', expr: 'query1', legendFormat: 'legendFormat1' },
|
||||
{ refId: 'B', expr: 'query2', datasource: { type: '__expr__', uid: '__expr__' } },
|
||||
],
|
||||
},
|
||||
datasourceSrv: {
|
||||
get() {
|
||||
return {
|
||||
getRef: jest.fn(),
|
||||
};
|
||||
},
|
||||
getDataSourceById: jest.fn(),
|
||||
queries: [
|
||||
{ refId: 'A', expr: 'query1', legendFormat: 'legendFormat1' },
|
||||
{ refId: 'B', expr: 'query2', datasource: { type: '__expr__', uid: '__expr__' } },
|
||||
],
|
||||
dsRef: {
|
||||
uid: 'ds1',
|
||||
},
|
||||
timeRange: { from: dateTime(), to: dateTime(), raw: { from: 'now-1h', to: 'now' } },
|
||||
} as unknown as GetExploreUrlArguments;
|
||||
it('should use raw range in explore url', async () => {
|
||||
expect(getExploreUrl(args).then((data) => expect(data).toMatch(/from%22:%22now-1h%22,%22to%22:%22now/g)));
|
||||
expect(await getExploreUrl(args)).toMatch(/from%22:%22now-1h%22,%22to%22:%22now/g);
|
||||
});
|
||||
it('should omit legendFormat in explore url', () => {
|
||||
expect(getExploreUrl(args).then((data) => expect(data).not.toMatch(/legendFormat1/g)));
|
||||
});
|
||||
it('should omit expression target in explore url', () => {
|
||||
expect(getExploreUrl(args).then((data) => expect(data).not.toMatch(/__expr__/g)));
|
||||
it('should omit expression target in explore url', async () => {
|
||||
expect(await getExploreUrl(args)).not.toMatch(/__expr__/g);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { nanoid } from '@reduxjs/toolkit';
|
||||
import { omit } from 'lodash';
|
||||
import { Unsubscribable } from 'rxjs';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
@ -17,15 +16,15 @@ import {
|
||||
LogsSortOrder,
|
||||
rangeUtil,
|
||||
RawTimeRange,
|
||||
ScopedVars,
|
||||
TimeRange,
|
||||
TimeZone,
|
||||
toURLRange,
|
||||
urlUtil,
|
||||
} from '@grafana/data';
|
||||
import { DataSourceSrv, getDataSourceSrv } from '@grafana/runtime';
|
||||
import { getDataSourceSrv } from '@grafana/runtime';
|
||||
import { RefreshPicker } from '@grafana/ui';
|
||||
import store from 'app/core/store';
|
||||
import { PanelModel } from 'app/features/dashboard/state';
|
||||
import { ExpressionDatasourceUID } from 'app/features/expressions/types';
|
||||
import { QueryOptions, QueryTransaction } from 'app/types/explore';
|
||||
|
||||
@ -47,10 +46,10 @@ export const setLastUsedDatasourceUID = (orgId: number, datasourceUID: string) =
|
||||
store.setObject(lastUsedDatasourceKeyForOrgId(orgId), datasourceUID);
|
||||
|
||||
export interface GetExploreUrlArguments {
|
||||
panel: PanelModel;
|
||||
/** Datasource service to query other datasources in case the panel datasource is mixed */
|
||||
datasourceSrv: DataSourceSrv;
|
||||
queries: DataQuery[];
|
||||
dsRef: DataSourceRef | null | undefined;
|
||||
timeRange: TimeRange;
|
||||
scopedVars: ScopedVars | undefined;
|
||||
}
|
||||
|
||||
export function generateExploreId() {
|
||||
@ -61,27 +60,23 @@ export function generateExploreId() {
|
||||
* Returns an Explore-URL that contains a panel's queries and the dashboard time range.
|
||||
*/
|
||||
export async function getExploreUrl(args: GetExploreUrlArguments): Promise<string | undefined> {
|
||||
const { panel, datasourceSrv, timeRange } = args;
|
||||
let exploreDatasource = await datasourceSrv.get(panel.datasource);
|
||||
const { queries, dsRef, timeRange, scopedVars } = args;
|
||||
let exploreDatasource = await getDataSourceSrv().get(dsRef);
|
||||
|
||||
/** In Explore, we don't have legend formatter and we don't want to keep
|
||||
* legend formatting as we can't change it
|
||||
*
|
||||
* We also don't have expressions, so filter those out
|
||||
/*
|
||||
* Explore does not support expressions so filter those out
|
||||
*/
|
||||
let exploreTargets: DataQuery[] = panel.targets
|
||||
.map((t) => omit(t, 'legendFormat'))
|
||||
.filter((t) => t.datasource?.uid !== ExpressionDatasourceUID);
|
||||
let exploreTargets: DataQuery[] = queries.filter((t) => t.datasource?.uid !== ExpressionDatasourceUID);
|
||||
|
||||
let url: string | undefined;
|
||||
|
||||
if (exploreDatasource) {
|
||||
let state: Partial<ExploreUrlState> = { range: toURLRange(timeRange.raw) };
|
||||
if (exploreDatasource.interpolateVariablesInQueries) {
|
||||
const scopedVars = panel.scopedVars || {};
|
||||
state = {
|
||||
...state,
|
||||
datasource: exploreDatasource.uid,
|
||||
queries: exploreDatasource.interpolateVariablesInQueries(exploreTargets, scopedVars),
|
||||
queries: exploreDatasource.interpolateVariablesInQueries(exploreTargets, scopedVars ?? {}),
|
||||
};
|
||||
} else {
|
||||
state = {
|
||||
|
@ -0,0 +1,98 @@
|
||||
import { getPanelPlugin } from '@grafana/data/test/__mocks__/pluginMocks';
|
||||
import { locationService } from '@grafana/runtime';
|
||||
import { SceneGridItem, SceneGridLayout, SceneQueryRunner, VizPanel, VizPanelMenu } from '@grafana/scenes';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
import { GetExploreUrlArguments } from 'app/core/utils/explore';
|
||||
|
||||
import { DashboardScene } from './DashboardScene';
|
||||
import { panelMenuBehavior } from './PanelMenuBehavior';
|
||||
|
||||
const mocks = {
|
||||
contextSrv: jest.mocked(contextSrv),
|
||||
getExploreUrl: jest.fn(),
|
||||
};
|
||||
|
||||
jest.mock('app/core/utils/explore', () => ({
|
||||
...jest.requireActual('app/core/utils/explore'),
|
||||
getExploreUrl: (options: GetExploreUrlArguments) => {
|
||||
return mocks.getExploreUrl(options);
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('app/core/services/context_srv');
|
||||
|
||||
describe('panelMenuBehavior', () => {
|
||||
beforeAll(() => {
|
||||
locationService.push('/scenes/dashboard/dash-1?from=now-5m&to=now');
|
||||
});
|
||||
|
||||
it('Given standard panel', async () => {
|
||||
const { menu, panel } = await buildTestScene({});
|
||||
|
||||
Object.assign(panel, 'getPlugin', () => getPanelPlugin({}));
|
||||
|
||||
mocks.contextSrv.hasAccessToExplore.mockReturnValue(true);
|
||||
mocks.getExploreUrl.mockReturnValue(Promise.resolve('/explore'));
|
||||
|
||||
menu.activate();
|
||||
|
||||
await new Promise((r) => setTimeout(r, 1));
|
||||
|
||||
expect(menu.state.items?.length).toBe(4);
|
||||
// verify view panel url keeps url params and adds viewPanel=<panel-key>
|
||||
expect(menu.state.items?.[0].href).toBe('/scenes/dashboard/dash-1?from=now-5m&to=now&viewPanel=panel-12');
|
||||
// verify edit url keeps url time range
|
||||
expect(menu.state.items?.[1].href).toBe('/scenes/dashboard/dash-1/panel-edit/12?from=now-5m&to=now');
|
||||
// verify explore url
|
||||
expect(menu.state.items?.[2].href).toBe('/explore');
|
||||
|
||||
// Verify explore url is called with correct arguments
|
||||
const getExploreArgs: GetExploreUrlArguments = mocks.getExploreUrl.mock.calls[0][0];
|
||||
expect(getExploreArgs.dsRef).toEqual({ uid: 'my-uid' });
|
||||
expect(getExploreArgs.queries).toEqual([{ query: 'buu', refId: 'A' }]);
|
||||
expect(getExploreArgs.scopedVars?.__sceneObject?.value).toBe(panel);
|
||||
|
||||
// verify inspect url keeps url params and adds inspect=<panel-key>
|
||||
expect(menu.state.items?.[3].href).toBe('/scenes/dashboard/dash-1?from=now-5m&to=now&inspect=panel-12');
|
||||
});
|
||||
});
|
||||
|
||||
interface SceneOptions {}
|
||||
|
||||
async function buildTestScene(options: SceneOptions) {
|
||||
const menu = new VizPanelMenu({
|
||||
$behaviors: [panelMenuBehavior],
|
||||
});
|
||||
|
||||
const panel = new VizPanel({
|
||||
title: 'Panel A',
|
||||
pluginId: 'table',
|
||||
key: 'panel-12',
|
||||
menu,
|
||||
$data: new SceneQueryRunner({
|
||||
datasource: { uid: 'my-uid' },
|
||||
queries: [{ query: 'buu', refId: 'A' }],
|
||||
}),
|
||||
});
|
||||
|
||||
const scene = new DashboardScene({
|
||||
title: 'hello',
|
||||
uid: 'dash-1',
|
||||
body: new SceneGridLayout({
|
||||
children: [
|
||||
new SceneGridItem({
|
||||
key: 'griditem-1',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 10,
|
||||
height: 12,
|
||||
body: panel,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
await new Promise((r) => setTimeout(r, 1));
|
||||
|
||||
return { scene, panel, menu };
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
import { locationUtil, PanelMenuItem } from '@grafana/data';
|
||||
import { locationService } from '@grafana/runtime';
|
||||
import { VizPanel, VizPanelMenu } from '@grafana/scenes';
|
||||
import { locationService, reportInteraction } from '@grafana/runtime';
|
||||
import { sceneGraph, VizPanel, VizPanelMenu } from '@grafana/scenes';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
import { t } from 'app/core/internationalization';
|
||||
import { getExploreUrl } from 'app/core/utils/explore';
|
||||
import { InspectTab } from 'app/features/inspector/types';
|
||||
|
||||
import { getDashboardUrl, getPanelIdForVizPanel } from '../utils/utils';
|
||||
import { getDashboardUrl, getPanelIdForVizPanel, getQueryRunnerFor } from '../utils/utils';
|
||||
|
||||
import { DashboardScene } from './DashboardScene';
|
||||
|
||||
@ -11,50 +14,68 @@ import { DashboardScene } from './DashboardScene';
|
||||
* Behavior is called when VizPanelMenu is activated (ie when it's opened).
|
||||
*/
|
||||
export function panelMenuBehavior(menu: VizPanelMenu) {
|
||||
// hm.. add another generic param to SceneObject to specify parent type?
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
const panel = menu.parent as VizPanel;
|
||||
const location = locationService.getLocation();
|
||||
const items: PanelMenuItem[] = [];
|
||||
const panelId = getPanelIdForVizPanel(panel);
|
||||
const dashboard = panel.getRoot();
|
||||
const asyncFunc = async () => {
|
||||
// hm.. add another generic param to SceneObject to specify parent type?
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
const panel = menu.parent as VizPanel;
|
||||
const location = locationService.getLocation();
|
||||
const items: PanelMenuItem[] = [];
|
||||
const panelId = getPanelIdForVizPanel(panel);
|
||||
const dashboard = panel.getRoot();
|
||||
const panelPlugin = panel.getPlugin();
|
||||
const queryRunner = getQueryRunnerFor(panel);
|
||||
|
||||
// TODO
|
||||
// Add tracking via reportInteraction (but preserve the fact that these are normal links)
|
||||
if (dashboard instanceof DashboardScene) {
|
||||
items.push({
|
||||
text: t('panel.header-menu.view', `View`),
|
||||
iconClassName: 'eye',
|
||||
shortcut: 'v',
|
||||
onClick: () => reportInteraction('dashboards_panelheader_menu', { item: 'view' }),
|
||||
href: locationUtil.getUrlForPartial(location, { viewPanel: panel.state.key }),
|
||||
});
|
||||
|
||||
// We could check isEditing here but I kind of think this should always be in the menu,
|
||||
// and going into panel edit should make the dashboard go into edit mode is it's not already
|
||||
items.push({
|
||||
text: t('panel.header-menu.edit', `Edit`),
|
||||
iconClassName: 'eye',
|
||||
shortcut: 'v',
|
||||
onClick: () => reportInteraction('dashboards_panelheader_menu', { item: 'edit' }),
|
||||
href: getDashboardUrl({
|
||||
uid: dashboard.state.uid,
|
||||
subPath: `/panel-edit/${panelId}`,
|
||||
currentQueryParams: location.search,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
if (contextSrv.hasAccessToExplore() && !panelPlugin?.meta.skipDataQuery && queryRunner) {
|
||||
const timeRange = sceneGraph.getTimeRange(panel);
|
||||
|
||||
items.push({
|
||||
text: t('panel.header-menu.explore', `Explore`),
|
||||
iconClassName: 'compass',
|
||||
shortcut: 'p x',
|
||||
onClick: () => reportInteraction('dashboards_panelheader_menu', { item: 'explore' }),
|
||||
href: await getExploreUrl({
|
||||
queries: queryRunner.state.queries,
|
||||
dsRef: queryRunner.state.datasource,
|
||||
timeRange: timeRange.state.value,
|
||||
scopedVars: { __sceneObject: { value: panel } },
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
if (dashboard instanceof DashboardScene) {
|
||||
items.push({
|
||||
text: t('panel.header-menu.view', `View`),
|
||||
iconClassName: 'eye',
|
||||
shortcut: 'v',
|
||||
href: getDashboardUrl({
|
||||
uid: dashboard.state.uid,
|
||||
currentQueryParams: location.search,
|
||||
updateQuery: { filter: null, new: 'A' },
|
||||
}),
|
||||
text: t('panel.header-menu.inspect', `Inspect`),
|
||||
iconClassName: 'info-circle',
|
||||
shortcut: 'i',
|
||||
onClick: () => reportInteraction('dashboards_panelheader_menu', { item: 'inspect', tab: InspectTab.Data }),
|
||||
href: locationUtil.getUrlForPartial(location, { inspect: panel.state.key }),
|
||||
});
|
||||
|
||||
// We could check isEditing here but I kind of think this should always be in the menu,
|
||||
// and going into panel edit should make the dashboard go into edit mode is it's not already
|
||||
items.push({
|
||||
text: t('panel.header-menu.edit', `Edit`),
|
||||
iconClassName: 'eye',
|
||||
shortcut: 'v',
|
||||
href: getDashboardUrl({
|
||||
uid: dashboard.state.uid,
|
||||
subPath: `/panel-edit/${panelId}`,
|
||||
currentQueryParams: location.search,
|
||||
updateQuery: { filter: null, new: 'A' },
|
||||
}),
|
||||
});
|
||||
}
|
||||
menu.setState({ items });
|
||||
};
|
||||
|
||||
items.push({
|
||||
text: t('panel.header-menu.inspect', `Inspect`),
|
||||
iconClassName: 'info-circle',
|
||||
shortcut: 'i',
|
||||
href: locationUtil.getUrlForPartial(location, { inspect: panel.state.key }),
|
||||
});
|
||||
|
||||
menu.setState({ items });
|
||||
asyncFunc();
|
||||
}
|
||||
|
@ -4,13 +4,7 @@ import {
|
||||
PluginExtensionPoints,
|
||||
type PluginExtensionPanelContext,
|
||||
} from '@grafana/data';
|
||||
import {
|
||||
AngularComponent,
|
||||
getDataSourceSrv,
|
||||
locationService,
|
||||
reportInteraction,
|
||||
getPluginLinkExtensions,
|
||||
} from '@grafana/runtime';
|
||||
import { AngularComponent, locationService, reportInteraction, getPluginLinkExtensions } from '@grafana/runtime';
|
||||
import { PanelCtrl } from 'app/angular/panel/panel_ctrl';
|
||||
import config from 'app/core/config';
|
||||
import { t } from 'app/core/internationalization';
|
||||
@ -112,7 +106,6 @@ export function getPanelMenu(
|
||||
event.ctrlKey || event.metaKey ? (url: string) => window.open(`${config.appSubUrl}${url}`) : undefined;
|
||||
store.dispatch(
|
||||
navigateToExplore(panel, {
|
||||
getDataSourceSrv,
|
||||
timeRange: getTimeSrv().timeRange(),
|
||||
getExploreUrl,
|
||||
openInNewWindow,
|
||||
|
@ -20,19 +20,17 @@ const getNavigateToExploreContext = async (openInNewWindow?: (url: string) => vo
|
||||
};
|
||||
const datasource = new MockDataSourceApi(panel.datasource!.uid!);
|
||||
const get = jest.fn().mockResolvedValue(datasource);
|
||||
const getDataSourceSrv = jest.fn().mockReturnValue({ get });
|
||||
const getExploreUrl = jest.fn().mockResolvedValue(url);
|
||||
const timeRange = { from: dateTime(), to: dateTime() };
|
||||
|
||||
const dispatchedActions = await thunkTester({})
|
||||
.givenThunk(navigateToExplore)
|
||||
.whenThunkIsDispatched(panel, { getDataSourceSrv, timeRange, getExploreUrl, openInNewWindow });
|
||||
.whenThunkIsDispatched(panel, { timeRange, getExploreUrl, openInNewWindow });
|
||||
|
||||
return {
|
||||
url,
|
||||
panel,
|
||||
get,
|
||||
getDataSourceSrv,
|
||||
timeRange,
|
||||
getExploreUrl,
|
||||
dispatchedActions,
|
||||
@ -47,20 +45,14 @@ describe('navigateToExplore', () => {
|
||||
expect(locationService.getLocation().pathname).toEqual(url);
|
||||
});
|
||||
|
||||
it('then getDataSourceSrv should have been once', async () => {
|
||||
const { getDataSourceSrv } = await getNavigateToExploreContext();
|
||||
|
||||
expect(getDataSourceSrv).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('then getExploreUrl should have been called with correct arguments', async () => {
|
||||
const { getExploreUrl, panel, getDataSourceSrv, timeRange } = await getNavigateToExploreContext();
|
||||
const { getExploreUrl, panel, timeRange } = await getNavigateToExploreContext();
|
||||
|
||||
expect(getExploreUrl).toHaveBeenCalledTimes(1);
|
||||
expect(getExploreUrl).toHaveBeenCalledWith({
|
||||
panel,
|
||||
datasourceSrv: getDataSourceSrv(),
|
||||
queries: panel.targets,
|
||||
timeRange,
|
||||
dsRef: panel.datasource,
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -73,22 +65,14 @@ describe('navigateToExplore', () => {
|
||||
expect(dispatchedActions).toEqual([]);
|
||||
});
|
||||
|
||||
it('then getDataSourceSrv should have been once', async () => {
|
||||
const { getDataSourceSrv } = await getNavigateToExploreContext(openInNewWindow);
|
||||
|
||||
expect(getDataSourceSrv).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('then getExploreUrl should have been called with correct arguments', async () => {
|
||||
const { getExploreUrl, panel, getDataSourceSrv, timeRange } = await getNavigateToExploreContext(
|
||||
openInNewWindow
|
||||
);
|
||||
const { getExploreUrl, panel, timeRange } = await getNavigateToExploreContext(openInNewWindow);
|
||||
|
||||
expect(getExploreUrl).toHaveBeenCalledTimes(1);
|
||||
expect(getExploreUrl).toHaveBeenCalledWith({
|
||||
panel,
|
||||
datasourceSrv: getDataSourceSrv(),
|
||||
queries: panel.targets,
|
||||
timeRange,
|
||||
dsRef: panel.datasource,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { createAction } from '@reduxjs/toolkit';
|
||||
import { AnyAction } from 'redux';
|
||||
|
||||
import { SplitOpenOptions, TimeRange } from '@grafana/data';
|
||||
import { DataSourceSrv, locationService } from '@grafana/runtime';
|
||||
import { locationService } from '@grafana/runtime';
|
||||
import { generateExploreId, GetExploreUrlArguments } from 'app/core/utils/explore';
|
||||
import { PanelModel } from 'app/features/dashboard/state';
|
||||
import { ExploreItemState, ExploreState } from 'app/types/explore';
|
||||
@ -105,7 +105,6 @@ const createNewSplitOpenPane = createAsyncThunk(
|
||||
);
|
||||
|
||||
export interface NavigateToExploreDependencies {
|
||||
getDataSourceSrv: () => DataSourceSrv;
|
||||
timeRange: TimeRange;
|
||||
getExploreUrl: (args: GetExploreUrlArguments) => Promise<string | undefined>;
|
||||
openInNewWindow?: (url: string) => void;
|
||||
@ -116,11 +115,12 @@ export const navigateToExplore = (
|
||||
dependencies: NavigateToExploreDependencies
|
||||
): ThunkResult<void> => {
|
||||
return async (dispatch) => {
|
||||
const { getDataSourceSrv, timeRange, getExploreUrl, openInNewWindow } = dependencies;
|
||||
const datasourceSrv = getDataSourceSrv();
|
||||
const { timeRange, getExploreUrl, openInNewWindow } = dependencies;
|
||||
|
||||
const path = await getExploreUrl({
|
||||
panel,
|
||||
datasourceSrv,
|
||||
queries: panel.targets,
|
||||
dsRef: panel.datasource,
|
||||
scopedVars: panel.scopedVars,
|
||||
timeRange,
|
||||
});
|
||||
|
||||
|
@ -162,6 +162,11 @@ export class TestDataDataSource extends DataSourceWithBackend<TestData> {
|
||||
}
|
||||
}
|
||||
|
||||
applyTemplateVariables(query: TestData, scopedVars: ScopedVars): TestData {
|
||||
this.resolveTemplateVariables(query, scopedVars);
|
||||
return query;
|
||||
}
|
||||
|
||||
annotationDataTopicTest(target: TestData, req: DataQueryRequest<TestData>): Observable<DataQueryResponse> {
|
||||
const events = this.buildFakeAnnotationEvents(req.range, target.lines ?? 10);
|
||||
const dataFrame = new ArrayDataFrame(events);
|
||||
|
Loading…
Reference in New Issue
Block a user