mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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
|
||||
import { ExploreQueryFieldProps } from '@grafana/data';
|
||||
import { FormLabel } from '@grafana/ui';
|
||||
|
||||
import { PrometheusDatasource } from '../datasource';
|
||||
import { PromQuery, PromOptions } from '../types';
|
||||
|
||||
import PromQueryField from './PromQueryField';
|
||||
import { PromExploreExtraField } from './PromExploreExtraField';
|
||||
|
||||
export type Props = ExploreQueryFieldProps<PrometheusDatasource, PromQuery, PromOptions>;
|
||||
|
||||
interface State {
|
||||
interval: string;
|
||||
}
|
||||
export function PromExploreQueryEditor(props: Props) {
|
||||
const { query, data, datasource, history, onChange, onRunQuery } = props;
|
||||
|
||||
export class PromExploreQueryEditor extends PureComponent<Props, State> {
|
||||
// Query target to be modified and used for queries
|
||||
query: PromQuery;
|
||||
|
||||
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,
|
||||
};
|
||||
function onChangeQueryStep(value: string) {
|
||||
const { query, onChange } = props;
|
||||
const nextQuery = { ...query, interval: value };
|
||||
onChange(nextQuery);
|
||||
}
|
||||
|
||||
onFieldChange = (query: PromQuery, override?: any) => {
|
||||
this.query.expr = query.expr;
|
||||
};
|
||||
function onStepChange(e: React.SyntheticEvent<HTMLInputElement>) {
|
||||
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>) => {
|
||||
function onReturnKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
|
||||
if (e.key === 'Enter') {
|
||||
this.onRunQuery();
|
||||
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}
|
||||
onRunQuery={onRunQuery}
|
||||
onChange={onChange}
|
||||
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}
|
||||
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'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</PromQueryField>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(PromExploreQueryEditor);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
import { Plugin } from 'slate';
|
||||
import {
|
||||
@@ -110,6 +110,7 @@ export function willApplySuggestion(suggestion: string, { typeaheadContext, type
|
||||
|
||||
interface PromQueryFieldProps extends ExploreQueryFieldProps<PrometheusDatasource, PromQuery, PromOptions> {
|
||||
history: Array<HistoryItem<PromQuery>>;
|
||||
ExtraFieldElement?: ReactNode;
|
||||
}
|
||||
|
||||
interface PromQueryFieldState {
|
||||
@@ -291,7 +292,7 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
||||
};
|
||||
|
||||
render() {
|
||||
const { data, query, children } = this.props;
|
||||
const { data, query, ExtraFieldElement } = this.props;
|
||||
const { metricsOptions, syntaxLoaded, hint } = this.state;
|
||||
const cleanText = this.languageProvider ? this.languageProvider.cleanText : undefined;
|
||||
const chooserText = getChooserText(syntaxLoaded, metricsOptions);
|
||||
@@ -321,7 +322,7 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
||||
syntaxLoaded={syntaxLoaded}
|
||||
/>
|
||||
</div>
|
||||
{children}
|
||||
{ExtraFieldElement}
|
||||
</div>
|
||||
{showError ? (
|
||||
<div className="query-row-break">
|
||||
|
||||
@@ -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>
|
||||
`;
|
||||
@@ -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 PromCheatSheet from './components/PromCheatSheet';
|
||||
import { PromExploreQueryEditor } from './components/PromExploreQueryEditor';
|
||||
import PromExploreQueryEditor from './components/PromExploreQueryEditor';
|
||||
|
||||
import { ConfigEditor } from './configuration/ConfigEditor';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user