mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Pluggable components from datasource plugins
- when instantiating a datasource, the datasource service checks if the plugin module exports Explore components, and if so, attaches them to the datasource - Explore component makes all major internal pluggable from a datasource `exploreComponents` property - Moved Prometheus query field to promehteus datasource and registered it as an exported Explore component - Added new Start page for Explore, also exported from the datasource
This commit is contained in:
@@ -2,6 +2,7 @@ import { DEFAULT_RANGE, serializeStateToUrlParam, parseUrlState } from './explor
|
||||
import { ExploreState } from 'app/types/explore';
|
||||
|
||||
const DEFAULT_EXPLORE_STATE: ExploreState = {
|
||||
customComponents: {},
|
||||
datasource: null,
|
||||
datasourceError: null,
|
||||
datasourceLoading: null,
|
||||
|
||||
@@ -4,6 +4,7 @@ import Select from 'react-select';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { ExploreState, ExploreUrlState, HistoryItem, Query, QueryTransaction, ResultType } from 'app/types/explore';
|
||||
import { RawTimeRange } from 'app/types/series';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import colors from 'app/core/utils/colors';
|
||||
import store from 'app/core/store';
|
||||
@@ -16,14 +17,13 @@ import IndicatorsContainer from 'app/core/components/Picker/IndicatorsContainer'
|
||||
import NoOptionsMessage from 'app/core/components/Picker/NoOptionsMessage';
|
||||
import TableModel, { mergeTablesIntoModel } from 'app/core/table_model';
|
||||
|
||||
import DefaultQueryRows from './QueryRows';
|
||||
import DefaultGraph from './Graph';
|
||||
import DefaultLogs from './Logs';
|
||||
import DefaultTable from './Table';
|
||||
import ErrorBoundary from './ErrorBoundary';
|
||||
import QueryRows from './QueryRows';
|
||||
import Graph from './Graph';
|
||||
import Logs from './Logs';
|
||||
import Table from './Table';
|
||||
import TimePicker from './TimePicker';
|
||||
import { ensureQueries, generateQueryKey, hasQuery } from './utils/query';
|
||||
import { RawTimeRange } from 'app/types/series';
|
||||
|
||||
const MAX_HISTORY_ITEMS = 100;
|
||||
|
||||
@@ -96,6 +96,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
||||
initialQueries = ensureQueries(queries);
|
||||
const initialRange = range || { ...DEFAULT_RANGE };
|
||||
this.state = {
|
||||
customComponents: {},
|
||||
datasource: null,
|
||||
datasourceError: null,
|
||||
datasourceLoading: null,
|
||||
@@ -176,8 +177,13 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
||||
query: this.queryExpressions[i],
|
||||
}));
|
||||
|
||||
const customComponents = {
|
||||
...datasource.exploreComponents,
|
||||
};
|
||||
|
||||
this.setState(
|
||||
{
|
||||
customComponents,
|
||||
datasource,
|
||||
datasourceError,
|
||||
history,
|
||||
@@ -330,6 +336,13 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
||||
);
|
||||
};
|
||||
|
||||
// Use this in help pages to set page to a single query
|
||||
onClickQuery = query => {
|
||||
const nextQueries = [{ query, key: generateQueryKey() }];
|
||||
this.queryExpressions = nextQueries.map(q => q.query);
|
||||
this.setState({ queries: nextQueries }, this.onSubmit);
|
||||
};
|
||||
|
||||
onClickSplit = () => {
|
||||
const { onChangeSplit } = this.props;
|
||||
if (onChangeSplit) {
|
||||
@@ -385,9 +398,9 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
||||
q.query = this.queryExpressions[i];
|
||||
return i === index
|
||||
? {
|
||||
key: generateQueryKey(index),
|
||||
query: datasource.modifyQuery(q.query, action),
|
||||
}
|
||||
key: generateQueryKey(index),
|
||||
query: datasource.modifyQuery(q.query, action),
|
||||
}
|
||||
: q;
|
||||
});
|
||||
nextQueryTransactions = queryTransactions
|
||||
@@ -721,6 +734,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
||||
render() {
|
||||
const { position, split } = this.props;
|
||||
const {
|
||||
customComponents,
|
||||
datasource,
|
||||
datasourceError,
|
||||
datasourceLoading,
|
||||
@@ -760,6 +774,14 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
||||
queryTransactions.filter(qt => qt.resultType === 'Logs' && qt.done && qt.result).map(qt => qt.result)
|
||||
);
|
||||
const loading = queryTransactions.some(qt => !qt.done);
|
||||
const showStartPages = queryTransactions.length === 0 && customComponents.StartPage;
|
||||
|
||||
// Custom components
|
||||
const Graph = customComponents.Graph || DefaultGraph;
|
||||
const Logs = customComponents.Logs || DefaultLogs;
|
||||
const QueryRows = customComponents.QueryRows || DefaultQueryRows;
|
||||
const StartPage = customComponents.StartPage;
|
||||
const Table = customComponents.Table || DefaultTable;
|
||||
|
||||
return (
|
||||
<div className={exploreClass} ref={this.getRef}>
|
||||
@@ -772,12 +794,12 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
||||
</a>
|
||||
</div>
|
||||
) : (
|
||||
<div className="navbar-buttons explore-first-button">
|
||||
<button className="btn navbar-button" onClick={this.onClickCloseSplit}>
|
||||
Close Split
|
||||
<div className="navbar-buttons explore-first-button">
|
||||
<button className="btn navbar-button" onClick={this.onClickCloseSplit}>
|
||||
Close Split
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{!datasourceMissing ? (
|
||||
<div className="navbar-buttons">
|
||||
<Select
|
||||
@@ -836,6 +858,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
||||
{datasource && !datasourceError ? (
|
||||
<div className="explore-container">
|
||||
<QueryRows
|
||||
customComponents={customComponents}
|
||||
datasource={datasource}
|
||||
history={history}
|
||||
queries={queries}
|
||||
@@ -847,43 +870,48 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
||||
supportsLogs={supportsLogs}
|
||||
transactions={queryTransactions}
|
||||
/>
|
||||
<div className="result-options">
|
||||
{supportsGraph ? (
|
||||
<button className={`btn toggle-btn ${graphButtonActive}`} onClick={this.onClickGraphButton}>
|
||||
Graph
|
||||
</button>
|
||||
) : null}
|
||||
{supportsTable ? (
|
||||
<button className={`btn toggle-btn ${tableButtonActive}`} onClick={this.onClickTableButton}>
|
||||
Table
|
||||
</button>
|
||||
) : null}
|
||||
{supportsLogs ? (
|
||||
<button className={`btn toggle-btn ${logsButtonActive}`} onClick={this.onClickLogsButton}>
|
||||
Logs
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<main className="m-t-2">
|
||||
<ErrorBoundary>
|
||||
{supportsGraph &&
|
||||
showingGraph && (
|
||||
<Graph
|
||||
data={graphResult}
|
||||
height={graphHeight}
|
||||
loading={graphLoading}
|
||||
id={`explore-graph-${position}`}
|
||||
range={graphRange}
|
||||
split={split}
|
||||
/>
|
||||
)}
|
||||
{supportsTable && showingTable ? (
|
||||
<div className="panel-container m-t-2">
|
||||
<Table data={tableResult} loading={tableLoading} onClickCell={this.onClickTableCell} />
|
||||
</div>
|
||||
) : null}
|
||||
{supportsLogs && showingLogs ? <Logs data={logsResult} loading={logsLoading} /> : null}
|
||||
{showStartPages && <StartPage onClickQuery={this.onClickQuery} />}
|
||||
{!showStartPages && (
|
||||
<>
|
||||
<div className="result-options">
|
||||
{supportsGraph ? (
|
||||
<button className={`btn toggle-btn ${graphButtonActive}`} onClick={this.onClickGraphButton}>
|
||||
Graph
|
||||
</button>
|
||||
) : null}
|
||||
{supportsTable ? (
|
||||
<button className={`btn toggle-btn ${tableButtonActive}`} onClick={this.onClickTableButton}>
|
||||
Table
|
||||
</button>
|
||||
) : null}
|
||||
{supportsLogs ? (
|
||||
<button className={`btn toggle-btn ${logsButtonActive}`} onClick={this.onClickLogsButton}>
|
||||
Logs
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{supportsGraph &&
|
||||
showingGraph && (
|
||||
<Graph
|
||||
data={graphResult}
|
||||
height={graphHeight}
|
||||
loading={graphLoading}
|
||||
id={`explore-graph-${position}`}
|
||||
range={graphRange}
|
||||
split={split}
|
||||
/>
|
||||
)}
|
||||
{supportsTable && showingTable ? (
|
||||
<div className="panel-container m-t-2">
|
||||
<Table data={tableResult} loading={tableLoading} onClickCell={this.onClickTableCell} />
|
||||
</div>
|
||||
) : null}
|
||||
{supportsLogs && showingLogs ? <Logs data={logsResult} loading={logsLoading} /> : null}
|
||||
</>
|
||||
)}
|
||||
</ErrorBoundary>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@@ -72,7 +72,7 @@ class QueryField extends React.PureComponent<TypeaheadFieldProps, TypeaheadField
|
||||
this.placeholdersBuffer = new PlaceholdersBuffer(props.initialValue || '');
|
||||
|
||||
// Base plugins
|
||||
this.plugins = [ClearPlugin(), NewlinePlugin(), ...props.additionalPlugins];
|
||||
this.plugins = [ClearPlugin(), NewlinePlugin(), ...props.additionalPlugins].filter(p => p);
|
||||
|
||||
this.state = {
|
||||
suggestions: [],
|
||||
@@ -434,19 +434,21 @@ class QueryField extends React.PureComponent<TypeaheadFieldProps, TypeaheadField
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="slate-query-field">
|
||||
{this.renderMenu()}
|
||||
<Editor
|
||||
autoCorrect={false}
|
||||
onBlur={this.handleBlur}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onChange={this.onChange}
|
||||
onFocus={this.handleFocus}
|
||||
placeholder={this.props.placeholder}
|
||||
plugins={this.plugins}
|
||||
spellCheck={false}
|
||||
value={this.state.value}
|
||||
/>
|
||||
<div className="slate-query-field-wrapper">
|
||||
<div className="slate-query-field">
|
||||
{this.renderMenu()}
|
||||
<Editor
|
||||
autoCorrect={false}
|
||||
onBlur={this.handleBlur}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onChange={this.onChange}
|
||||
onFocus={this.handleFocus}
|
||||
placeholder={this.props.placeholder}
|
||||
plugins={this.plugins}
|
||||
spellCheck={false}
|
||||
value={this.state.value}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,9 +2,8 @@ import React, { PureComponent } from 'react';
|
||||
|
||||
import { QueryTransaction, HistoryItem, Query, QueryHint } from 'app/types/explore';
|
||||
|
||||
// TODO make this datasource-plugin-dependent
|
||||
import QueryField from './PromQueryField';
|
||||
import QueryTransactions from './QueryTransactions';
|
||||
import DefaultQueryField from './QueryField';
|
||||
import QueryTransactionStatus from './QueryTransactionStatus';
|
||||
|
||||
function getFirstHintFromTransactions(transactions: QueryTransaction[]): QueryHint {
|
||||
const transaction = transactions.find(qt => qt.hints && qt.hints.length > 0);
|
||||
@@ -24,6 +23,7 @@ interface QueryRowEventHandlers {
|
||||
|
||||
interface QueryRowCommonProps {
|
||||
className?: string;
|
||||
customComponents: any;
|
||||
datasource: any;
|
||||
history: HistoryItem[];
|
||||
// Temporarily
|
||||
@@ -37,7 +37,7 @@ type QueryRowProps = QueryRowCommonProps &
|
||||
query: string;
|
||||
};
|
||||
|
||||
class QueryRow extends PureComponent<QueryRowProps> {
|
||||
class DefaultQueryRow extends PureComponent<QueryRowProps> {
|
||||
onChangeQuery = (value, override?: boolean) => {
|
||||
const { index, onChangeQuery } = this.props;
|
||||
if (onChangeQuery) {
|
||||
@@ -78,14 +78,15 @@ class QueryRow extends PureComponent<QueryRowProps> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { datasource, history, query, supportsLogs, transactions } = this.props;
|
||||
const { customComponents, datasource, history, query, supportsLogs, transactions } = this.props;
|
||||
const transactionWithError = transactions.find(t => t.error !== undefined);
|
||||
const hint = getFirstHintFromTransactions(transactions);
|
||||
const queryError = transactionWithError ? transactionWithError.error : null;
|
||||
const QueryField = customComponents.QueryField || DefaultQueryField;
|
||||
return (
|
||||
<div className="query-row">
|
||||
<div className="query-row-status">
|
||||
<QueryTransactions transactions={transactions} />
|
||||
<QueryTransactionStatus transactions={transactions} />
|
||||
</div>
|
||||
<div className="query-row-field">
|
||||
<QueryField
|
||||
@@ -123,12 +124,14 @@ type QueryRowsProps = QueryRowCommonProps &
|
||||
|
||||
export default class QueryRows extends PureComponent<QueryRowsProps> {
|
||||
render() {
|
||||
const { className = '', queries, transactions, ...handlers } = this.props;
|
||||
const { className = '', customComponents, queries, transactions, ...handlers } = this.props;
|
||||
const QueryRow = customComponents.QueryRow || DefaultQueryRow;
|
||||
return (
|
||||
<div className={className}>
|
||||
{queries.map((q, index) => (
|
||||
<QueryRow
|
||||
key={q.key}
|
||||
customComponents={customComponents}
|
||||
index={index}
|
||||
query={q.query}
|
||||
transactions={transactions.filter(t => t.rowIndex === index)}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { QueryTransaction as QueryTransactionModel } from 'app/types/explore';
|
||||
import { QueryTransaction } from 'app/types/explore';
|
||||
import ElapsedTime from './ElapsedTime';
|
||||
|
||||
function formatLatency(value) {
|
||||
return `${(value / 1000).toFixed(1)}s`;
|
||||
}
|
||||
|
||||
interface QueryTransactionProps {
|
||||
transaction: QueryTransactionModel;
|
||||
interface QueryTransactionStatusItemProps {
|
||||
transaction: QueryTransaction;
|
||||
}
|
||||
|
||||
class QueryTransaction extends PureComponent<QueryTransactionProps> {
|
||||
class QueryTransactionStatusItem extends PureComponent<QueryTransactionStatusItemProps> {
|
||||
render() {
|
||||
const { transaction } = this.props;
|
||||
const className = transaction.done ? 'query-transaction' : 'query-transaction query-transaction--loading';
|
||||
@@ -26,16 +26,16 @@ class QueryTransaction extends PureComponent<QueryTransactionProps> {
|
||||
}
|
||||
}
|
||||
|
||||
interface QueryTransactionsProps {
|
||||
transactions: QueryTransactionModel[];
|
||||
interface QueryTransactionStatusProps {
|
||||
transactions: QueryTransaction[];
|
||||
}
|
||||
|
||||
export default class QueryTransactions extends PureComponent<QueryTransactionsProps> {
|
||||
export default class QueryTransactionStatus extends PureComponent<QueryTransactionStatusProps> {
|
||||
render() {
|
||||
const { transactions } = this.props;
|
||||
return (
|
||||
<div className="query-transactions">
|
||||
{transactions.map((t, i) => <QueryTransaction key={`${t.query}:${t.resultType}`} transaction={t} />)}
|
||||
{transactions.map((t, i) => <QueryTransactionStatusItem key={`${t.query}:${t.resultType}`} transaction={t} />)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -64,6 +64,7 @@ export class DatasourceSrv {
|
||||
const instance = this.$injector.instantiate(plugin.Datasource, { instanceSettings: dsConfig });
|
||||
instance.meta = pluginDef;
|
||||
instance.name = name;
|
||||
instance.exploreComponents = plugin.ExploreComponents;
|
||||
this.datasources[name] = instance;
|
||||
deferred.resolve(instance);
|
||||
})
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
|
||||
const CHEAT_SHEET_ITEMS = [
|
||||
{
|
||||
title: 'Request Rate',
|
||||
expression: 'rate(http_request_total[5m])',
|
||||
label:
|
||||
'Given an HTTP request counter, this query calculates the per-second average request rate over the last 5 minutes.',
|
||||
},
|
||||
{
|
||||
title: '95th Percentile of Request Latencies',
|
||||
expression: 'histogram_quantile(0.95, sum(rate(prometheus_http_request_duration_seconds_bucket[5m])) by (le))',
|
||||
label: 'Calculates the 95th percentile of HTTP request rate over 5 minute windows.',
|
||||
},
|
||||
{
|
||||
title: 'Alerts Firing',
|
||||
expression: 'sort_desc(sum(sum_over_time(ALERTS{alertstate="firing"}[24h])) by (alertname))',
|
||||
label: 'Sums up the alerts that have been firing over the last 24 hours.',
|
||||
},
|
||||
];
|
||||
|
||||
export default (props: any) => (
|
||||
<div>
|
||||
<h1>PromQL Cheat Sheet</h1>
|
||||
{CHEAT_SHEET_ITEMS.map(item => (
|
||||
<div className="cheat-sheet-item" key={item.expression}>
|
||||
<div className="cheat-sheet-item__title">{item.title}</div>
|
||||
<div className="cheat-sheet-item__expression" onClick={e => props.onClickQuery(item.expression)}>
|
||||
<code>{item.expression}</code>
|
||||
</div>
|
||||
<div className="cheat-sheet-item__label">{item.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
@@ -7,11 +7,10 @@ import Prism from 'prismjs';
|
||||
import { TypeaheadOutput } from 'app/types/explore';
|
||||
|
||||
// dom also includes Element polyfills
|
||||
import { getNextCharacter, getPreviousCousin } from './utils/dom';
|
||||
import BracesPlugin from './slate-plugins/braces';
|
||||
import RunnerPlugin from './slate-plugins/runner';
|
||||
|
||||
import TypeaheadField, { TypeaheadInput, TypeaheadFieldState } from './QueryField';
|
||||
import { getNextCharacter, getPreviousCousin } from 'app/features/explore/utils/dom';
|
||||
import BracesPlugin from 'app/features/explore/slate-plugins/braces';
|
||||
import RunnerPlugin from 'app/features/explore/slate-plugins/runner';
|
||||
import TypeaheadField, { TypeaheadInput, TypeaheadFieldState } from 'app/features/explore/QueryField';
|
||||
|
||||
const HISTOGRAM_GROUP = '__histograms__';
|
||||
const METRIC_MARK = 'metric';
|
||||
@@ -261,19 +260,17 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
||||
)}
|
||||
</div>
|
||||
<div className="prom-query-field-wrapper">
|
||||
<div className="slate-query-field-wrapper">
|
||||
<TypeaheadField
|
||||
additionalPlugins={this.plugins}
|
||||
cleanText={cleanText}
|
||||
initialValue={initialQuery}
|
||||
onTypeahead={this.onTypeahead}
|
||||
onWillApplySuggestion={willApplySuggestion}
|
||||
onValueChanged={this.onChangeQuery}
|
||||
placeholder="Enter a PromQL query"
|
||||
portalOrigin="prometheus"
|
||||
syntaxLoaded={syntaxLoaded}
|
||||
/>
|
||||
</div>
|
||||
<TypeaheadField
|
||||
additionalPlugins={this.plugins}
|
||||
cleanText={cleanText}
|
||||
initialValue={initialQuery}
|
||||
onTypeahead={this.onTypeahead}
|
||||
onWillApplySuggestion={willApplySuggestion}
|
||||
onValueChanged={this.onChangeQuery}
|
||||
placeholder="Enter a PromQL query"
|
||||
portalOrigin="prometheus"
|
||||
syntaxLoaded={syntaxLoaded}
|
||||
/>
|
||||
{error ? <div className="prom-query-field-info text-error">{error}</div> : null}
|
||||
{hint ? (
|
||||
<div className="prom-query-field-info text-warning">
|
||||
@@ -0,0 +1,60 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import PromCheatSheet from './PromCheatSheet';
|
||||
|
||||
const TAB_MENU_ITEMS = [
|
||||
{
|
||||
text: 'Start',
|
||||
id: 'start',
|
||||
icon: 'fa fa-rocket',
|
||||
},
|
||||
];
|
||||
|
||||
export default class PromStart extends PureComponent<any, { active: string }> {
|
||||
state = {
|
||||
active: 'start',
|
||||
};
|
||||
|
||||
onClickTab = active => {
|
||||
this.setState({ active });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { active } = this.state;
|
||||
const customCss = '';
|
||||
|
||||
return (
|
||||
<div style={{ margin: '45px 0', border: '1px solid #ddd', borderRadius: 5 }}>
|
||||
<div className="page-header-canvas">
|
||||
<div className="page-container">
|
||||
<div className="page-header">
|
||||
<nav>
|
||||
<ul className={`gf-tabs ${customCss}`}>
|
||||
{TAB_MENU_ITEMS.map((tab, idx) => {
|
||||
const tabClasses = classNames({
|
||||
'gf-tabs-link': true,
|
||||
active: tab.id === active,
|
||||
});
|
||||
|
||||
return (
|
||||
<li className="gf-tabs-item" key={tab.id}>
|
||||
<a className={tabClasses} onClick={() => this.onClickTab(tab.id)}>
|
||||
<i className={tab.icon} />
|
||||
{tab.text}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="page-container page-body">
|
||||
{active === 'start' && <PromCheatSheet onClickQuery={this.props.onClickQuery} />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,22 @@ import { PrometheusDatasource } from './datasource';
|
||||
import { PrometheusQueryCtrl } from './query_ctrl';
|
||||
import { PrometheusConfigCtrl } from './config_ctrl';
|
||||
|
||||
import PrometheusStartPage from './components/PromStart';
|
||||
import PromQueryField from './components/PromQueryField';
|
||||
|
||||
class PrometheusAnnotationsQueryCtrl {
|
||||
static templateUrl = 'partials/annotations.editor.html';
|
||||
}
|
||||
|
||||
const ExploreComponents = {
|
||||
QueryField: PromQueryField,
|
||||
StartPage: PrometheusStartPage,
|
||||
};
|
||||
|
||||
export {
|
||||
PrometheusDatasource as Datasource,
|
||||
PrometheusQueryCtrl as QueryCtrl,
|
||||
PrometheusConfigCtrl as ConfigCtrl,
|
||||
PrometheusAnnotationsQueryCtrl as AnnotationsQueryCtrl,
|
||||
ExploreComponents,
|
||||
};
|
||||
|
||||
@@ -146,6 +146,7 @@ export interface TextMatch {
|
||||
}
|
||||
|
||||
export interface ExploreState {
|
||||
customComponents: any;
|
||||
datasource: any;
|
||||
datasourceError: any;
|
||||
datasourceLoading: boolean | null;
|
||||
|
||||
@@ -6,6 +6,7 @@ export interface PluginExports {
|
||||
ConfigCtrl?: any;
|
||||
AnnotationsQueryCtrl?: any;
|
||||
PanelOptions?: any;
|
||||
ExploreComponents?: any;
|
||||
}
|
||||
|
||||
export interface PanelPlugin {
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
}
|
||||
|
||||
.result-options {
|
||||
margin-top: 2 * $panel-margin;
|
||||
margin: 2 * $panel-margin 0;
|
||||
}
|
||||
|
||||
.time-series-disclaimer {
|
||||
@@ -322,3 +322,19 @@
|
||||
.ReactTable .rt-tr .rt-td:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
// TODO Experimental
|
||||
|
||||
.cheat-sheet-item {
|
||||
margin: 2*$panel-margin 0;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.cheat-sheet-item__title {
|
||||
font-size: $font-size-h3;
|
||||
}
|
||||
|
||||
.cheat-sheet-item__expression {
|
||||
margin: $panel-margin/2 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ module.exports = {
|
||||
publicPath: "public/build/",
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.es6', '.js', '.json'],
|
||||
extensions: ['.ts', '.tsx', '.es6', '.js', '.json', '.svg'],
|
||||
alias: {
|
||||
},
|
||||
modules: [
|
||||
|
||||
Reference in New Issue
Block a user