prometheus: enable new monaco-based query field (#41357)

* prometheus: enable new monaco-based query field

* updated test

* updated tests

* fix e2e tests

* updated comment
This commit is contained in:
Gábor Farkas
2021-11-08 14:20:48 +01:00
committed by GitHub
parent e31bd2df2f
commit 9d82111a1a
10 changed files with 57 additions and 35 deletions

View File

@@ -46,6 +46,14 @@ describe('Exemplars', () => {
e2e.components.DataSourcePicker.input().should('be.visible').click(); e2e.components.DataSourcePicker.input().should('be.visible').click();
e2e().contains(dataSourceName).scrollIntoView().should('be.visible').click(); e2e().contains(dataSourceName).scrollIntoView().should('be.visible').click();
// we need to wait for the query-field being lazy-loaded, in two steps:
// 1. first we wait for the text 'Loading...' to appear
// 1. then we wait for the text 'Loading...' to disappear
const monacoLoadingText = 'Loading...';
e2e.components.QueryField.container().should('be.visible').should('have.text', monacoLoadingText);
e2e.components.QueryField.container().should('be.visible').should('not.have.text', monacoLoadingText);
e2e.components.TimePicker.openButton().click(); e2e.components.TimePicker.openButton().click();
e2e.components.TimePicker.fromField().clear().type('2021-07-10 17:10:00'); e2e.components.TimePicker.fromField().clear().type('2021-07-10 17:10:00');
e2e.components.TimePicker.toField().clear().type('2021-07-10 17:30:00'); e2e.components.TimePicker.toField().clear().type('2021-07-10 17:30:00');

View File

@@ -13,7 +13,15 @@ e2e.scenario({
cy.contains('gdev-prometheus').scrollIntoView().should('be.visible').click(); cy.contains('gdev-prometheus').scrollIntoView().should('be.visible').click();
const queryText = 'http_requests_total'; const queryText = 'http_requests_total';
e2e.components.QueryField.container().should('be.visible').type(queryText).type('{backspace}'); // we need to wait for the query-field being lazy-loaded, in two steps:
// it is a two-step process:
// 1. first we wait for the text 'Loading...' to appear
// 1. then we wait for the text 'Loading...' to disappear
const monacoLoadingText = 'Loading...';
e2e.components.QueryField.container().should('be.visible').should('have.text', monacoLoadingText);
e2e.components.QueryField.container().should('be.visible').should('not.have.text', monacoLoadingText);
e2e.components.QueryField.container().type(queryText).type('{backspace}');
cy.contains(queryText.slice(0, -1)).should('be.visible'); cy.contains(queryText.slice(0, -1)).should('be.visible');

View File

@@ -50,7 +50,6 @@ export interface FeatureToggles {
tempoServiceGraph: boolean; tempoServiceGraph: boolean;
tempoSearch: boolean; tempoSearch: boolean;
recordedQueries: boolean; recordedQueries: boolean;
prometheusMonaco: boolean;
newNavigation: boolean; newNavigation: boolean;
fullRangeLogsVolume: boolean; fullRangeLogsVolume: boolean;
autoLoadFullRangeLogsVolume: boolean; autoLoadFullRangeLogsVolume: boolean;

View File

@@ -66,7 +66,6 @@ export class GrafanaBootConfig implements GrafanaConfig {
tempoServiceGraph: false, tempoServiceGraph: false,
tempoSearch: false, tempoSearch: false,
recordedQueries: false, recordedQueries: false,
prometheusMonaco: false,
newNavigation: false, newNavigation: false,
fullRangeLogsVolume: false, fullRangeLogsVolume: false,
autoLoadFullRangeLogsVolume: false, autoLoadFullRangeLogsVolume: false,

View File

@@ -6,6 +6,16 @@ import { PrometheusDatasource } from '../datasource';
import { PromQuery } from '../types'; import { PromQuery } from '../types';
import { LoadingState, PanelData, toUtc, TimeRange } from '@grafana/data'; import { LoadingState, PanelData, toUtc, TimeRange } from '@grafana/data';
// the monaco-based editor uses lazy-loading and that does not work
// well with this test, and we do not need the monaco-related
// functionality in this test anyway, so we mock it out.
jest.mock('./monaco-query-field/MonacoQueryFieldWrapper', () => {
const fakeQueryField = () => <div>prometheus query field</div>;
return {
MonacoQueryFieldWrapper: fakeQueryField,
};
});
const setup = (renderMethod: any, propOverrides?: object) => { const setup = (renderMethod: any, propOverrides?: object) => {
const datasourceMock: unknown = { const datasourceMock: unknown = {
languageProvider: { languageProvider: {

View File

@@ -7,6 +7,16 @@ import { PrometheusDatasource } from '../datasource';
import { testIds as alertingTestIds } from './PromQueryEditorForAlerting'; import { testIds as alertingTestIds } from './PromQueryEditorForAlerting';
import { testIds as regularTestIds } from './PromQueryEditor'; import { testIds as regularTestIds } from './PromQueryEditor';
// the monaco-based editor uses lazy-loading and that does not work
// well with this test, and we do not need the monaco-related
// functionality in this test anyway, so we mock it out.
jest.mock('./monaco-query-field/MonacoQueryFieldWrapper', () => {
const fakeQueryField = () => <div>prometheus query field</div>;
return {
MonacoQueryFieldWrapper: fakeQueryField,
};
});
function setup(app: CoreApp): RenderResult { function setup(app: CoreApp): RenderResult {
const dataSource = ({ const dataSource = ({
createQuery: jest.fn((q) => q), createQuery: jest.fn((q) => q),

View File

@@ -14,7 +14,6 @@ export function PromQueryEditorForAlerting(props: PromQueryEditorProps) {
history={[]} history={[]}
range={range} range={range}
data={data} data={data}
placeholder="Enter a PromQL query"
data-testid={testIds.editor} data-testid={testIds.editor}
/> />
); );

View File

@@ -7,6 +7,16 @@ import { DataSourceInstanceSettings, PanelData, LoadingState, DataFrame } from '
import { PromOptions } from '../types'; import { PromOptions } from '../types';
import { render, screen } from '@testing-library/react'; import { render, screen } from '@testing-library/react';
// the monaco-based editor uses lazy-loading and that does not work
// well with this test, and we do not need the monaco-related
// functionality in this test anyway, so we mock it out.
jest.mock('./monaco-query-field/MonacoQueryFieldWrapper', () => {
const fakeQueryField = () => <div>prometheus query field</div>;
return {
MonacoQueryFieldWrapper: fakeQueryField,
};
});
describe('PromQueryField', () => { describe('PromQueryField', () => {
beforeAll(() => { beforeAll(() => {
// @ts-ignore // @ts-ignore

View File

@@ -1,12 +1,10 @@
import React, { ReactNode } from 'react'; import React, { ReactNode } from 'react';
import { config } from '@grafana/runtime';
import { Plugin } from 'slate'; import { Plugin } from 'slate';
import { import {
SlatePrism, SlatePrism,
TypeaheadInput, TypeaheadInput,
TypeaheadOutput, TypeaheadOutput,
QueryField,
BracesPlugin, BracesPlugin,
DOMUtil, DOMUtil,
SuggestionsState, SuggestionsState,
@@ -73,7 +71,6 @@ export function willApplySuggestion(suggestion: string, { typeaheadContext, type
interface PromQueryFieldProps extends QueryEditorProps<PrometheusDatasource, PromQuery, PromOptions> { interface PromQueryFieldProps extends QueryEditorProps<PrometheusDatasource, PromQuery, PromOptions> {
ExtraFieldElement?: ReactNode; ExtraFieldElement?: ReactNode;
placeholder?: string;
'data-testid'?: string; 'data-testid'?: string;
} }
@@ -266,18 +263,14 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
datasource: { languageProvider }, datasource: { languageProvider },
query, query,
ExtraFieldElement, ExtraFieldElement,
placeholder = 'Enter a PromQL query (run with Shift+Enter)',
history = [], history = [],
} = this.props; } = this.props;
const { labelBrowserVisible, syntaxLoaded, hint } = this.state; const { labelBrowserVisible, syntaxLoaded, hint } = this.state;
const cleanText = languageProvider ? languageProvider.cleanText : undefined;
const hasMetrics = languageProvider.metrics.length > 0; const hasMetrics = languageProvider.metrics.length > 0;
const chooserText = getChooserText(datasource.lookupsDisabled, syntaxLoaded, hasMetrics); const chooserText = getChooserText(datasource.lookupsDisabled, syntaxLoaded, hasMetrics);
const buttonDisabled = !(syntaxLoaded && hasMetrics); const buttonDisabled = !(syntaxLoaded && hasMetrics);
const isMonacoEditorEnabled = config.featureToggles.prometheusMonaco;
return ( return (
<LocalStorageValueProvider<string[]> storageKey={LAST_USED_LABELS_KEY} defaultValue={[]}> <LocalStorageValueProvider<string[]> storageKey={LAST_USED_LABELS_KEY} defaultValue={[]}>
{(lastUsedLabels, onLastUsedLabelsSave, onLastUsedLabelsDelete) => { {(lastUsedLabels, onLastUsedLabelsSave, onLastUsedLabelsDelete) => {
@@ -297,7 +290,6 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
</button> </button>
<div className="gf-form gf-form--grow flex-shrink-1 min-width-15"> <div className="gf-form gf-form--grow flex-shrink-1 min-width-15">
{isMonacoEditorEnabled ? (
<MonacoQueryFieldWrapper <MonacoQueryFieldWrapper
runQueryOnBlur={this.props.app !== CoreApp.Explore} runQueryOnBlur={this.props.app !== CoreApp.Explore}
languageProvider={languageProvider} languageProvider={languageProvider}
@@ -306,21 +298,6 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
onRunQuery={this.props.onRunQuery} onRunQuery={this.props.onRunQuery}
initialValue={query.expr ?? ''} initialValue={query.expr ?? ''}
/> />
) : (
<QueryField
additionalPlugins={this.plugins}
cleanText={cleanText}
query={query.expr}
onTypeahead={this.onTypeahead}
onWillApplySuggestion={willApplySuggestion}
onBlur={this.props.onBlur}
onChange={this.onChangeQuery}
onRunQuery={this.props.onRunQuery}
placeholder={placeholder}
portalOrigin="prometheus"
syntaxLoaded={syntaxLoaded}
/>
)}
</div> </div>
</div> </div>
{labelBrowserVisible && ( {labelBrowserVisible && (

View File

@@ -4,6 +4,7 @@ import { GrafanaTheme2 } from '@grafana/data';
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import { useLatest } from 'react-use'; import { useLatest } from 'react-use';
import { promLanguageDefinition } from 'monaco-promql'; import { promLanguageDefinition } from 'monaco-promql';
import { selectors } from '@grafana/e2e-selectors';
import { getCompletionProvider, getSuggestOptions } from './monaco-completion-provider'; import { getCompletionProvider, getSuggestOptions } from './monaco-completion-provider';
import { Props } from './MonacoQueryFieldProps'; import { Props } from './MonacoQueryFieldProps';
import { getOverrideServices } from './getOverrideServices'; import { getOverrideServices } from './getOverrideServices';
@@ -101,6 +102,7 @@ const MonacoQueryField = (props: Props) => {
return ( return (
<div <div
aria-label={selectors.components.QueryField.container}
className={styles.container} className={styles.container}
// NOTE: we will be setting inline-style-width/height on this element // NOTE: we will be setting inline-style-width/height on this element
ref={containerRef} ref={containerRef}