Skip to content

Commit c5898f5

Browse files
angeles-mbelasticmachine
authored andcommitted
[SharedUX] Enable Monaco commands quick access in dev mode (elastic#230543)
Closes elastic#209894. ## Summary - Added quick command access palette to Monaco editor only when in dev mode (access via `F1`) - Removed DevTools `inspectTokens` related functionality since this is now included in the palette (elastic#189203) - Added a way to close the inspect tokens widget which was currently not closing since our ESC listener was interfering with Monaco's one - **Caveat:** Monaco's palette is globally enabled, that means that once any editor's `isDevMode` is true the palette will be enabled for other editors. I don't see any scenario where `isDevMode` could not match between editors or could change during the same session. _Update:_ first approach was using an `isDevMode` prop provided by consumers, now that the env is being checked at the editor itself the palette will be expectedly enabled for every editor when in dev mode. ## Visuals 1. Open Palette 2. Open Inspect Tokens widget 3. ESC closes the widget 4. ESC for a 2nd time behaves as expected and shows editor hint 5. Open Suggestions 6. ESC closes suggestions menu 7. ESC for a 2nd time behaves as expected and shows editor hint **ES|QL editor** https://github.com/user-attachments/assets/55b9a943-750a-425a-b4fa-f1acf3dc8b02 **DevTools editor** https://github.com/user-attachments/assets/f13c2966-03da-4ad8-bea5-c37e826b8429 --------- Co-authored-by: Elastic Machine <[email protected]>
1 parent 8517e19 commit c5898f5

File tree

7 files changed

+76
-16
lines changed

7 files changed

+76
-16
lines changed

src/platform/packages/shared/shared-ux/code_editor/impl/code_editor.test.tsx

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,9 @@ describe('<CodeEditor />', () => {
168168
test('should detect that the suggestion menu is open and not show the hint on ESC', async () => {
169169
getHint().simulate('keydown', { key: keys.ENTER });
170170

171-
// expect((getHint().props() as any).className).toContain('isInactive');
171+
expect(getHint().find('[data-test-subj~="codeEditorHint"]').prop('data-test-subj')).toContain(
172+
`codeEditorHint--inactive`
173+
);
172174
expect(mockedEditorInstance?.__helpers__.areSuggestionsVisible()).toBe(false);
173175

174176
// Show the suggestions in the editor
@@ -183,14 +185,50 @@ describe('<CodeEditor />', () => {
183185
expect(mockedEditorInstance?.__helpers__.areSuggestionsVisible()).toBe(false);
184186

185187
// The keyboard hint is still **not** active
186-
// expect((getHint().props() as any).className).toContain('isInactive');
188+
expect(getHint().find('[data-test-subj~="codeEditorHint"]').prop('data-test-subj')).toContain(
189+
`codeEditorHint--inactive`
190+
);
191+
// Hitting a second time the ESC key should now show the hint
192+
findTestSubject(component, 'monacoEditorTextarea').simulate('keydown', {
193+
keyCode: monaco.KeyCode.Escape,
194+
});
195+
196+
expect(getHint().find('[data-test-subj~="codeEditorHint"]').prop('data-test-subj')).toContain(
197+
`codeEditorHint--active`
198+
);
199+
});
200+
201+
test('should detect that the inspect tokens widget is open and not show the hint on ESC', async () => {
202+
getHint().simulate('keydown', { key: keys.ENTER });
187203

204+
expect(getHint().find('[data-test-subj~="codeEditorHint"]').prop('data-test-subj')).toContain(
205+
`codeEditorHint--inactive`
206+
);
207+
expect(mockedEditorInstance?.__helpers__.isInspectTokensWidgetVisible()).toBe(false);
208+
209+
// Show the inspect tokens widget in the editor
210+
mockedEditorInstance?.__helpers__.showInspectTokensWidget();
211+
expect(mockedEditorInstance?.__helpers__.isInspectTokensWidgetVisible()).toBe(true);
212+
213+
// Hitting the ESC key with the inspect tokens widget visible
214+
findTestSubject(component, 'monacoEditorTextarea').simulate('keydown', {
215+
keyCode: monaco.KeyCode.Escape,
216+
});
217+
218+
expect(mockedEditorInstance?.__helpers__.isInspectTokensWidgetVisible()).toBe(false);
219+
220+
// The keyboard hint is still **not** active
221+
expect(getHint().find('[data-test-subj~="codeEditorHint"]').prop('data-test-subj')).toContain(
222+
`codeEditorHint--inactive`
223+
);
188224
// Hitting a second time the ESC key should now show the hint
189225
findTestSubject(component, 'monacoEditorTextarea').simulate('keydown', {
190226
keyCode: monaco.KeyCode.Escape,
191227
});
192228

193-
// expect((getHint().props() as any).className).not.toContain('isInactive');
229+
expect(getHint().find('[data-test-subj~="codeEditorHint"]').prop('data-test-subj')).toContain(
230+
`codeEditorHint--active`
231+
);
194232
});
195233
});
196234

src/platform/packages/shared/shared-ux/code_editor/impl/code_editor.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,8 +290,15 @@ export const CodeEditor: React.FC<CodeEditorProps> = ({
290290
);
291291

292292
const onKeydownMonaco = useCallback(
293-
(ev: monaco.IKeyboardEvent) => {
293+
(ev: monaco.IKeyboardEvent, editor: monaco.editor.IStandaloneCodeEditor) => {
294294
if (ev.keyCode === monaco.KeyCode.Escape) {
295+
const inspectTokensWidget = editor?.getContribution(
296+
'editor.contrib.inspectTokens'
297+
// @ts-expect-errors -- "_widget" is not part of the TS interface but does exist
298+
)?._widget;
299+
// If the inspect tokens widget is open then we want to let monaco handle ESCAPE for it,
300+
// otherwise widget will not close.
301+
if (inspectTokensWidget) return;
295302
// If the autocompletion context menu is open then we want to let ESCAPE close it but
296303
// **not** exit out of editing mode.
297304
if (!isSuggestionMenuOpen.current) {
@@ -486,7 +493,9 @@ export const CodeEditor: React.FC<CodeEditorProps> = ({
486493
textboxMutationObserver.current.observe(textbox, { attributes: true });
487494
}
488495

489-
editor.onKeyDown(onKeydownMonaco);
496+
editor.onKeyDown((ev: monaco.IKeyboardEvent) => {
497+
onKeydownMonaco(ev, editor);
498+
});
490499
editor.onDidBlurEditorText(onBlurMonaco);
491500

492501
const messageContribution = editor.getContribution('editor.contrib.messageController');

src/platform/packages/shared/shared-ux/code_editor/impl/react_monaco_editor/editor.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ import { useEuiTheme } from '@elastic/eui';
3333
import * as React from 'react';
3434
import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
3535

36+
if (process.env.NODE_ENV !== 'production') {
37+
import(
38+
'monaco-editor/esm/vs/editor/standalone/browser/quickAccess/standaloneCommandsQuickAccess'
39+
);
40+
}
41+
3642
export type EditorConstructionOptions = monacoEditor.editor.IStandaloneEditorConstructionOptions;
3743

3844
export type EditorWillMount = (monaco: typeof monacoEditor) => void | EditorConstructionOptions;

src/platform/packages/shared/shared-ux/code_editor/mocks/monaco_mock/index.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ function createEditorInstance() {
2020
const didHideListeners: Array<(e?: unknown) => void> = [];
2121
let placeholderDiv: undefined | HTMLDivElement;
2222
let areSuggestionsVisible = false;
23+
let isInspectTokensWidgetVisible = false;
2324

2425
/**
2526
* Mocks for monaco editor API
@@ -72,6 +73,7 @@ function createEditorInstance() {
7273
// Helpers for our tests
7374
__helpers__: {
7475
areSuggestionsVisible: () => areSuggestionsVisible,
76+
isInspectTokensWidgetVisible: () => isInspectTokensWidgetVisible,
7577
getPlaceholderRef: (div: HTMLDivElement) => {
7678
placeholderDiv = div;
7779
},
@@ -83,6 +85,10 @@ function createEditorInstance() {
8385
if (e.keyCode === monaco.KeyCode.Escape && areSuggestionsVisible) {
8486
editorInstanceMethods.__helpers__.hideSuggestions();
8587
}
88+
// Close the inspect tokens when hitting the ESC key
89+
if (e.keyCode === monaco.KeyCode.Escape && isInspectTokensWidgetVisible) {
90+
editorInstanceMethods.__helpers__.hideInspectTokensWidget();
91+
}
8692
}) as KeyboardEventHandler,
8793
showSuggestions: () => {
8894
areSuggestionsVisible = true;
@@ -92,6 +98,14 @@ function createEditorInstance() {
9298
areSuggestionsVisible = false;
9399
didHideListeners.forEach((listener) => listener());
94100
},
101+
showInspectTokensWidget: () => {
102+
isInspectTokensWidgetVisible = true;
103+
didShowListeners.forEach((listener) => listener());
104+
},
105+
hideInspectTokensWidget: () => {
106+
isInspectTokensWidgetVisible = false;
107+
didHideListeners.forEach((listener) => listener());
108+
},
95109
},
96110
};
97111

src/platform/plugins/shared/console/public/application/containers/editor/monaco_editor.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ export const MonacoEditor = ({ localStorageValue, value, setValue }: EditorProps
5757
application,
5858
},
5959
docLinkVersion,
60-
config: { isDevMode },
6160
} = context;
6261
const { toasts } = notifications;
6362
const {
@@ -102,13 +101,13 @@ export const MonacoEditor = ({ localStorageValue, value, setValue }: EditorProps
102101

103102
const editorDidMountCallback = useCallback(
104103
(editor: monaco.editor.IStandaloneCodeEditor) => {
105-
const provider = new MonacoEditorActionsProvider(editor, setEditorActionsCss, isDevMode);
104+
const provider = new MonacoEditorActionsProvider(editor, setEditorActionsCss);
106105
setInputEditor(provider);
107106
actionsProvider.current = provider;
108107
setupResizeChecker(divRef.current!, editor);
109108
setEditorInstace(editor);
110109
},
111-
[setupResizeChecker, setInputEditor, setEditorInstace, isDevMode]
110+
[setupResizeChecker, setInputEditor, setEditorInstace]
112111
);
113112

114113
useEffect(() => {

src/platform/plugins/shared/console/public/application/containers/editor/monaco_editor_actions_provider.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ describe('Editor actions provider', () => {
9595

9696
const setEditorActionsCssMock = jest.fn();
9797

98-
editorActionsProvider = new MonacoEditorActionsProvider(editor, setEditorActionsCssMock, true);
98+
editorActionsProvider = new MonacoEditorActionsProvider(editor, setEditorActionsCssMock);
9999
});
100100

101101
describe('getCurl', () => {

src/platform/plugins/shared/console/public/application/containers/editor/monaco_editor_actions_provider.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,14 @@ const TRIGGER_SUGGESTIONS_ACTION_LABEL = 'Trigger suggestions';
5353
const TRIGGER_SUGGESTIONS_HANDLER_ID = 'editor.action.triggerSuggest';
5454
const DEBOUNCE_HIGHLIGHT_WAIT_MS = 200;
5555
const DEBOUNCE_AUTOCOMPLETE_WAIT_MS = 500;
56-
const INSPECT_TOKENS_LABEL = 'Inspect tokens';
57-
const INSPECT_TOKENS_HANDLER_ID = 'editor.action.inspectTokens';
5856
const { collapseLiteralStrings } = XJson;
5957

6058
export class MonacoEditorActionsProvider {
6159
private parsedRequestsProvider: ConsoleParsedRequestsProvider;
6260
private highlightedLines: monaco.editor.IEditorDecorationsCollection;
6361
constructor(
6462
private editor: monaco.editor.IStandaloneCodeEditor,
65-
private setEditorActionsCss: (css: CSSProperties) => void,
66-
private isDevMode: boolean
63+
private setEditorActionsCss: (css: CSSProperties) => void
6764
) {
6865
this.parsedRequestsProvider = getParsedRequestsProvider(this.editor.getModel());
6966
this.highlightedLines = this.editor.createDecorationsCollection();
@@ -112,9 +109,6 @@ export class MonacoEditorActionsProvider {
112109
if (event.keyCode === monaco.KeyCode.Backspace) {
113110
debouncedTriggerSuggestions();
114111
}
115-
if (this.isDevMode && event.keyCode === monaco.KeyCode.F1) {
116-
this.editor.trigger(INSPECT_TOKENS_LABEL, INSPECT_TOKENS_HANDLER_ID, {});
117-
}
118112
});
119113
}
120114

0 commit comments

Comments
 (0)