GrafanaUI: Prevent code editors from 'trapping' scroll (#77125)

* GrafanaUI: Prevent CodeEditor from 'trapping' scroll

* fix test

* fix lint for unused import
This commit is contained in:
Josh Hunt 2023-10-25 16:21:54 +00:00 committed by GitHub
parent 1e81ffccac
commit 333c858bc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 22 additions and 8 deletions

View File

@ -853,9 +853,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "2"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"] [0, 0, 0, "Unexpected any. Specify a different type.", "3"]
], ],
"packages/grafana-ui/src/components/Monaco/CodeEditor.tsx:5381": [
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"]
],
"packages/grafana-ui/src/components/PageLayout/PageToolbar.tsx:5381": [ "packages/grafana-ui/src/components/PageLayout/PageToolbar.tsx:5381": [
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"]
], ],

View File

@ -387,7 +387,7 @@ export const Components = {
singleLink: 'Data link', singleLink: 'Data link',
}, },
CodeEditor: { CodeEditor: {
container: 'Code editor container', container: 'data-testid Code editor container',
}, },
DashboardImportPage: { DashboardImportPage: {
textarea: 'data-testid-import-dashboard-textarea', textarea: 'data-testid-import-dashboard-textarea',

View File

@ -101,6 +101,7 @@ class UnthemedCodeEditor extends PureComponent<Props> {
if (getSuggestions && this.modelId) { if (getSuggestions && this.modelId) {
this.completionCancel = registerSuggestions(monaco, language, getSuggestions, this.modelId); this.completionCancel = registerSuggestions(monaco, language, getSuggestions, this.modelId);
} }
// Save when pressing Ctrl+S or Cmd+S // Save when pressing Ctrl+S or Cmd+S
editor.onKeyDown((e: monacoType.IKeyboardEvent) => { editor.onKeyDown((e: monacoType.IKeyboardEvent) => {
if (e.keyCode === monaco.KeyCode.KeyS && (e.ctrlKey || e.metaKey)) { if (e.keyCode === monaco.KeyCode.KeyS && (e.ctrlKey || e.metaKey)) {
@ -122,6 +123,8 @@ class UnthemedCodeEditor extends PureComponent<Props> {
render() { render() {
const { theme, language, width, height, showMiniMap, showLineNumbers, readOnly, monacoOptions } = this.props; const { theme, language, width, height, showMiniMap, showLineNumbers, readOnly, monacoOptions } = this.props;
const { alwaysConsumeMouseWheel, ...restMonacoOptions } = monacoOptions ?? {};
const value = this.props.value ?? ''; const value = this.props.value ?? '';
const longText = value.length > 100; const longText = value.length > 100;
@ -147,6 +150,10 @@ class UnthemedCodeEditor extends PureComponent<Props> {
bottom: 0.5 * theme.spacing.gridSize, bottom: 0.5 * theme.spacing.gridSize,
}, },
fixedOverflowWidgets: true, // Ensures suggestions menu is drawn on top fixedOverflowWidgets: true, // Ensures suggestions menu is drawn on top
scrollbar: {
alwaysConsumeMouseWheel: alwaysConsumeMouseWheel ?? false,
},
}; };
if (!showLineNumbers) { if (!showLineNumbers) {
@ -157,7 +164,7 @@ class UnthemedCodeEditor extends PureComponent<Props> {
} }
return ( return (
<div className={containerStyles} onBlur={this.onBlur} aria-label={selectors.components.CodeEditor.container}> <div className={containerStyles} onBlur={this.onBlur} data-testid={selectors.components.CodeEditor.container}>
<ReactMonacoEditorLazy <ReactMonacoEditorLazy
width={width} width={width}
height={height} height={height}
@ -165,7 +172,7 @@ class UnthemedCodeEditor extends PureComponent<Props> {
value={value} value={value}
options={{ options={{
...options, ...options,
...(monacoOptions ?? {}), ...(restMonacoOptions ?? {}),
}} }}
beforeMount={this.handleBeforeMount} beforeMount={this.handleBeforeMount}
onMount={this.handleOnMount} onMount={this.handleOnMount}

View File

@ -147,4 +147,11 @@ export interface MonacoOptionsWithGrafanaDefaults extends monacoType.editor.ISta
* Defaults to true. * Defaults to true.
*/ */
automaticLayout?: boolean; automaticLayout?: boolean;
/**
* Always consume mouse wheel events (always call preventDefault() and stopPropagation() on the browser events).
* Always consuming mouse wheel events will prevent the page from scrolling if the cursor is over the editor.
* Defaults to `false`.
*/
alwaysConsumeMouseWheel?: boolean;
} }

View File

@ -2,8 +2,9 @@ import { render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import React from 'react'; import React from 'react';
import { TestProvider } from 'test/helpers/TestProvider'; import { TestProvider } from 'test/helpers/TestProvider';
import { byLabelText, byRole, byTestId } from 'testing-library-selector'; import { byRole, byTestId } from 'testing-library-selector';
import { selectors } from '@grafana/e2e-selectors';
import { locationService, setDataSourceSrv } from '@grafana/runtime'; import { locationService, setDataSourceSrv } from '@grafana/runtime';
import { contextSrv } from 'app/core/services/context_srv'; import { contextSrv } from 'app/core/services/context_srv';
import store from 'app/core/store'; import store from 'app/core/store';
@ -82,7 +83,7 @@ const ui = {
confirmButton: byRole('button', { name: /Yes, reset configuration/ }), confirmButton: byRole('button', { name: /Yes, reset configuration/ }),
resetButton: byRole('button', { name: /Reset configuration/ }), resetButton: byRole('button', { name: /Reset configuration/ }),
saveButton: byRole('button', { name: /Save/ }), saveButton: byRole('button', { name: /Save/ }),
configInput: byLabelText(/Code editor container/), configInput: byTestId(selectors.components.CodeEditor.container),
readOnlyConfig: byTestId('readonly-config'), readOnlyConfig: byTestId('readonly-config'),
}; };

View File

@ -40,6 +40,7 @@ const options: monacoTypes.editor.IStandaloneEditorConstructionOptions = {
verticalScrollbarSize: 8, // used as "padding-right" verticalScrollbarSize: 8, // used as "padding-right"
horizontal: 'hidden', horizontal: 'hidden',
horizontalScrollbarSize: 0, horizontalScrollbarSize: 0,
alwaysConsumeMouseWheel: false,
}, },
scrollBeyondLastLine: false, scrollBeyondLastLine: false,
suggest: getSuggestOptions(), suggest: getSuggestOptions(),

View File

@ -38,6 +38,7 @@ const options: monacoTypes.editor.IStandaloneEditorConstructionOptions = {
verticalScrollbarSize: 8, // used as "padding-right" verticalScrollbarSize: 8, // used as "padding-right"
horizontal: 'hidden', horizontal: 'hidden',
horizontalScrollbarSize: 0, horizontalScrollbarSize: 0,
alwaysConsumeMouseWheel: false,
}, },
scrollBeyondLastLine: false, scrollBeyondLastLine: false,
suggest: getSuggestOptions(), suggest: getSuggestOptions(),