mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Loki Query Editor: Make Monaco the default editor (#62247)
* feat(loki-editor): remove slate and make Monaco the default editor * feat(loki-editor): remove unsupported usages of onBlur * feat(loki-editor): remove monaco feature flag * Chore: remove unused import
This commit is contained in:
parent
4d268cbcdb
commit
57369915f5
@ -6027,10 +6027,6 @@ exports[`better eslint`] = {
|
||||
"public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx:5381": [
|
||||
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"]
|
||||
],
|
||||
"public/app/plugins/datasource/loki/components/LokiQueryField.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
||||
],
|
||||
"public/app/plugins/datasource/loki/components/monaco-query-field/MonacoQueryField.tsx:5381": [
|
||||
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"]
|
||||
],
|
||||
|
@ -23,7 +23,6 @@ Some stable features are enabled by default. You can disable a stable feature by
|
||||
| ----------------------------------- | ------------------------------------------------------------------------------------ | ------------------ |
|
||||
| `disableEnvelopeEncryption` | Disable envelope encryption (emergency only) | |
|
||||
| `database_metrics` | Add Prometheus metrics for database tables | |
|
||||
| `lokiMonacoEditor` | Access to Monaco query editor for Loki | Yes |
|
||||
| `featureHighlights` | Highlight Grafana Enterprise features | |
|
||||
| `cloudWatchDynamicLabels` | Use dynamic labels instead of alias patterns in CloudWatch datasource | Yes |
|
||||
| `internationalization` | Enables internationalization | Yes |
|
||||
|
@ -32,7 +32,6 @@ export interface FeatureToggles {
|
||||
publicDashboardsEmailSharing?: boolean;
|
||||
lokiLive?: boolean;
|
||||
lokiDataframeApi?: boolean;
|
||||
lokiMonacoEditor?: boolean;
|
||||
swaggerUi?: boolean;
|
||||
featureHighlights?: boolean;
|
||||
dashboardComments?: boolean;
|
||||
|
@ -94,13 +94,6 @@ var (
|
||||
Description: "Use experimental loki api for WebSocket streaming (early prototype)",
|
||||
State: FeatureStateAlpha,
|
||||
},
|
||||
{
|
||||
Name: "lokiMonacoEditor",
|
||||
Description: "Access to Monaco query editor for Loki",
|
||||
State: FeatureStateStable,
|
||||
Expression: "true",
|
||||
FrontendOnly: true,
|
||||
},
|
||||
{
|
||||
Name: "swaggerUi",
|
||||
Description: "Serves swagger UI",
|
||||
|
@ -71,10 +71,6 @@ const (
|
||||
// Use experimental loki api for WebSocket streaming (early prototype)
|
||||
FlagLokiDataframeApi = "lokiDataframeApi"
|
||||
|
||||
// FlagLokiMonacoEditor
|
||||
// Access to Monaco query editor for Loki
|
||||
FlagLokiMonacoEditor = "lokiMonacoEditor"
|
||||
|
||||
// FlagSwaggerUi
|
||||
// Serves swagger UI
|
||||
FlagSwaggerUi = "swaggerUi"
|
||||
|
@ -56,7 +56,6 @@ export const LokiAnnotationsQueryEditor = memo(function LokiAnnotationQueryEdito
|
||||
query={queryWithRefId}
|
||||
onChange={onChangeQuery}
|
||||
onRunQuery={() => {}}
|
||||
onBlur={() => {}}
|
||||
history={history}
|
||||
ExtraFieldElement={
|
||||
<LokiOptionFields
|
||||
|
@ -4,7 +4,6 @@ import { cloneDeep, defaultsDeep } from 'lodash';
|
||||
import React from 'react';
|
||||
|
||||
import { CoreApp } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { QueryEditorMode } from 'app/plugins/datasource/prometheus/querybuilder/shared/types';
|
||||
|
||||
import { createLokiDatasource } from '../mocks';
|
||||
@ -59,10 +58,6 @@ const defaultProps = {
|
||||
onChange: () => {},
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
config.featureToggles.lokiMonacoEditor = true;
|
||||
});
|
||||
|
||||
describe('LokiQueryEditorSelector', () => {
|
||||
it('shows code editor if expr and nothing else', async () => {
|
||||
// We opt for showing code editor for queries created before this feature was added
|
||||
|
@ -12,7 +12,6 @@ export function LokiQueryEditorForAlerting(props: LokiQueryEditorProps) {
|
||||
query={query}
|
||||
onChange={onChange}
|
||||
onRunQuery={onRunQuery}
|
||||
onBlur={onRunQuery}
|
||||
history={history}
|
||||
data={data}
|
||||
placeholder="Enter a Loki query"
|
||||
|
@ -2,7 +2,6 @@ import { render, screen } from '@testing-library/react';
|
||||
import React, { ComponentProps } from 'react';
|
||||
|
||||
import { dateTime } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
|
||||
import { createLokiDatasource } from '../mocks';
|
||||
|
||||
@ -29,7 +28,6 @@ describe('LokiQueryField', () => {
|
||||
};
|
||||
jest.spyOn(props.datasource.languageProvider, 'start').mockResolvedValue([]);
|
||||
jest.spyOn(props.datasource.languageProvider, 'fetchLabels').mockResolvedValue(['label1']);
|
||||
config.featureToggles.lokiMonacoEditor = true;
|
||||
});
|
||||
|
||||
it('refreshes metrics when time range changes over 1 minute', async () => {
|
||||
@ -73,12 +71,4 @@ describe('LokiQueryField', () => {
|
||||
rerender(<LokiQueryField {...props} range={newRange} />);
|
||||
expect(props.datasource.languageProvider.fetchLabels).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('can fall back to the legacy editor', async () => {
|
||||
config.featureToggles.lokiMonacoEditor = false;
|
||||
render(<LokiQueryField {...props} />);
|
||||
|
||||
expect(await screen.findByText('Enter a Loki query (run with Shift+Enter)')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -1,66 +1,13 @@
|
||||
import { LanguageMap, languages as prismLanguages } from 'prismjs';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Plugin, Node } from 'slate';
|
||||
import { Editor } from 'slate-react';
|
||||
|
||||
import { CoreApp, QueryEditorProps } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import {
|
||||
SlatePrism,
|
||||
TypeaheadOutput,
|
||||
SuggestionsState,
|
||||
QueryField,
|
||||
TypeaheadInput,
|
||||
BracesPlugin,
|
||||
DOMUtil,
|
||||
} from '@grafana/ui';
|
||||
import { LocalStorageValueProvider } from 'app/core/components/LocalStorageValueProvider';
|
||||
|
||||
import LokiLanguageProvider from '../LanguageProvider';
|
||||
import { LokiDatasource } from '../datasource';
|
||||
import { escapeLabelValueInSelector, shouldRefreshLabels } from '../languageUtils';
|
||||
import { shouldRefreshLabels } from '../languageUtils';
|
||||
import { LokiQuery, LokiOptions } from '../types';
|
||||
|
||||
import { MonacoQueryFieldWrapper } from './monaco-query-field/MonacoQueryFieldWrapper';
|
||||
|
||||
const LAST_USED_LABELS_KEY = 'grafana.datasources.loki.browser.labels';
|
||||
|
||||
function willApplySuggestion(suggestion: string, { typeaheadContext, typeaheadText }: SuggestionsState): string {
|
||||
// Modify suggestion based on context
|
||||
switch (typeaheadContext) {
|
||||
case 'context-labels': {
|
||||
const nextChar = DOMUtil.getNextCharacter();
|
||||
if (!nextChar || nextChar === '}' || nextChar === ',') {
|
||||
suggestion += '=';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'context-label-values': {
|
||||
// Always add quotes and remove existing ones instead
|
||||
let suggestionModified = '';
|
||||
|
||||
if (!typeaheadText.match(/^(!?=~?"|")/)) {
|
||||
suggestionModified = '"';
|
||||
}
|
||||
|
||||
suggestionModified += escapeLabelValueInSelector(suggestion, typeaheadText);
|
||||
|
||||
if (DOMUtil.getNextCharacter() !== '"') {
|
||||
suggestionModified += '"';
|
||||
}
|
||||
|
||||
suggestion = suggestionModified;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
return suggestion;
|
||||
}
|
||||
|
||||
export interface LokiQueryFieldProps extends QueryEditorProps<LokiDatasource, LokiQuery, LokiOptions> {
|
||||
ExtraFieldElement?: ReactNode;
|
||||
placeholder?: string;
|
||||
@ -69,28 +16,15 @@ export interface LokiQueryFieldProps extends QueryEditorProps<LokiDatasource, Lo
|
||||
|
||||
interface LokiQueryFieldState {
|
||||
labelsLoaded: boolean;
|
||||
labelBrowserVisible: boolean;
|
||||
}
|
||||
|
||||
export class LokiQueryField extends React.PureComponent<LokiQueryFieldProps, LokiQueryFieldState> {
|
||||
plugins: Array<Plugin<Editor>>;
|
||||
_isMounted = false;
|
||||
|
||||
constructor(props: LokiQueryFieldProps) {
|
||||
super(props);
|
||||
|
||||
this.state = { labelsLoaded: false, labelBrowserVisible: false };
|
||||
|
||||
this.plugins = [
|
||||
BracesPlugin(),
|
||||
SlatePrism(
|
||||
{
|
||||
onlyIn: (node: Node) => node.object === 'block' && node.type === 'code_block',
|
||||
getSyntax: (node: Node) => 'logql',
|
||||
},
|
||||
{ ...(prismLanguages as LanguageMap), logql: this.props.datasource.languageProvider.getSyntax() }
|
||||
),
|
||||
];
|
||||
this.state = { labelsLoaded: false };
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
@ -117,11 +51,6 @@ export class LokiQueryField extends React.PureComponent<LokiQueryFieldProps, Lok
|
||||
}
|
||||
}
|
||||
|
||||
onChangeLabelBrowser = (selector: string) => {
|
||||
this.onChangeQuery(selector, true);
|
||||
this.setState({ labelBrowserVisible: false });
|
||||
};
|
||||
|
||||
onChangeQuery = (value: string, override?: boolean) => {
|
||||
// Send text change to parent
|
||||
const { query, onChange, onRunQuery } = this.props;
|
||||
@ -135,76 +64,28 @@ export class LokiQueryField extends React.PureComponent<LokiQueryFieldProps, Lok
|
||||
}
|
||||
};
|
||||
|
||||
onTypeahead = async (typeahead: TypeaheadInput): Promise<TypeaheadOutput> => {
|
||||
const { datasource } = this.props;
|
||||
|
||||
if (!datasource.languageProvider) {
|
||||
return { suggestions: [] };
|
||||
}
|
||||
|
||||
const lokiLanguageProvider = datasource.languageProvider as LokiLanguageProvider;
|
||||
const { history } = this.props;
|
||||
const { prefix, text, value, wrapperClasses, labelKey } = typeahead;
|
||||
|
||||
const result = await lokiLanguageProvider.provideCompletionItems(
|
||||
{ text, value, prefix, wrapperClasses, labelKey },
|
||||
{ history }
|
||||
);
|
||||
return result;
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
ExtraFieldElement,
|
||||
query,
|
||||
app,
|
||||
datasource,
|
||||
placeholder = 'Enter a Loki query (run with Shift+Enter)',
|
||||
history,
|
||||
onRunQuery,
|
||||
onBlur,
|
||||
} = this.props;
|
||||
const { ExtraFieldElement, query, app, datasource, history, onRunQuery } = this.props;
|
||||
|
||||
return (
|
||||
<LocalStorageValueProvider<string[]> storageKey={LAST_USED_LABELS_KEY} defaultValue={[]}>
|
||||
{(lastUsedLabels, onLastUsedLabelsSave, onLastUsedLabelsDelete) => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="gf-form-inline gf-form-inline--xs-view-flex-column flex-grow-1"
|
||||
data-testid={this.props['data-testid']}
|
||||
>
|
||||
<div className="gf-form gf-form--grow flex-shrink-1 min-width-15">
|
||||
{config.featureToggles.lokiMonacoEditor ? (
|
||||
<MonacoQueryFieldWrapper
|
||||
runQueryOnBlur={app !== CoreApp.Explore}
|
||||
datasource={datasource}
|
||||
history={history ?? []}
|
||||
onChange={this.onChangeQuery}
|
||||
onRunQuery={onRunQuery}
|
||||
initialValue={query.expr ?? ''}
|
||||
/>
|
||||
) : (
|
||||
<QueryField
|
||||
additionalPlugins={this.plugins}
|
||||
cleanText={datasource.languageProvider.cleanText}
|
||||
query={query.expr}
|
||||
onTypeahead={this.onTypeahead}
|
||||
onWillApplySuggestion={willApplySuggestion}
|
||||
onChange={this.onChangeQuery}
|
||||
onBlur={onBlur}
|
||||
onRunQuery={onRunQuery}
|
||||
placeholder={placeholder}
|
||||
portalOrigin="loki"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{ExtraFieldElement}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</LocalStorageValueProvider>
|
||||
<>
|
||||
<div
|
||||
className="gf-form-inline gf-form-inline--xs-view-flex-column flex-grow-1"
|
||||
data-testid={this.props['data-testid']}
|
||||
>
|
||||
<div className="gf-form gf-form--grow flex-shrink-1 min-width-15">
|
||||
<MonacoQueryFieldWrapper
|
||||
runQueryOnBlur={app !== CoreApp.Explore}
|
||||
datasource={datasource}
|
||||
history={history ?? []}
|
||||
onChange={this.onChangeQuery}
|
||||
onRunQuery={onRunQuery}
|
||||
initialValue={query.expr ?? ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{ExtraFieldElement}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import { config } from '@grafana/runtime';
|
||||
|
||||
import { createLokiDatasource } from '../../mocks';
|
||||
import { LokiQuery } from '../../types';
|
||||
|
||||
@ -27,10 +25,6 @@ const createDefaultProps = () => {
|
||||
return props;
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
config.featureToggles.lokiMonacoEditor = true;
|
||||
});
|
||||
|
||||
describe('LokiQueryCodeEditor', () => {
|
||||
it('shows explain section when showExplain is true', async () => {
|
||||
const props = createDefaultProps();
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React from 'react';
|
||||
|
||||
import { CoreApp, GrafanaTheme2 } from '@grafana/data';
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { testIds } from '../../components/LokiQueryEditor';
|
||||
@ -27,15 +27,6 @@ export function LokiQueryCodeEditor({
|
||||
}: Props) {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
// the inner QueryField works like this when a blur event happens:
|
||||
// - if it has an onBlur prop, it calls it
|
||||
// - else it calls onRunQuery (some extra conditions apply)
|
||||
//
|
||||
// we want it to not do anything when a blur event happens in explore mode,
|
||||
// so we set an empty-function in such case. otherwise we set `undefined`,
|
||||
// which will cause it to run the query when blur happens.
|
||||
const onBlur = app === CoreApp.Explore ? () => undefined : undefined;
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<LokiQueryField
|
||||
@ -44,7 +35,6 @@ export function LokiQueryCodeEditor({
|
||||
range={range}
|
||||
onRunQuery={onRunQuery}
|
||||
onChange={onChange}
|
||||
onBlur={onBlur}
|
||||
history={history}
|
||||
data={data}
|
||||
app={app}
|
||||
|
Loading…
Reference in New Issue
Block a user