Datasource: updates PromExploreQueryEditor to prevent it from throwing error on edit (#21605)
* Datasource: updates PromExploreQueryEditor - rewrite to functional component * Datasource: updates PromQueryField - moves an extra field from children to the separate prop * Datasource: adds PromExploreExtraField * Datasource: updates PromExploreQueryEditor - fixes typo * Datasource: updates prometheus explore editor snapshots * Datasource: updates PromExploreExtraField export * Datasource: removes unnecessary div from PromExploreQueryEditor * Datasource: adds basic PromExploreExtraField snapshot test * Datasource: adds basic PromExploreQueryEditor test * Datasource: updates PromExploreQueryEditor snapshot to fix timezone issues * Datasource: updates PromExploreQueryEditor - onChangeQueryStep cleanup * Datasource: updates PromExploreQueryEditor test to check ExtraFieldElement render * Datasource: simplified PromExploreQueryEditor onStepChange method * Datasource: updates Prometheus module import * Datasource: updates PromExploreQueryEditor test * Datasource: updates PromExploreQueryEditor tests * Datasource: fixes PromExploreQueryEditor error on empty interval init * Datasource: adds a tooltip to PromExploreExtraField mounted in PromExploreQueryEditor * Datasource: updates PromExploreQueryEditor snapshots
This commit is contained in:
@@ -0,0 +1,28 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { shallow } from 'enzyme';
|
||||||
|
import { PromExploreExtraField, PromExploreExtraFieldProps } from './PromExploreExtraField';
|
||||||
|
|
||||||
|
const setup = (propOverrides?: PromExploreExtraFieldProps) => {
|
||||||
|
const label = 'Prometheus Explore Extra Field';
|
||||||
|
const value = '123';
|
||||||
|
const onChangeFunc = jest.fn();
|
||||||
|
const onKeyDownFunc = jest.fn();
|
||||||
|
|
||||||
|
const props: any = {
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
onChangeFunc,
|
||||||
|
onKeyDownFunc,
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.assign(props, propOverrides);
|
||||||
|
|
||||||
|
return shallow(<PromExploreExtraField {...props} />);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('PrometheusExploreExtraField', () => {
|
||||||
|
it('should render component', () => {
|
||||||
|
const wrapper = setup();
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// Libraries
|
||||||
|
import React, { memo } from 'react';
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import { FormLabel } from '@grafana/ui';
|
||||||
|
|
||||||
|
export interface PromExploreExtraFieldProps {
|
||||||
|
label: string;
|
||||||
|
onChangeFunc: (e: React.SyntheticEvent<HTMLInputElement>) => void;
|
||||||
|
onKeyDownFunc: (e: React.KeyboardEvent<HTMLInputElement>) => void;
|
||||||
|
value: string;
|
||||||
|
hasTooltip?: boolean;
|
||||||
|
tooltipContent?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PromExploreExtraField(props: PromExploreExtraFieldProps) {
|
||||||
|
const { label, onChangeFunc, onKeyDownFunc, value, hasTooltip, tooltipContent } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="gf-form-inline explore-input--ml">
|
||||||
|
<div className="gf-form">
|
||||||
|
<FormLabel width={5} tooltip={hasTooltip ? tooltipContent : null}>
|
||||||
|
{label}
|
||||||
|
</FormLabel>
|
||||||
|
<input
|
||||||
|
type={'text'}
|
||||||
|
className="gf-form-input width-4"
|
||||||
|
placeholder={'auto'}
|
||||||
|
onChange={onChangeFunc}
|
||||||
|
onKeyDown={onKeyDownFunc}
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(PromExploreExtraField);
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { shallow, mount } from 'enzyme';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
|
import PromExploreQueryEditor from './PromExploreQueryEditor';
|
||||||
|
import { PromExploreExtraField } from './PromExploreExtraField';
|
||||||
|
import { PrometheusDatasource } from '../datasource';
|
||||||
|
import { PromQuery } from '../types';
|
||||||
|
import { PanelData, LoadingState, dateTime } from '@grafana/data';
|
||||||
|
|
||||||
|
const setup = (renderMethod: any, propOverrides?: object) => {
|
||||||
|
const datasourceMock: unknown = {};
|
||||||
|
const datasource: PrometheusDatasource = datasourceMock as PrometheusDatasource;
|
||||||
|
const onRunQuery = jest.fn();
|
||||||
|
const onChange = jest.fn();
|
||||||
|
const query: PromQuery = { expr: '', refId: 'A', interval: '1s' };
|
||||||
|
const data: PanelData = {
|
||||||
|
state: LoadingState.NotStarted,
|
||||||
|
series: [],
|
||||||
|
request: {
|
||||||
|
requestId: '1',
|
||||||
|
dashboardId: 1,
|
||||||
|
interval: '1s',
|
||||||
|
panelId: 1,
|
||||||
|
range: {
|
||||||
|
from: dateTime('2020-01-01', 'YYYY-MM-DD'),
|
||||||
|
to: dateTime('2020-01-02', 'YYYY-MM-DD'),
|
||||||
|
raw: {
|
||||||
|
from: dateTime('2020-01-01', 'YYYY-MM-DD'),
|
||||||
|
to: dateTime('2020-01-02', 'YYYY-MM-DD'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
scopedVars: {},
|
||||||
|
targets: [],
|
||||||
|
timezone: 'GMT',
|
||||||
|
app: 'Grafana',
|
||||||
|
startTime: 0,
|
||||||
|
},
|
||||||
|
timeRange: {
|
||||||
|
from: dateTime('2020-01-01', 'YYYY-MM-DD'),
|
||||||
|
to: dateTime('2020-01-02', 'YYYY-MM-DD'),
|
||||||
|
raw: {
|
||||||
|
from: dateTime('2020-01-01', 'YYYY-MM-DD'),
|
||||||
|
to: dateTime('2020-01-02', 'YYYY-MM-DD'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const history: any[] = [];
|
||||||
|
const exploreMode = 'Metrics';
|
||||||
|
|
||||||
|
const props: any = {
|
||||||
|
query,
|
||||||
|
data,
|
||||||
|
datasource,
|
||||||
|
exploreMode,
|
||||||
|
history,
|
||||||
|
onChange,
|
||||||
|
onRunQuery,
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.assign(props, propOverrides);
|
||||||
|
|
||||||
|
return renderMethod(<PromExploreQueryEditor {...props} />);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('PromExploreQueryEditor', () => {
|
||||||
|
let originalGetSelection: typeof window.getSelection;
|
||||||
|
beforeAll(() => {
|
||||||
|
originalGetSelection = window.getSelection;
|
||||||
|
window.getSelection = () => null;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
window.getSelection = originalGetSelection;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render component', () => {
|
||||||
|
const wrapper = setup(shallow);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render PromQueryField with ExtraFieldElement', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
const wrapper = setup(mount);
|
||||||
|
expect(wrapper.find(PromExploreExtraField).length).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,85 +1,57 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { memo } from 'react';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { ExploreQueryFieldProps } from '@grafana/data';
|
import { ExploreQueryFieldProps } from '@grafana/data';
|
||||||
import { FormLabel } from '@grafana/ui';
|
|
||||||
|
|
||||||
import { PrometheusDatasource } from '../datasource';
|
import { PrometheusDatasource } from '../datasource';
|
||||||
import { PromQuery, PromOptions } from '../types';
|
import { PromQuery, PromOptions } from '../types';
|
||||||
|
|
||||||
import PromQueryField from './PromQueryField';
|
import PromQueryField from './PromQueryField';
|
||||||
|
import { PromExploreExtraField } from './PromExploreExtraField';
|
||||||
|
|
||||||
export type Props = ExploreQueryFieldProps<PrometheusDatasource, PromQuery, PromOptions>;
|
export type Props = ExploreQueryFieldProps<PrometheusDatasource, PromQuery, PromOptions>;
|
||||||
|
|
||||||
interface State {
|
export function PromExploreQueryEditor(props: Props) {
|
||||||
interval: string;
|
const { query, data, datasource, history, onChange, onRunQuery } = props;
|
||||||
}
|
|
||||||
|
|
||||||
export class PromExploreQueryEditor extends PureComponent<Props, State> {
|
function onChangeQueryStep(value: string) {
|
||||||
// Query target to be modified and used for queries
|
const { query, onChange } = props;
|
||||||
query: PromQuery;
|
const nextQuery = { ...query, interval: value };
|
||||||
|
onChange(nextQuery);
|
||||||
constructor(props: Props) {
|
|
||||||
super(props);
|
|
||||||
const { query } = props;
|
|
||||||
this.query = query;
|
|
||||||
// Query target properties that are fully controlled inputs
|
|
||||||
this.state = {
|
|
||||||
// Fully controlled text inputs
|
|
||||||
interval: query.interval,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onFieldChange = (query: PromQuery, override?: any) => {
|
function onStepChange(e: React.SyntheticEvent<HTMLInputElement>) {
|
||||||
this.query.expr = query.expr;
|
if (e.currentTarget.value !== query.interval) {
|
||||||
};
|
onChangeQueryStep(e.currentTarget.value);
|
||||||
|
|
||||||
onIntervalChange = (e: React.SyntheticEvent<HTMLInputElement>) => {
|
|
||||||
const interval = e.currentTarget.value;
|
|
||||||
this.query.interval = interval;
|
|
||||||
this.setState({ interval });
|
|
||||||
};
|
|
||||||
|
|
||||||
onRunQuery = () => {
|
|
||||||
const { query } = this;
|
|
||||||
this.props.onChange(query);
|
|
||||||
this.props.onRunQuery();
|
|
||||||
};
|
|
||||||
|
|
||||||
onReturnKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
this.onRunQuery();
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { datasource, query, data, history } = this.props;
|
|
||||||
const { interval } = this.state;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="gf-form-inline">
|
|
||||||
<PromQueryField
|
|
||||||
datasource={datasource}
|
|
||||||
query={query}
|
|
||||||
onRunQuery={this.onRunQuery}
|
|
||||||
onChange={this.onFieldChange}
|
|
||||||
history={history}
|
|
||||||
data={data}
|
|
||||||
>
|
|
||||||
<div className="gf-form-inline explore-input--ml">
|
|
||||||
<div className="gf-form">
|
|
||||||
<FormLabel width={4}>Step</FormLabel>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="gf-form-input width-6"
|
|
||||||
placeholder={'auto'}
|
|
||||||
onChange={this.onIntervalChange}
|
|
||||||
onKeyDown={this.onReturnKeyDown}
|
|
||||||
value={interval}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</PromQueryField>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onReturnKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
onRunQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PromQueryField
|
||||||
|
datasource={datasource}
|
||||||
|
query={query}
|
||||||
|
onRunQuery={onRunQuery}
|
||||||
|
onChange={onChange}
|
||||||
|
history={history}
|
||||||
|
data={data}
|
||||||
|
ExtraFieldElement={
|
||||||
|
<PromExploreExtraField
|
||||||
|
label={'Step'}
|
||||||
|
onChangeFunc={onStepChange}
|
||||||
|
onKeyDownFunc={onReturnKeyDown}
|
||||||
|
value={query.interval || ''}
|
||||||
|
hasTooltip={true}
|
||||||
|
tooltipContent={'Needs to be a valid time unit string, for example 5s, 1m, 3h, 1d, 1y'}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default memo(PromExploreQueryEditor);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React from 'react';
|
import React, { ReactNode } from 'react';
|
||||||
|
|
||||||
import { Plugin } from 'slate';
|
import { Plugin } from 'slate';
|
||||||
import {
|
import {
|
||||||
@@ -110,6 +110,7 @@ export function willApplySuggestion(suggestion: string, { typeaheadContext, type
|
|||||||
|
|
||||||
interface PromQueryFieldProps extends ExploreQueryFieldProps<PrometheusDatasource, PromQuery, PromOptions> {
|
interface PromQueryFieldProps extends ExploreQueryFieldProps<PrometheusDatasource, PromQuery, PromOptions> {
|
||||||
history: Array<HistoryItem<PromQuery>>;
|
history: Array<HistoryItem<PromQuery>>;
|
||||||
|
ExtraFieldElement?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PromQueryFieldState {
|
interface PromQueryFieldState {
|
||||||
@@ -291,7 +292,7 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { data, query, children } = this.props;
|
const { data, query, ExtraFieldElement } = this.props;
|
||||||
const { metricsOptions, syntaxLoaded, hint } = this.state;
|
const { metricsOptions, syntaxLoaded, hint } = this.state;
|
||||||
const cleanText = this.languageProvider ? this.languageProvider.cleanText : undefined;
|
const cleanText = this.languageProvider ? this.languageProvider.cleanText : undefined;
|
||||||
const chooserText = getChooserText(syntaxLoaded, metricsOptions);
|
const chooserText = getChooserText(syntaxLoaded, metricsOptions);
|
||||||
@@ -321,7 +322,7 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
|||||||
syntaxLoaded={syntaxLoaded}
|
syntaxLoaded={syntaxLoaded}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{children}
|
{ExtraFieldElement}
|
||||||
</div>
|
</div>
|
||||||
{showError ? (
|
{showError ? (
|
||||||
<div className="query-row-break">
|
<div className="query-row-break">
|
||||||
|
|||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`PrometheusExploreExtraField should render component 1`] = `
|
||||||
|
<div
|
||||||
|
className="gf-form-inline explore-input--ml"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<Component
|
||||||
|
tooltip={null}
|
||||||
|
width={5}
|
||||||
|
>
|
||||||
|
Prometheus Explore Extra Field
|
||||||
|
</Component>
|
||||||
|
<input
|
||||||
|
className="gf-form-input width-4"
|
||||||
|
onChange={[MockFunction]}
|
||||||
|
onKeyDown={[MockFunction]}
|
||||||
|
placeholder="auto"
|
||||||
|
type="text"
|
||||||
|
value="123"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`PromExploreQueryEditor should render component 1`] = `
|
||||||
|
<PromQueryField
|
||||||
|
ExtraFieldElement={
|
||||||
|
<PromExploreExtraField
|
||||||
|
hasTooltip={true}
|
||||||
|
label="Step"
|
||||||
|
onChangeFunc={[Function]}
|
||||||
|
onKeyDownFunc={[Function]}
|
||||||
|
tooltipContent="Needs to be a valid time unit string, for example 5s, 1m, 3h, 1d, 1y"
|
||||||
|
value="1s"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
data={
|
||||||
|
Object {
|
||||||
|
"request": Object {
|
||||||
|
"app": "Grafana",
|
||||||
|
"dashboardId": 1,
|
||||||
|
"interval": "1s",
|
||||||
|
"panelId": 1,
|
||||||
|
"range": Object {
|
||||||
|
"from": "2020-01-01T00:00:00.000Z",
|
||||||
|
"raw": Object {
|
||||||
|
"from": "2020-01-01T00:00:00.000Z",
|
||||||
|
"to": "2020-01-02T00:00:00.000Z",
|
||||||
|
},
|
||||||
|
"to": "2020-01-02T00:00:00.000Z",
|
||||||
|
},
|
||||||
|
"requestId": "1",
|
||||||
|
"scopedVars": Object {},
|
||||||
|
"startTime": 0,
|
||||||
|
"targets": Array [],
|
||||||
|
"timezone": "GMT",
|
||||||
|
},
|
||||||
|
"series": Array [],
|
||||||
|
"state": "NotStarted",
|
||||||
|
"timeRange": Object {
|
||||||
|
"from": "2020-01-01T00:00:00.000Z",
|
||||||
|
"raw": Object {
|
||||||
|
"from": "2020-01-01T00:00:00.000Z",
|
||||||
|
"to": "2020-01-02T00:00:00.000Z",
|
||||||
|
},
|
||||||
|
"to": "2020-01-02T00:00:00.000Z",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
datasource={Object {}}
|
||||||
|
history={Array []}
|
||||||
|
onChange={[MockFunction]}
|
||||||
|
onRunQuery={[MockFunction]}
|
||||||
|
query={
|
||||||
|
Object {
|
||||||
|
"expr": "",
|
||||||
|
"interval": "1s",
|
||||||
|
"refId": "A",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
`;
|
||||||
@@ -3,7 +3,7 @@ import { ANNOTATION_QUERY_STEP_DEFAULT, PrometheusDatasource } from './datasourc
|
|||||||
|
|
||||||
import { PromQueryEditor } from './components/PromQueryEditor';
|
import { PromQueryEditor } from './components/PromQueryEditor';
|
||||||
import PromCheatSheet from './components/PromCheatSheet';
|
import PromCheatSheet from './components/PromCheatSheet';
|
||||||
import { PromExploreQueryEditor } from './components/PromExploreQueryEditor';
|
import PromExploreQueryEditor from './components/PromExploreQueryEditor';
|
||||||
|
|
||||||
import { ConfigEditor } from './configuration/ConfigEditor';
|
import { ConfigEditor } from './configuration/ConfigEditor';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user