mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: Add entry to panel menu to jump to Explore
* panel container menu gets new Explore entry (between Edit and Share) * entry only shows if datasource has `supportsExplore` set to true (set for Prometheus only for now) * click on Explore entry changes url to `/explore/state` via location provider * `state` is a JSON representation of the panel queries * datasources implement `getExploreState()` how to turn a panel config into explore initial state * Explore can parse the state and initialize its query expressions * ReactContainer now forwards route parameters as props to component * `pluginlist` and `singlestat` panel subclasses needed to be adapted because `panel_ctrl` now has the location provider as a property already
This commit is contained in:
parent
0fc4da810f
commit
05b0bfafe4
@ -38,6 +38,19 @@ function makeTimeSeriesList(dataList, options) {
|
||||
});
|
||||
}
|
||||
|
||||
function parseInitialQueries(initial) {
|
||||
if (!initial) {
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(initial);
|
||||
return parsed.queries.map(q => q.query);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
interface IExploreState {
|
||||
datasource: any;
|
||||
datasourceError: any;
|
||||
@ -58,6 +71,7 @@ export class Explore extends React.Component<any, IExploreState> {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const initialQueries = parseInitialQueries(props.routeParams.initial);
|
||||
this.state = {
|
||||
datasource: null,
|
||||
datasourceError: null,
|
||||
@ -65,7 +79,7 @@ export class Explore extends React.Component<any, IExploreState> {
|
||||
graphResult: null,
|
||||
latency: 0,
|
||||
loading: false,
|
||||
queries: ensureQueries(),
|
||||
queries: ensureQueries(initialQueries),
|
||||
requestOptions: null,
|
||||
showingGraph: true,
|
||||
showingTable: true,
|
||||
@ -77,7 +91,7 @@ export class Explore extends React.Component<any, IExploreState> {
|
||||
const datasource = await this.props.datasourceSrv.get();
|
||||
const testResult = await datasource.testDatasource();
|
||||
if (testResult.status === 'success') {
|
||||
this.setState({ datasource, datasourceError: null, datasourceLoading: false });
|
||||
this.setState({ datasource, datasourceError: null, datasourceLoading: false }, () => this.handleSubmit());
|
||||
} else {
|
||||
this.setState({ datasource: null, datasourceError: testResult.message, datasourceLoading: false });
|
||||
}
|
||||
|
@ -6,13 +6,16 @@ class QueryRow extends PureComponent<any, any> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
query: '',
|
||||
edited: false,
|
||||
query: props.query || '',
|
||||
};
|
||||
}
|
||||
|
||||
handleChangeQuery = value => {
|
||||
const { index, onChangeQuery } = this.props;
|
||||
this.setState({ query: value });
|
||||
const { query } = this.state;
|
||||
const edited = query !== value;
|
||||
this.setState({ edited, query: value });
|
||||
if (onChangeQuery) {
|
||||
onChangeQuery(value, index);
|
||||
}
|
||||
@ -41,6 +44,7 @@ class QueryRow extends PureComponent<any, any> {
|
||||
|
||||
render() {
|
||||
const { request } = this.props;
|
||||
const { edited, query } = this.state;
|
||||
return (
|
||||
<div className="query-row">
|
||||
<div className="query-row-tools">
|
||||
@ -52,7 +56,12 @@ class QueryRow extends PureComponent<any, any> {
|
||||
</button>
|
||||
</div>
|
||||
<div className="query-field-wrapper">
|
||||
<QueryField onPressEnter={this.handlePressEnter} onQueryChange={this.handleChangeQuery} request={request} />
|
||||
<QueryField
|
||||
initialQuery={edited ? null : query}
|
||||
onPressEnter={this.handlePressEnter}
|
||||
onQueryChange={this.handleChangeQuery}
|
||||
request={request}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -63,7 +72,9 @@ export default class QueryRows extends PureComponent<any, any> {
|
||||
render() {
|
||||
const { className = '', queries, ...handlers } = this.props;
|
||||
return (
|
||||
<div className={className}>{queries.map((q, index) => <QueryRow key={q.key} index={index} {...handlers} />)}</div>
|
||||
<div className={className}>
|
||||
{queries.map((q, index) => <QueryRow key={q.key} index={index} query={q.query} {...handlers} />)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ export class PanelCtrl {
|
||||
editorTabs: any;
|
||||
$scope: any;
|
||||
$injector: any;
|
||||
$location: any;
|
||||
$timeout: any;
|
||||
fullscreen: boolean;
|
||||
inspector: any;
|
||||
@ -35,6 +36,7 @@ export class PanelCtrl {
|
||||
|
||||
constructor($scope, $injector) {
|
||||
this.$injector = $injector;
|
||||
this.$location = $injector.get('$location');
|
||||
this.$scope = $scope;
|
||||
this.$timeout = $injector.get('$timeout');
|
||||
this.editorTabIndex = 0;
|
||||
@ -97,6 +99,12 @@ export class PanelCtrl {
|
||||
this.changeView(false, false);
|
||||
}
|
||||
|
||||
explore() {
|
||||
// TS hack :<
|
||||
const initialState = JSON.stringify(this['datasource'].getExploreState(this.panel));
|
||||
this.$location.url(`/explore/${initialState}`);
|
||||
}
|
||||
|
||||
initEditMode() {
|
||||
this.editorTabs = [];
|
||||
this.addEditorTab('General', 'public/app/partials/panelgeneral.html');
|
||||
@ -154,6 +162,16 @@ export class PanelCtrl {
|
||||
});
|
||||
}
|
||||
|
||||
// TS hack :<
|
||||
if ('datasource' in this && this['datasource'].supportsExplore) {
|
||||
menu.push({
|
||||
text: 'Explore',
|
||||
click: 'ctrl.explore();',
|
||||
icon: 'fa fa-fw fa-rocket',
|
||||
shortcut: 'x',
|
||||
});
|
||||
}
|
||||
|
||||
menu.push({
|
||||
text: 'Share',
|
||||
click: 'ctrl.sharePanel();',
|
||||
|
@ -19,6 +19,7 @@ export class PrometheusDatasource {
|
||||
type: string;
|
||||
editorSrc: string;
|
||||
name: string;
|
||||
supportsExplore: boolean;
|
||||
supportMetrics: boolean;
|
||||
url: string;
|
||||
directUrl: string;
|
||||
@ -34,6 +35,7 @@ export class PrometheusDatasource {
|
||||
this.type = 'prometheus';
|
||||
this.editorSrc = 'app/features/prometheus/partials/query.editor.html';
|
||||
this.name = instanceSettings.name;
|
||||
this.supportsExplore = true;
|
||||
this.supportMetrics = true;
|
||||
this.url = instanceSettings.url;
|
||||
this.directUrl = instanceSettings.directUrl;
|
||||
@ -323,6 +325,14 @@ export class PrometheusDatasource {
|
||||
});
|
||||
}
|
||||
|
||||
getExploreState(panel) {
|
||||
if (!panel.targets) {
|
||||
return {};
|
||||
}
|
||||
const queries = panel.targets.map(t => ({ query: t.expr, format: t.format }));
|
||||
return { queries };
|
||||
}
|
||||
|
||||
getPrometheusTime(date, roundUp) {
|
||||
if (_.isString(date)) {
|
||||
date = dateMath.parse(date, roundUp);
|
||||
|
@ -12,7 +12,7 @@ class PluginListCtrl extends PanelCtrl {
|
||||
panelDefaults = {};
|
||||
|
||||
/** @ngInject */
|
||||
constructor($scope, $injector, private backendSrv, private $location) {
|
||||
constructor($scope, $injector, private backendSrv) {
|
||||
super($scope, $injector);
|
||||
|
||||
_.defaults(this.panel, this.panelDefaults);
|
||||
|
@ -77,7 +77,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
};
|
||||
|
||||
/** @ngInject */
|
||||
constructor($scope, $injector, private $location, private linkSrv) {
|
||||
constructor($scope, $injector, private linkSrv) {
|
||||
super($scope, $injector);
|
||||
_.defaults(this.panel, this.panelDefaults);
|
||||
|
||||
|
@ -29,6 +29,7 @@ export function reactContainer($route, $location, backendSrv: BackendSrv, dataso
|
||||
const props = {
|
||||
backendSrv: backendSrv,
|
||||
datasourceSrv: datasourceSrv,
|
||||
routeParams: $route.current.params,
|
||||
};
|
||||
|
||||
ReactDOM.render(WrapInProvider(store, component, props), elem[0]);
|
||||
|
@ -111,7 +111,7 @@ export function setupAngularRoutes($routeProvider, $locationProvider) {
|
||||
controller: 'FolderDashboardsCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
})
|
||||
.when('/explore', {
|
||||
.when('/explore/:initial?', {
|
||||
template: '<react-container />',
|
||||
resolve: {
|
||||
component: () => import(/* webpackChunkName: "explore" */ 'app/containers/Explore/Explore'),
|
||||
|
Loading…
Reference in New Issue
Block a user