mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
ReactPanels: Adds Explore menu item (#20236)
* Fix: Adds Explore menuitem to React Panels Fixes #19865 * Refactor: Adds CMD|CTRL+click to Explore menu item
This commit is contained in:
parent
a499586f15
commit
e66fc3d47a
@ -1,11 +1,11 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import { ComponentType } from 'react';
|
||||
import { PluginMeta, GrafanaPlugin } from './plugin';
|
||||
import { GrafanaPlugin, PluginMeta } from './plugin';
|
||||
import { PanelData } from './panel';
|
||||
import { LogRowModel } from './logs';
|
||||
import { AnnotationEvent, TimeSeries, TableData, LoadingState, KeyValue } from './data';
|
||||
import { AnnotationEvent, KeyValue, LoadingState, TableData, TimeSeries } from './data';
|
||||
import { DataFrame, DataFrameDTO } from './dataFrame';
|
||||
import { TimeRange, RawTimeRange } from './time';
|
||||
import { RawTimeRange, TimeRange } from './time';
|
||||
import { ScopedVars } from './ScopedVars';
|
||||
|
||||
export interface DataSourcePluginOptionsEditorProps<JSONData = DataSourceJsonData, SecureJSONData = {}> {
|
||||
@ -253,6 +253,8 @@ export abstract class DataSourceApi<
|
||||
* in the annotation editor `annotations` capability also needs to be enabled in plugin.json.
|
||||
*/
|
||||
annotationQuery?(options: AnnotationQueryRequest<TQuery>): Promise<AnnotationEvent[]>;
|
||||
|
||||
interpolateVariablesInQueries?(queries: TQuery[]): TQuery[];
|
||||
}
|
||||
|
||||
export interface QueryEditorProps<
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { ComponentClass, ComponentType } from 'react';
|
||||
import { DataQueryRequest, DataQueryError } from './datasource';
|
||||
import { PluginMeta, GrafanaPlugin } from './plugin';
|
||||
import { DataQueryError, DataQueryRequest } from './datasource';
|
||||
import { GrafanaPlugin, PluginMeta } from './plugin';
|
||||
import { ScopedVars } from './ScopedVars';
|
||||
import { LoadingState } from './data';
|
||||
import { DataFrame } from './dataFrame';
|
||||
import { TimeRange, TimeZone, AbsoluteTimeRange } from './time';
|
||||
import { AbsoluteTimeRange, TimeRange, TimeZone } from './time';
|
||||
|
||||
export type InterpolateFunction = (value: string, scopedVars?: ScopedVars, format?: string | Function) => string;
|
||||
|
||||
@ -125,7 +125,7 @@ export interface PanelMenuItem {
|
||||
type?: 'submenu' | 'divider';
|
||||
text?: string;
|
||||
iconClassName?: string;
|
||||
onClick?: () => void;
|
||||
onClick?: (event: React.MouseEvent<any>) => void;
|
||||
shortcut?: string;
|
||||
subMenu?: PanelMenuItem[];
|
||||
}
|
||||
|
@ -5,13 +5,13 @@ import appEvents from 'app/core/app_events';
|
||||
import { getExploreUrl } from 'app/core/utils/explore';
|
||||
import locationUtil from 'app/core/utils/location_util';
|
||||
import { store } from 'app/store/store';
|
||||
import { CoreEvents, AppEventEmitter } from 'app/types';
|
||||
import { AppEventEmitter, CoreEvents } from 'app/types';
|
||||
|
||||
import Mousetrap from 'mousetrap';
|
||||
import { PanelEvents } from '@grafana/data';
|
||||
import 'mousetrap-global-bind';
|
||||
import { ContextSrv } from './context_srv';
|
||||
import { ILocationService, ITimeoutService, IRootScopeService } from 'angular';
|
||||
import { ILocationService, IRootScopeService, ITimeoutService } from 'angular';
|
||||
import { GrafanaRootScope } from 'app/routes/GrafanaCtrl';
|
||||
import { getLocationSrv } from '@grafana/runtime';
|
||||
|
||||
@ -224,7 +224,13 @@ export class KeybindingSrv {
|
||||
if (dashboard.meta.focusPanelId) {
|
||||
const panel = dashboard.getPanelById(dashboard.meta.focusPanelId);
|
||||
const datasource = await this.datasourceSrv.get(panel.datasource);
|
||||
const url = await getExploreUrl(panel, panel.targets, datasource, this.datasourceSrv, this.timeSrv);
|
||||
const url = await getExploreUrl({
|
||||
panel,
|
||||
panelTargets: panel.targets,
|
||||
panelDatasource: datasource,
|
||||
datasourceSrv: this.datasourceSrv,
|
||||
timeSrv: this.timeSrv,
|
||||
});
|
||||
const urlWithoutBase = locationUtil.stripBaseFromUrl(url);
|
||||
|
||||
if (urlWithoutBase) {
|
||||
|
@ -3,34 +3,34 @@ import _ from 'lodash';
|
||||
import { Unsubscribable } from 'rxjs';
|
||||
// Services & Utils
|
||||
import {
|
||||
dateMath,
|
||||
toUtc,
|
||||
TimeRange,
|
||||
RawTimeRange,
|
||||
TimeZone,
|
||||
TimeFragment,
|
||||
LogRowModel,
|
||||
LogsModel,
|
||||
LogsDedupStrategy,
|
||||
IntervalValues,
|
||||
DefaultTimeZone,
|
||||
DataQuery,
|
||||
DataSourceApi,
|
||||
DataQueryError,
|
||||
DataQueryRequest,
|
||||
PanelModel,
|
||||
DataSourceApi,
|
||||
dateMath,
|
||||
DefaultTimeZone,
|
||||
HistoryItem,
|
||||
IntervalValues,
|
||||
LogRowModel,
|
||||
LogsDedupStrategy,
|
||||
LogsModel,
|
||||
PanelModel,
|
||||
RawTimeRange,
|
||||
TimeFragment,
|
||||
TimeRange,
|
||||
TimeZone,
|
||||
toUtc,
|
||||
} from '@grafana/data';
|
||||
import { renderUrl } from 'app/core/utils/url';
|
||||
import store from 'app/core/store';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import { getNextRefIdChar } from './query';
|
||||
|
||||
// Types
|
||||
import { RefreshPicker } from '@grafana/ui';
|
||||
import { ExploreUrlState, QueryTransaction, QueryOptions, ExploreMode } from 'app/types/explore';
|
||||
import { ExploreMode, ExploreUrlState, QueryOptions, QueryTransaction } from 'app/types/explore';
|
||||
import { config } from '../config';
|
||||
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
import { DataSourceSrv } from '@grafana/runtime';
|
||||
|
||||
export const DEFAULT_RANGE = {
|
||||
from: 'now-1h',
|
||||
@ -57,13 +57,15 @@ export const lastUsedDatasourceKeyForOrgId = (orgId: number) => `${LAST_USED_DAT
|
||||
* @param datasourceSrv Datasource service to query other datasources in case the panel datasource is mixed
|
||||
* @param timeSrv Time service to get the current dashboard range from
|
||||
*/
|
||||
export async function getExploreUrl(
|
||||
panel: PanelModel,
|
||||
panelTargets: DataQuery[],
|
||||
panelDatasource: any,
|
||||
datasourceSrv: any,
|
||||
timeSrv: TimeSrv
|
||||
) {
|
||||
export interface GetExploreUrlArguments {
|
||||
panel: PanelModel;
|
||||
panelTargets: DataQuery[];
|
||||
panelDatasource: DataSourceApi;
|
||||
datasourceSrv: DataSourceSrv;
|
||||
timeSrv: TimeSrv;
|
||||
}
|
||||
export async function getExploreUrl(args: GetExploreUrlArguments) {
|
||||
const { panel, panelTargets, panelDatasource, datasourceSrv, timeSrv } = args;
|
||||
let exploreDatasource = panelDatasource;
|
||||
let exploreTargets: DataQuery[] = panelTargets;
|
||||
let url: string;
|
||||
|
@ -1,15 +1,20 @@
|
||||
import { updateLocation } from 'app/core/actions';
|
||||
import { store } from 'app/store/store';
|
||||
import config from 'app/core/config';
|
||||
|
||||
import { removePanel, duplicatePanel, copyPanel, editPanelJson, sharePanel } from 'app/features/dashboard/utils/panel';
|
||||
import { PanelModel } from 'app/features/dashboard/state/PanelModel';
|
||||
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
|
||||
import { getLocationSrv } from '@grafana/runtime';
|
||||
import { getDataSourceSrv, getLocationSrv } from '@grafana/runtime';
|
||||
import { PanelMenuItem } from '@grafana/data';
|
||||
|
||||
import { copyPanel, duplicatePanel, editPanelJson, removePanel, sharePanel } from 'app/features/dashboard/utils/panel';
|
||||
import { PanelModel } from 'app/features/dashboard/state/PanelModel';
|
||||
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
|
||||
import { contextSrv } from '../../../core/services/context_srv';
|
||||
import { navigateToExplore } from '../../explore/state/actions';
|
||||
import { getExploreUrl } from '../../../core/utils/explore';
|
||||
import { getTimeSrv } from '../services/TimeSrv';
|
||||
|
||||
export const getPanelMenu = (dashboard: DashboardModel, panel: PanelModel) => {
|
||||
const onViewPanel = () => {
|
||||
const onViewPanel = (event: React.MouseEvent<any>) => {
|
||||
event.preventDefault();
|
||||
store.dispatch(
|
||||
updateLocation({
|
||||
query: {
|
||||
@ -22,7 +27,8 @@ export const getPanelMenu = (dashboard: DashboardModel, panel: PanelModel) => {
|
||||
);
|
||||
};
|
||||
|
||||
const onEditPanel = () => {
|
||||
const onEditPanel = (event: React.MouseEvent<any>) => {
|
||||
event.preventDefault();
|
||||
store.dispatch(
|
||||
updateLocation({
|
||||
query: {
|
||||
@ -35,11 +41,13 @@ export const getPanelMenu = (dashboard: DashboardModel, panel: PanelModel) => {
|
||||
);
|
||||
};
|
||||
|
||||
const onSharePanel = () => {
|
||||
const onSharePanel = (event: React.MouseEvent<any>) => {
|
||||
event.preventDefault();
|
||||
sharePanel(dashboard, panel);
|
||||
};
|
||||
|
||||
const onInspectPanel = () => {
|
||||
const onInspectPanel = (event: React.MouseEvent<any>) => {
|
||||
event.preventDefault();
|
||||
getLocationSrv().update({
|
||||
partial: true,
|
||||
query: {
|
||||
@ -48,22 +56,36 @@ export const getPanelMenu = (dashboard: DashboardModel, panel: PanelModel) => {
|
||||
});
|
||||
};
|
||||
|
||||
const onDuplicatePanel = () => {
|
||||
const onMore = (event: React.MouseEvent<any>) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
const onDuplicatePanel = (event: React.MouseEvent<any>) => {
|
||||
event.preventDefault();
|
||||
duplicatePanel(dashboard, panel);
|
||||
};
|
||||
|
||||
const onCopyPanel = () => {
|
||||
const onCopyPanel = (event: React.MouseEvent<any>) => {
|
||||
event.preventDefault();
|
||||
copyPanel(panel);
|
||||
};
|
||||
|
||||
const onEditPanelJson = () => {
|
||||
const onEditPanelJson = (event: React.MouseEvent<any>) => {
|
||||
event.preventDefault();
|
||||
editPanelJson(dashboard, panel);
|
||||
};
|
||||
|
||||
const onRemovePanel = () => {
|
||||
const onRemovePanel = (event: React.MouseEvent<any>) => {
|
||||
event.preventDefault();
|
||||
removePanel(dashboard, panel, true);
|
||||
};
|
||||
|
||||
const onNavigateToExplore = (event: React.MouseEvent<any>) => {
|
||||
event.preventDefault();
|
||||
const openInNewWindow = event.ctrlKey || event.metaKey ? (url: string) => window.open(url) : undefined;
|
||||
store.dispatch(navigateToExplore(panel, { getDataSourceSrv, getTimeSrv, getExploreUrl, openInNewWindow }));
|
||||
};
|
||||
|
||||
const menu: PanelMenuItem[] = [];
|
||||
|
||||
menu.push({
|
||||
@ -89,6 +111,14 @@ export const getPanelMenu = (dashboard: DashboardModel, panel: PanelModel) => {
|
||||
shortcut: 'p s',
|
||||
});
|
||||
|
||||
if (contextSrv.hasAccessToExplore() && panel.datasource) {
|
||||
menu.push({
|
||||
text: 'Explore',
|
||||
iconClassName: 'gicon gicon-explore',
|
||||
shortcut: 'x',
|
||||
onClick: onNavigateToExplore,
|
||||
});
|
||||
}
|
||||
if (config.featureToggles.inspect) {
|
||||
menu.push({
|
||||
text: 'Inspect',
|
||||
@ -123,6 +153,7 @@ export const getPanelMenu = (dashboard: DashboardModel, panel: PanelModel) => {
|
||||
text: 'More...',
|
||||
iconClassName: 'fa fa-fw fa-cube',
|
||||
subMenu: subMenu,
|
||||
onClick: onMore,
|
||||
});
|
||||
|
||||
if (dashboard.meta.canEdit) {
|
||||
|
@ -1,18 +1,21 @@
|
||||
import { refreshExplore, loadDatasource } from './actions';
|
||||
import { ExploreId, ExploreUrlState, ExploreUpdateState, ExploreMode } from 'app/types';
|
||||
import { loadDatasource, navigateToExplore, refreshExplore } from './actions';
|
||||
import { ExploreId, ExploreMode, ExploreUpdateState, ExploreUrlState } from 'app/types';
|
||||
import { thunkTester } from 'test/core/thunk/thunkTester';
|
||||
import {
|
||||
initializeExploreAction,
|
||||
InitializeExplorePayload,
|
||||
updateUIStateAction,
|
||||
setQueriesAction,
|
||||
loadDatasourcePendingAction,
|
||||
loadDatasourceReadyAction,
|
||||
setQueriesAction,
|
||||
updateUIStateAction,
|
||||
} from './actionTypes';
|
||||
import { Emitter } from 'app/core/core';
|
||||
import { ActionOf } from 'app/core/redux/actionCreatorFactory';
|
||||
import { makeInitialUpdateState } from './reducers';
|
||||
import { DataQuery, DefaultTimeZone, RawTimeRange, LogsDedupStrategy, toUtc } from '@grafana/data';
|
||||
import { DataQuery, DefaultTimeZone, LogsDedupStrategy, RawTimeRange, toUtc } from '@grafana/data';
|
||||
import { PanelModel } from 'app/features/dashboard/state';
|
||||
import { updateLocation } from '../../../core/actions';
|
||||
import { MockDataSourceApi } from '../../../../test/mocks/datasource_srv';
|
||||
|
||||
jest.mock('app/features/plugins/datasource_srv', () => ({
|
||||
getDatasourceSrv: () => ({
|
||||
@ -218,3 +221,129 @@ describe('loading datasource', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const getNavigateToExploreContext = async (openInNewWindow: (url: string) => void = undefined) => {
|
||||
const url = 'http://www.someurl.com';
|
||||
const panel: Partial<PanelModel> = {
|
||||
datasource: 'mocked datasource',
|
||||
targets: [{ refId: 'A' }],
|
||||
};
|
||||
const datasource = new MockDataSourceApi(panel.datasource);
|
||||
const get = jest.fn().mockResolvedValue(datasource);
|
||||
const getDataSourceSrv = jest.fn().mockReturnValue({ get });
|
||||
const getTimeSrv = jest.fn();
|
||||
const getExploreUrl = jest.fn().mockResolvedValue(url);
|
||||
|
||||
const dispatchedActions = await thunkTester({})
|
||||
.givenThunk(navigateToExplore)
|
||||
.whenThunkIsDispatched(panel, { getDataSourceSrv, getTimeSrv, getExploreUrl, openInNewWindow });
|
||||
|
||||
return {
|
||||
url,
|
||||
panel,
|
||||
datasource,
|
||||
get,
|
||||
getDataSourceSrv,
|
||||
getTimeSrv,
|
||||
getExploreUrl,
|
||||
dispatchedActions,
|
||||
};
|
||||
};
|
||||
|
||||
describe('navigateToExplore', () => {
|
||||
describe('when navigateToExplore thunk is dispatched', () => {
|
||||
describe('and openInNewWindow is undefined', () => {
|
||||
const openInNewWindow: (url: string) => void = undefined;
|
||||
it('then it should dispatch correct actions', async () => {
|
||||
const { dispatchedActions, url } = await getNavigateToExploreContext(openInNewWindow);
|
||||
|
||||
expect(dispatchedActions).toEqual([updateLocation({ path: url, query: {} })]);
|
||||
});
|
||||
|
||||
it('then getDataSourceSrv should have been once', async () => {
|
||||
const { getDataSourceSrv } = await getNavigateToExploreContext(openInNewWindow);
|
||||
|
||||
expect(getDataSourceSrv).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('then getDataSourceSrv.get should have been called with correct arguments', async () => {
|
||||
const { get, panel } = await getNavigateToExploreContext(openInNewWindow);
|
||||
|
||||
expect(get).toHaveBeenCalledTimes(1);
|
||||
expect(get).toHaveBeenCalledWith(panel.datasource);
|
||||
});
|
||||
|
||||
it('then getTimeSrv should have been called once', async () => {
|
||||
const { getTimeSrv } = await getNavigateToExploreContext(openInNewWindow);
|
||||
|
||||
expect(getTimeSrv).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('then getExploreUrl should have been called with correct arguments', async () => {
|
||||
const { getExploreUrl, panel, datasource, getDataSourceSrv, getTimeSrv } = await getNavigateToExploreContext(
|
||||
openInNewWindow
|
||||
);
|
||||
|
||||
expect(getExploreUrl).toHaveBeenCalledTimes(1);
|
||||
expect(getExploreUrl).toHaveBeenCalledWith({
|
||||
panel,
|
||||
panelTargets: panel.targets,
|
||||
panelDatasource: datasource,
|
||||
datasourceSrv: getDataSourceSrv(),
|
||||
timeSrv: getTimeSrv(),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('and openInNewWindow is defined', () => {
|
||||
const openInNewWindow: (url: string) => void = jest.fn();
|
||||
it('then it should dispatch no actions', async () => {
|
||||
const { dispatchedActions } = await getNavigateToExploreContext(openInNewWindow);
|
||||
|
||||
expect(dispatchedActions).toEqual([]);
|
||||
});
|
||||
|
||||
it('then getDataSourceSrv should have been once', async () => {
|
||||
const { getDataSourceSrv } = await getNavigateToExploreContext(openInNewWindow);
|
||||
|
||||
expect(getDataSourceSrv).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('then getDataSourceSrv.get should have been called with correct arguments', async () => {
|
||||
const { get, panel } = await getNavigateToExploreContext(openInNewWindow);
|
||||
|
||||
expect(get).toHaveBeenCalledTimes(1);
|
||||
expect(get).toHaveBeenCalledWith(panel.datasource);
|
||||
});
|
||||
|
||||
it('then getTimeSrv should have been called once', async () => {
|
||||
const { getTimeSrv } = await getNavigateToExploreContext(openInNewWindow);
|
||||
|
||||
expect(getTimeSrv).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('then getExploreUrl should have been called with correct arguments', async () => {
|
||||
const { getExploreUrl, panel, datasource, getDataSourceSrv, getTimeSrv } = await getNavigateToExploreContext(
|
||||
openInNewWindow
|
||||
);
|
||||
|
||||
expect(getExploreUrl).toHaveBeenCalledTimes(1);
|
||||
expect(getExploreUrl).toHaveBeenCalledWith({
|
||||
panel,
|
||||
panelTargets: panel.targets,
|
||||
panelDatasource: datasource,
|
||||
datasourceSrv: getDataSourceSrv(),
|
||||
timeSrv: getTimeSrv(),
|
||||
});
|
||||
});
|
||||
|
||||
it('then openInNewWindow should have been called with correct arguments', async () => {
|
||||
const openInNewWindowFunc = jest.fn();
|
||||
const { url } = await getNavigateToExploreContext(openInNewWindowFunc);
|
||||
|
||||
expect(openInNewWindowFunc).toHaveBeenCalledTimes(1);
|
||||
expect(openInNewWindowFunc).toHaveBeenCalledWith(url);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -6,81 +6,84 @@ import store from 'app/core/store';
|
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||
import { Emitter } from 'app/core/core';
|
||||
import {
|
||||
ensureQueries,
|
||||
generateEmptyQuery,
|
||||
parseUrlState,
|
||||
getTimeRange,
|
||||
getTimeRangeFromUrl,
|
||||
generateNewKeyAndAddRefIdIfMissing,
|
||||
lastUsedDatasourceKeyForOrgId,
|
||||
hasNonEmptyQuery,
|
||||
buildQueryTransaction,
|
||||
clearQueryKeys,
|
||||
ensureQueries,
|
||||
generateEmptyQuery,
|
||||
generateNewKeyAndAddRefIdIfMissing,
|
||||
GetExploreUrlArguments,
|
||||
getTimeRange,
|
||||
getTimeRangeFromUrl,
|
||||
hasNonEmptyQuery,
|
||||
lastUsedDatasourceKeyForOrgId,
|
||||
parseUrlState,
|
||||
serializeStateToUrlParam,
|
||||
stopQueryState,
|
||||
updateHistory,
|
||||
} from 'app/core/utils/explore';
|
||||
// Types
|
||||
import { ThunkResult, ExploreUrlState, ExploreItemState } from 'app/types';
|
||||
import { ExploreItemState, ExploreUrlState, ThunkResult } from 'app/types';
|
||||
import { RefreshPicker } from '@grafana/ui';
|
||||
|
||||
import {
|
||||
DataSourceApi,
|
||||
DataQuery,
|
||||
DataSourceSelectItem,
|
||||
QueryFixAction,
|
||||
PanelData,
|
||||
RawTimeRange,
|
||||
LogsDedupStrategy,
|
||||
AbsoluteTimeRange,
|
||||
LoadingState,
|
||||
TimeRange,
|
||||
isDateTime,
|
||||
DataQuery,
|
||||
DataSourceApi,
|
||||
DataSourceSelectItem,
|
||||
dateTimeForTimeZone,
|
||||
isDateTime,
|
||||
LoadingState,
|
||||
LogsDedupStrategy,
|
||||
PanelData,
|
||||
QueryFixAction,
|
||||
RawTimeRange,
|
||||
TimeRange,
|
||||
} from '@grafana/data';
|
||||
import { ExploreId, ExploreUIState, ExploreMode, QueryOptions } from 'app/types/explore';
|
||||
import { ExploreId, ExploreMode, ExploreUIState, QueryOptions } from 'app/types/explore';
|
||||
import {
|
||||
updateDatasourceInstanceAction,
|
||||
addQueryRowAction,
|
||||
changeModeAction,
|
||||
changeQueryAction,
|
||||
changeRangeAction,
|
||||
changeRefreshIntervalAction,
|
||||
ChangeRefreshIntervalPayload,
|
||||
changeSizeAction,
|
||||
ChangeSizePayload,
|
||||
clearOriginAction,
|
||||
clearQueriesAction,
|
||||
historyUpdatedAction,
|
||||
initializeExploreAction,
|
||||
loadDatasourceMissingAction,
|
||||
loadDatasourcePendingAction,
|
||||
queriesImportedAction,
|
||||
LoadDatasourceReadyPayload,
|
||||
loadDatasourceReadyAction,
|
||||
LoadDatasourceReadyPayload,
|
||||
loadExploreDatasources,
|
||||
modifyQueriesAction,
|
||||
queriesImportedAction,
|
||||
queryStoreSubscriptionAction,
|
||||
queryStreamUpdatedAction,
|
||||
scanStartAction,
|
||||
scanStopAction,
|
||||
setQueriesAction,
|
||||
setUrlReplacedAction,
|
||||
splitCloseAction,
|
||||
splitOpenAction,
|
||||
addQueryRowAction,
|
||||
toggleGraphAction,
|
||||
toggleTableAction,
|
||||
ToggleGraphPayload,
|
||||
ToggleTablePayload,
|
||||
updateUIStateAction,
|
||||
loadExploreDatasources,
|
||||
changeModeAction,
|
||||
scanStopAction,
|
||||
setUrlReplacedAction,
|
||||
changeRangeAction,
|
||||
historyUpdatedAction,
|
||||
queryStreamUpdatedAction,
|
||||
queryStoreSubscriptionAction,
|
||||
clearOriginAction,
|
||||
syncTimesAction,
|
||||
toggleGraphAction,
|
||||
ToggleGraphPayload,
|
||||
toggleTableAction,
|
||||
ToggleTablePayload,
|
||||
updateDatasourceInstanceAction,
|
||||
updateUIStateAction,
|
||||
} from './actionTypes';
|
||||
import { ActionOf, ActionCreator } from 'app/core/redux/actionCreatorFactory';
|
||||
import { ActionCreator, ActionOf } from 'app/core/redux/actionCreatorFactory';
|
||||
import { getTimeZone } from 'app/features/profile/state/selectors';
|
||||
import { getShiftedTimeRange } from 'app/core/utils/timePicker';
|
||||
import { updateLocation } from '../../../core/actions';
|
||||
import { getTimeSrv } from '../../dashboard/services/TimeSrv';
|
||||
import { runRequest, preProcessPanelData } from '../../dashboard/state/runRequest';
|
||||
import { getTimeSrv, TimeSrv } from '../../dashboard/services/TimeSrv';
|
||||
import { preProcessPanelData, runRequest } from '../../dashboard/state/runRequest';
|
||||
import { PanelModel } from 'app/features/dashboard/state';
|
||||
import { DataSourceSrv } from '@grafana/runtime';
|
||||
|
||||
/**
|
||||
* Updates UI state and save it to the URL
|
||||
@ -764,3 +767,36 @@ export function refreshExplore(exploreId: ExploreId): ThunkResult<void> {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export interface NavigateToExploreDependencies {
|
||||
getDataSourceSrv: () => DataSourceSrv;
|
||||
getTimeSrv: () => TimeSrv;
|
||||
getExploreUrl: (args: GetExploreUrlArguments) => Promise<string>;
|
||||
openInNewWindow?: (url: string) => void;
|
||||
}
|
||||
|
||||
export const navigateToExplore = (
|
||||
panel: PanelModel,
|
||||
dependencies: NavigateToExploreDependencies
|
||||
): ThunkResult<void> => {
|
||||
return async dispatch => {
|
||||
const { getDataSourceSrv, getTimeSrv, getExploreUrl, openInNewWindow } = dependencies;
|
||||
const datasourceSrv = getDataSourceSrv();
|
||||
const datasource = await datasourceSrv.get(panel.datasource);
|
||||
const path = await getExploreUrl({
|
||||
panel,
|
||||
panelTargets: panel.targets,
|
||||
panelDatasource: datasource,
|
||||
datasourceSrv,
|
||||
timeSrv: getTimeSrv(),
|
||||
});
|
||||
|
||||
if (openInNewWindow) {
|
||||
openInNewWindow(path);
|
||||
return;
|
||||
}
|
||||
|
||||
const query = {}; // strips any angular query param
|
||||
dispatch(updateLocation({ path, query }));
|
||||
};
|
||||
};
|
||||
|
@ -7,16 +7,16 @@ import { getExploreUrl } from 'app/core/utils/explore';
|
||||
import { applyPanelTimeOverrides, getResolution } from 'app/features/dashboard/utils/panel';
|
||||
import { ContextSrv } from 'app/core/services/context_srv';
|
||||
import {
|
||||
toLegacyResponseData,
|
||||
toDataFrameDTO,
|
||||
TimeRange,
|
||||
LoadingState,
|
||||
DataFrame,
|
||||
LegacyResponseData,
|
||||
DataSourceApi,
|
||||
PanelData,
|
||||
DataQueryResponse,
|
||||
DataSourceApi,
|
||||
LegacyResponseData,
|
||||
LoadingState,
|
||||
PanelData,
|
||||
PanelEvents,
|
||||
TimeRange,
|
||||
toDataFrameDTO,
|
||||
toLegacyResponseData,
|
||||
} from '@grafana/data';
|
||||
import { Unsubscribable } from 'rxjs';
|
||||
import { PanelModel } from 'app/features/dashboard/state';
|
||||
@ -256,7 +256,13 @@ class MetricsPanelCtrl extends PanelCtrl {
|
||||
text: 'Explore',
|
||||
icon: 'gicon gicon-explore',
|
||||
shortcut: 'x',
|
||||
href: await getExploreUrl(this.panel, this.panel.targets, this.datasource, this.datasourceSrv, this.timeSrv),
|
||||
href: await getExploreUrl({
|
||||
panel: this.panel,
|
||||
panelTargets: this.panel.targets,
|
||||
panelDatasource: this.datasource,
|
||||
datasourceSrv: this.datasourceSrv,
|
||||
timeSrv: this.timeSrv,
|
||||
}),
|
||||
});
|
||||
}
|
||||
return items;
|
||||
|
Loading…
Reference in New Issue
Block a user