From 480264ff9f6a200fe005f51e4712f709513d249a Mon Sep 17 00:00:00 2001 From: logonoff Date: Fri, 20 Dec 2024 13:33:54 -0500 Subject: [PATCH 01/13] CONSOLE-4407: Update monaco and YAML language server Remove `umd-compat-loader` --- frontend/package.json | 12 +- .../src/extensions/console-types.ts | 3 +- .../src/components/editor/CodeEditor.tsx | 16 +- .../components/editor/CodeEditorSidebar.tsx | 3 +- .../src/components/editor/theme.ts | 33 --- .../components/editor/yaml-editor-utils.ts | 248 ++++------------ .../integration-tests/support/pages/app.ts | 1 + .../buildconfig/sections/EditorField.tsx | 1 - frontend/public/components/edit-yaml.jsx | 1 - frontend/webpack.config.ts | 18 +- frontend/yarn.lock | 273 ++++++++---------- 11 files changed, 198 insertions(+), 411 deletions(-) delete mode 100644 frontend/packages/console-shared/src/components/editor/theme.ts diff --git a/frontend/package.json b/frontend/package.json index e38c942812d..55b12cbcbc3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -132,6 +132,7 @@ "resolver": "./jest-resolver.js" }, "dependencies": { + "@monaco-editor/react": "^4.6.0", "@patternfly-5/patternfly": "npm:@patternfly/patternfly@5.4.2", "@patternfly/patternfly": "^6.2.0-prerelease.2", "@patternfly/quickstarts": "^6.2.0-prerelease.4", @@ -180,7 +181,7 @@ "js-yaml": "^3.13.1", "json-schema": "^0.3.0", "lodash-es": "^4.17.21", - "monaco-languageclient": "^0.13.0", + "monaco-yaml": "^5.2.3", "murmurhash-js": "1.0.x", "node-polyfill-webpack-plugin": "^4.0.0", "pluralize": "^8.0.0", @@ -197,7 +198,7 @@ "react-linkify": "^0.2.2", "react-measure": "^2.2.6", "react-modal": "^3.12.1", - "react-monaco-editor": "0.46.x", + "react-monaco-editor": "^0.56.2", "react-redux": "7.2.2", "react-router": "5.3.x", "react-router-dom": "5.3.x", @@ -219,12 +220,10 @@ "typesafe-actions": "^4.2.1", "url-search-params-polyfill": "2.x", "victory": "^37.3.6", - "vscode-languageserver-types": "^3.10.0", "whatwg-fetch": "2.x", "xterm": "^4.10.0", "xterm-addon-attach": "0.6.0", "xterm-addon-fit": "0.5.0", - "yaml-language-server": "0.13.0", "yup": "^0.27.0" }, "devDependencies": { @@ -305,8 +304,8 @@ "mochawesome-merge": "^4.1.0", "mochawesome-report-generator": "^5.1.0", "mock-socket": "^9.0.3", - "monaco-editor": "^0.28.1", - "monaco-editor-webpack-plugin": "^4.2.0", + "monaco-editor": "^0.36.1", + "monaco-editor-webpack-plugin": "^7.1.0", "prettier": "2.0.5", "react-refresh": "^0.10.0", "read-pkg": "5.x", @@ -319,7 +318,6 @@ "ts-jest": "21.x", "ts-node": "10.9.2", "typescript": "5.7.2", - "umd-compat-loader": "^2.1.2", "val-loader": "^6.0.0", "webpack": "^5.75.0", "webpack-bundle-analyzer": "4.10.2", diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts b/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts index 5848425dab8..f9f361e04a7 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts @@ -3,7 +3,6 @@ import { QuickStartContextValues } from '@patternfly/quickstarts'; import { ButtonProps } from '@patternfly/react-core'; import { ICell, OnSelect, SortByDirection, TableGridBreakpoint } from '@patternfly/react-table'; import { LocationDescriptor } from 'history'; -import MonacoEditor from 'react-monaco-editor/lib/editor'; import { ExtensionK8sGroupKindModel, K8sModel, @@ -647,7 +646,7 @@ export type CodeEditorProps = { }; export type CodeEditorRef = { - editor?: MonacoEditor['editor']; + editor?: any; }; export type ResourceYAMLEditorProps = { diff --git a/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx b/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx index bbe9d8b4c9f..c0223859688 100644 --- a/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx +++ b/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx @@ -1,14 +1,13 @@ import * as React from 'react'; import Measure from 'react-measure'; -import MonacoEditor from 'react-monaco-editor'; +import MonacoEditor, { EditorDidMount } from 'react-monaco-editor'; import { CodeEditorProps } from '@console/dynamic-plugin-sdk'; import { ThemeContext } from '@console/internal/components/ThemeProvider'; import CodeEditorToolbar from './CodeEditorToolbar'; import { registerYAMLinMonaco, defaultEditorOptions } from './yaml-editor-utils'; -import './theme'; import './CodeEditor.scss'; -const CodeEditor = React.forwardRef((props, ref) => { +const CodeEditor = React.forwardRef((props, ref) => { const { value, options = defaultEditorOptions, @@ -23,9 +22,9 @@ const CodeEditor = React.forwardRef((props, ref) const theme = React.useContext(ThemeContext); const [usesValue] = React.useState(value !== undefined); - const editorDidMount = React.useCallback( + const editorDidMount: EditorDidMount = React.useCallback( (editor, monaco) => { - const currentLanguage = editor.getModel()?.getModeId(); + const currentLanguage = editor.getModel()?.getLanguageId(); editor.layout(); editor.focus(); switch (currentLanguage) { @@ -39,7 +38,7 @@ const CodeEditor = React.forwardRef((props, ref) break; } monaco.editor.getModels()[0]?.updateOptions({ tabSize: 2 }); - onSave && editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S, onSave); // eslint-disable-line no-bitwise + onSave && editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, onSave); // eslint-disable-line no-bitwise }, [onSave, usesValue], ); @@ -59,11 +58,10 @@ const CodeEditor = React.forwardRef((props, ref) {({ measureRef, contentRect }) => (
-
+
; + editorRef: React.MutableRefObject; model?: K8sKind; samples?: Sample[]; schema?: JSONSchema7; diff --git a/frontend/packages/console-shared/src/components/editor/theme.ts b/frontend/packages/console-shared/src/components/editor/theme.ts deleted file mode 100644 index b0c41f490af..00000000000 --- a/frontend/packages/console-shared/src/components/editor/theme.ts +++ /dev/null @@ -1,33 +0,0 @@ -(window as any).monaco.editor.defineTheme('console-light', { - base: 'vs', - inherit: true, - colors: { - 'editor.background': '#fff', - 'editorGutter.background': '#f5f5f5', // black-150 - 'editorLineNumber.activeForeground': '#151515', - 'editorLineNumber.foreground': '#151515', - }, - rules: [ - { token: 'number', foreground: '486b00' }, // light-green-600 - { token: 'type', foreground: '795600' }, // gold-500 - { token: 'string', foreground: '004080' }, // blue-600 - { token: 'keyword', foreground: '40199a' }, // purple-600 - ], -}); - -(window as any).monaco.editor.defineTheme('console-dark', { - base: 'vs-dark', - inherit: true, - colors: { - 'editor.background': '#151515', - 'editorGutter.background': '#292e34', // no pf token defined - 'editorLineNumber.activeForeground': '#fff', - 'editorLineNumber.foreground': '#f0f0f0', - }, - rules: [ - { token: 'number', foreground: 'ace12e' }, // light-green-600 - { token: 'type', foreground: '73bcf7' }, // blue-200 - { token: 'string', foreground: 'f0ab00' }, // gold-400 - { token: 'keyword', foreground: 'cbc1ff' }, // purple-100 - ], -}); diff --git a/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts b/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts index fdc7645e917..0e4e6794f90 100644 --- a/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts +++ b/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts @@ -1,131 +1,15 @@ -import * as URL from 'url'; -import { Uri, Range } from 'monaco-editor'; -import { - MonacoToProtocolConverter, - ProtocolToMonacoConverter, -} from 'monaco-languageclient/lib/monaco-converter'; +import { Range } from 'monaco-editor'; +import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; +import { configureMonacoYaml } from 'monaco-yaml'; import * as yaml from 'yaml-ast-parser'; -import { getLanguageService, TextDocument } from 'yaml-language-server'; import { openAPItoJSONSchema } from '@console/internal/module/k8s/openapi-to-json-schema'; import { getSwaggerDefinitions } from '@console/internal/module/k8s/swagger'; export const defaultEditorOptions = { readOnly: false, scrollBeyondLastLine: false }; -const MODEL_URI = 'inmemory://model.yaml'; -const MONACO_URI = Uri.parse(MODEL_URI); - -const createDocument = (model) => { - return TextDocument.create( - MODEL_URI, - model?.getModeId(), - model?.getVersionId(), - model?.getValue(), - ); -}; - -// Unfortunately, `editor.focus()` doesn't work when hiding the shortcuts -// popover. We need to find the actual DOM element. -export const hackyFocusEditor = () => - setTimeout(() => document.querySelector('.monaco-editor textarea')?.focus()); - -export const registerYAMLLanguage = (monaco) => { - // register the YAML language with Monaco - monaco.languages.register({ - id: 'yaml', - extensions: ['.yml', '.yaml'], - aliases: ['YAML', 'yaml'], - mimetypes: ['application/yaml'], - }); -}; - -export const createYAMLService = () => { - const resolveSchema = (url: string): Promise => { - const promise = new Promise((resolve, reject) => { - const xhr = new XMLHttpRequest(); - xhr.onload = () => resolve(xhr.responseText); - xhr.onerror = () => reject(xhr.statusText); - xhr.open('GET', url, true); - xhr.send(); - }); - return promise as Promise; - }; - - const workspaceContext = { - resolveRelativePath: (relativePath, resource) => URL.resolve(resource, relativePath), - }; - - const yamlService = getLanguageService(resolveSchema, workspaceContext); - - // Prepare the schema - const yamlOpenAPI = getSwaggerDefinitions(); - - // Convert the openAPI schema to something the language server understands - const kubernetesJSONSchema = openAPItoJSONSchema(yamlOpenAPI); - - const schemas = [ - { - uri: 'inmemory:yaml', - fileMatch: ['*'], - schema: kubernetesJSONSchema, - }, - ]; - yamlService.configure({ - validate: true, - schemas, - hover: true, - completion: true, - }); - return yamlService; -}; - -export const registerYAMLCompletion = (languageID, monaco, m2p, p2m, yamlService) => { - monaco.languages.registerCompletionItemProvider(languageID, { - provideCompletionItems(model, position) { - const document = createDocument(model); - return yamlService - .doComplete(document, m2p.asPosition(position.lineNumber, position.column), true) - .then((list) => { - return p2m.asCompletionResult(list); - }); - }, - }); -}; - -export const registerYAMLDocumentSymbols = (languageID, monaco, p2m, yamlService) => { - monaco.languages.registerDocumentSymbolProvider(languageID, { - provideDocumentSymbols(model) { - const document = createDocument(model); - return p2m.asSymbolInformations(yamlService.findDocumentSymbols(document)); - }, - }); -}; - -export const registerYAMLHover = (languageID, monaco, m2p, p2m, yamlService) => { - monaco.languages.registerHoverProvider(languageID, { - provideHover(model, position) { - const doc = createDocument(model); - return yamlService - .doHover(doc, m2p.asPosition(position.lineNumber, position.column), true) - .then((hover) => { - return p2m.asHover(hover); - }) - .then((e) => { - for (const el of document.getElementsByClassName('monaco-editor-hover')) { - el.onclick = (event) => event.preventDefault(); - el.onauxclick = (event) => { - window.open(event.target.getAttribute('data-href'), '_blank').opener = null; - event.preventDefault(); - }; - } - return e; - }); - }, - }); -}; - -const findManagedMetadata = (model) => { - const document = createDocument(model); - const doc = yaml.safeLoad(document.getText()); +const findManagedMetadata = (model: monaco.editor.ITextModel) => { + const modelValue = model.getValue(); + const doc = yaml.safeLoad(modelValue); const rootMappings = doc?.mappings || []; for (const rootElement of rootMappings) { const rootKey = rootElement.key; @@ -139,8 +23,8 @@ const findManagedMetadata = (model) => { // Search for managedFields if (childKey.value === 'managedFields') { - const startLine = document.positionAt(metadataChildren.startPosition).line + 1; - const endLine = document.positionAt(metadataChildren.endPosition).line; + const startLine = model.getPositionAt(metadataChildren.startPosition).lineNumber; + const endLine = model.getPositionAt(metadataChildren.endPosition).lineNumber; return { start: startLine, end: endLine, @@ -155,7 +39,11 @@ const findManagedMetadata = (model) => { }; }; -export const fold = (editor, model, resetMouseLocation: boolean): void => { +export const fold = ( + editor: monaco.editor.IStandaloneCodeEditor, + model: monaco.editor.ITextModel, + resetMouseLocation: boolean, +): void => { const managedLocation = findManagedMetadata(model); const { start } = managedLocation; const { end } = managedLocation; @@ -176,49 +64,20 @@ export const fold = (editor, model, resetMouseLocation: boolean): void => { } }; -// TODO: These functions are not part of React Component LifeCycle, will need refactoring -export const enableYAMLValidation = ( - editor, - monaco, - p2m, - monacoURI, - yamlService, +/** + * Register auto fold for the editor + * This should probably be a React hook + */ +export const registerAutoFold = ( + editor: monaco.editor.IStandaloneCodeEditor, + model: monaco.editor.ITextModel, alreadyInUse: boolean = false, ) => { - const pendingValidationRequests = new Map(); - - const getModel = () => monaco.editor?.getModels()[0]; - - const cleanPendingValidation = (document) => { - const request = pendingValidationRequests.get(document.uri); - if (request !== undefined) { - clearTimeout(request); - pendingValidationRequests.delete(document.uri); - } - }; - - const cleanDiagnostics = () => - monaco.editor.setModelMarkers(monaco.editor.getModel(monacoURI), 'default', []); - - const doValidate = (document) => { - if (document.getText().length === 0) { - cleanDiagnostics(); - return; - } - yamlService - .doValidation(document, true) - .then((diagnostics) => { - const markers = p2m.asDiagnostics(diagnostics); - monaco.editor.setModelMarkers(getModel(), 'default', markers); - }) - .catch(() => {}); - }; - let initialFoldingTriggered = false; const tryFolding = () => { - const document = createDocument(getModel()); - if (!initialFoldingTriggered && document.getText() !== '') { - setTimeout(() => fold(editor, getModel(), true)); + const document = model.getValue(); + if (!initialFoldingTriggered && document !== '') { + setTimeout(() => fold(editor, model, true)); initialFoldingTriggered = true; } }; @@ -226,33 +85,16 @@ export const enableYAMLValidation = ( tryFolding(); } - getModel()?.onDidChangeContent(() => { + model.onDidChangeContent(() => { tryFolding(); - - const document = createDocument(getModel()); - cleanPendingValidation(document); - pendingValidationRequests.set( - document.uri, - setTimeout(() => { - pendingValidationRequests.delete(document.uri); - doValidate(document); - }), - ); }); }; -export const registerYAMLinMonaco = (editor, monaco, alreadyInUse: boolean = false) => { - const LANGUAGE_ID = 'yaml'; - - const m2p = new MonacoToProtocolConverter(); - const p2m = new ProtocolToMonacoConverter(); - - const yamlService = createYAMLService(); - - // validation is not a 'registered' feature like the others, it relies on calling the yamlService - // directly for validation results when content in the editor has changed - enableYAMLValidation(editor, monaco, p2m, MONACO_URI, yamlService, alreadyInUse); - +export const registerYAMLinMonaco = ( + editor: monaco.editor.IStandaloneCodeEditor, + monacoInstance: typeof monaco, + alreadyInUse: boolean = false, +) => { /** * This exists because react-monaco-editor passes the same monaco * object each time. Without it you would be registering all the features again and @@ -264,12 +106,32 @@ export const registerYAMLinMonaco = (editor, monaco, alreadyInUse: boolean = fal * We check that > 1 YAML language exists because one is the default and one is the initial register * that setups our features. */ - if (monaco.languages.getLanguages().filter((x) => x.id === LANGUAGE_ID).length > 1) { - return; + if (monacoInstance.languages.getLanguages().filter((x) => x.id === 'yaml').length <= 1) { + // Prepare the schema + const yamlOpenAPI = getSwaggerDefinitions(); + + // Convert the openAPI schema to something the language server understands + const kubernetesJSONSchema = openAPItoJSONSchema(yamlOpenAPI); + + const schemas = [ + { + uri: 'inmemory:yaml', + fileMatch: ['*'], + schema: kubernetesJSONSchema, + }, + ]; + + configureMonacoYaml(monacoInstance, { + isKubernetes: true, + validate: true, + schemas, + hover: true, + completion: true, + }); } - registerYAMLLanguage(monaco); // register the YAML language with monaco - registerYAMLCompletion(LANGUAGE_ID, monaco, m2p, p2m, yamlService); - registerYAMLDocumentSymbols(LANGUAGE_ID, monaco, p2m, yamlService); - registerYAMLHover(LANGUAGE_ID, monaco, m2p, p2m, yamlService); + if (!alreadyInUse) { + const model = editor.getModel(); + registerAutoFold(editor, model); + } }; diff --git a/frontend/packages/dev-console/integration-tests/support/pages/app.ts b/frontend/packages/dev-console/integration-tests/support/pages/app.ts index bedc03e60f4..260fa04b9ba 100644 --- a/frontend/packages/dev-console/integration-tests/support/pages/app.ts +++ b/frontend/packages/dev-console/integration-tests/support/pages/app.ts @@ -394,6 +394,7 @@ export const yamlEditor = { isLoaded: () => { app.waitForLoad(); cy.get(yamlPO.yamlEditor).should('be.visible'); + cy.window().its('monaco.editor.getModels').should('exist'); }, clearYAMLEditor: () => { diff --git a/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx b/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx index 0d9081aa9d6..4a328f8793e 100644 --- a/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx +++ b/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx @@ -4,7 +4,6 @@ import { FormikValues, useFormikContext } from 'formik'; import MonacoEditor, { ChangeHandler, MonacoEditorProps } from 'react-monaco-editor'; import { ThemeContext } from '@console/internal/components/ThemeProvider'; import { RedExclamationCircleIcon, useDebounceCallback } from '@console/shared/src'; -import '@console/shared/src/components/editor/theme'; type EditorFieldProps = { name: string; diff --git a/frontend/public/components/edit-yaml.jsx b/frontend/public/components/edit-yaml.jsx index a52a5560e78..302e2c7955b 100644 --- a/frontend/public/components/edit-yaml.jsx +++ b/frontend/public/components/edit-yaml.jsx @@ -27,7 +27,6 @@ import { import CodeEditor from '@console/shared/src/components/editor/CodeEditor'; import CodeEditorSidebar from '@console/shared/src/components/editor/CodeEditorSidebar'; -import '@console/shared/src/components/editor/theme'; import { fold } from '@console/shared/src/components/editor/yaml-editor-utils'; import { downloadYaml } from '@console/shared/src/components/editor/yaml-download-utils'; import { isYAMLTemplate, getImpersonate } from '@console/dynamic-plugin-sdk'; diff --git a/frontend/webpack.config.ts b/frontend/webpack.config.ts index ebfff943107..ddc1142b1f5 100644 --- a/frontend/webpack.config.ts +++ b/frontend/webpack.config.ts @@ -147,14 +147,6 @@ const config: Configuration = { }, ], }, - { - test: /node_modules[\\\\|/](yaml-language-server)/, - loader: 'umd-compat-loader', - }, - { - test: /node_modules[\\\\|/](vscode-json-languageservice)/, - loader: 'umd-compat-loader', - }, { test: /\.s?css$/, exclude: /node_modules\/(?!(@patternfly(-\S+)?|@console\/plugin-shared)\/).*/, @@ -265,6 +257,16 @@ const config: Configuration = { new MonacoWebpackPlugin({ languages: ['yaml', 'dockerfile', 'json', 'plaintext'], globalAPI: true, + customLanguages: [ + { + label: 'yaml', + entry: 'monaco-yaml', + worker: { + id: 'monaco-yaml/yamlWorker', + entry: 'monaco-yaml/yaml.worker', + }, + }, + ], }), new NodePolyfillPlugin({ additionalAliases: ['process'], diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 161484e59ed..e042e413c50 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1747,6 +1747,20 @@ resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz#c3ec604a0b54b9a9b87e9735dfc59e1a5da6a5fb" integrity sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug== +"@monaco-editor/loader@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.4.0.tgz#f08227057331ec890fa1e903912a5b711a2ad558" + integrity sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg== + dependencies: + state-local "^1.0.6" + +"@monaco-editor/react@^4.6.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@monaco-editor/react/-/react-4.6.0.tgz#bcc68671e358a21c3814566b865a54b191e24119" + integrity sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw== + dependencies: + "@monaco-editor/loader" "^1.4.0" + "@nodelib/fs.scandir@2.1.3": version "2.1.3" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" @@ -3939,16 +3953,6 @@ ast-types-flow@0.0.7, ast-types-flow@^0.0.7: resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= -ast-types@0.9.6: - version "0.9.6" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9" - integrity sha1-ECyenpAF0+fjgpvwxPok7oYu6bk= - -ast-types@^0.9.2: - version "0.9.14" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.14.tgz#d34ba5dffb9d15a44351fd2a9d82e4ab2838b5ba" - integrity sha512-Ebvx7/0lLboCdyEmAw/4GqwBeKIijPveXNiVGhCGCNxc7z26T5he7DC6ARxu8ByKuzUZZcLog+VP8GMyZrBzJw== - astral-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" @@ -7886,7 +7890,7 @@ espree@^6.1.2: acorn-jsx "^5.1.0" eslint-visitor-keys "^1.1.0" -esprima@^3.1.3, esprima@~3.1.0: +esprima@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= @@ -9005,11 +9009,6 @@ glob-stream@^8.0.0: normalize-path "^3.0.0" streamx "^2.12.5" -glob-to-regexp@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" - integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= - glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" @@ -9639,7 +9638,7 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= -https-proxy-agent@^2.2.1, https-proxy-agent@^2.2.3: +https-proxy-agent@^2.2.1: version "2.2.4" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== @@ -11140,11 +11139,16 @@ json5@^2.1.0, json5@^2.1.2, json5@^2.2.3: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -jsonc-parser@^2.2.1, jsonc-parser@^2.3.1: +jsonc-parser@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342" integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg== +jsonc-parser@^3.0.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.3.1.tgz#f2a524b4f7fd11e3d791e559977ad60b98b798b4" + integrity sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ== + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -11614,7 +11618,7 @@ loader-runner@^4.2.0: resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== -loader-utils@^1.0.3, loader-utils@^1.1.0, loader-utils@^1.4.0: +loader-utils@^1.1.0, loader-utils@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== @@ -11632,7 +11636,7 @@ loader-utils@^2.0.0: emojis-list "^3.0.0" json5 "^2.1.2" -loader-utils@^2.0.4: +loader-utils@^2.0.2, loader-utils@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== @@ -12371,27 +12375,60 @@ module-deps@^6.0.0, module-deps@^6.2.3: through2 "^2.0.0" xtend "^4.0.0" -monaco-editor-webpack-plugin@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-4.2.0.tgz#2be76cde9cca7bd8c3418503625990f86886927b" - integrity sha512-/P3sFiEgBl+Y50he4mbknMhbLJVop5gBUZiPS86SuHUDOOnQiQ5rL1jU5lwt1XKAwMEkhwZbUwqaHxTPkb1Utw== +monaco-editor-webpack-plugin@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-7.1.0.tgz#16f265c2b5dbb5fe08681b6b3b7d00d3c5b2ee97" + integrity sha512-ZjnGINHN963JQkFqjjcBtn1XBtUATDZBMgNQhDQwd78w2ukRhFXAPNgWuacaQiDZsUr4h1rWv5Mv6eriKuOSzA== dependencies: - loader-utils "^2.0.0" + loader-utils "^2.0.2" -monaco-editor@^0.28.1: - version "0.28.1" - resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.28.1.tgz#732788ff2172d59e6d436b206da8cac715413940" - integrity sha512-P1vPqxB4B1ZFzTeR1ScggSp9/5NoQrLCq88fnlNUsuRAP1usEBN4TIpI2lw0AYIZNVIanHk0qwjze2uJwGOHUw== +monaco-editor@^0.36.1: + version "0.36.1" + resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.36.1.tgz#aad528c815605307473a1634612946921d8079b5" + integrity sha512-/CaclMHKQ3A6rnzBzOADfwdSJ25BFoFT0Emxsc4zYVyav5SkK9iA6lEtIeuN/oRYbwPgviJT+t3l+sjFa28jYg== -monaco-languageclient@^0.13.0: - version "0.13.0" - resolved "https://registry.yarnpkg.com/monaco-languageclient/-/monaco-languageclient-0.13.0.tgz#59b68b42fb7633171502d6557f597c2752f6c266" - integrity sha512-aCwd33dTitwV5QwY56rpYHwzEGXei8TZ+yvZcvP3gEMd6Mizr8m3pOuoknDi2SUfLuNAHS6+ulvLgZlNQB5awg== +monaco-languageserver-types@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/monaco-languageserver-types/-/monaco-languageserver-types-0.4.0.tgz#8f3414c1ebba786b9c9db45857cc853e50e768e8" + integrity sha512-QQ3BZiU5LYkJElGncSNb5AKoJ/LCs6YBMCJMAz9EA7v+JaOdn3kx2cXpPTcZfKA5AEsR0vc97sAw+5mdNhVBmw== dependencies: - glob-to-regexp "^0.3.0" - vscode-jsonrpc "^5.0.0" - vscode-languageclient "^6.0.0" - vscode-uri "^2.1.1" + monaco-types "^0.1.0" + vscode-languageserver-protocol "^3.0.0" + vscode-uri "^3.0.0" + +monaco-marker-data-provider@^1.0.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/monaco-marker-data-provider/-/monaco-marker-data-provider-1.2.4.tgz#56fbeede5bd830ec2ee15b2aea9a7df62fa447a7" + integrity sha512-4DsPgsAqpTyUDs3humXRBPUJoihTv+L6v9aupQWD80X2YXaCXUd11mWYeSCYHuPgdUmjFaNWCEOjQ6ewf/QA1Q== + dependencies: + monaco-types "^0.1.0" + +monaco-types@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/monaco-types/-/monaco-types-0.1.0.tgz#3a3066aba499cb5923cd60efc736f3f14a169e10" + integrity sha512-aWK7SN9hAqNYi0WosPoMjenMeXJjwCxDibOqWffyQ/qXdzB/86xshGQobRferfmNz7BSNQ8GB0MD0oby9/5fTQ== + +monaco-worker-manager@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/monaco-worker-manager/-/monaco-worker-manager-2.0.1.tgz#f67c54dfca34ed4b225d5de84e77b24b4e36de8a" + integrity sha512-kdPL0yvg5qjhKPNVjJoym331PY/5JC11aPJXtCZNwWRvBr6jhkIamvYAyiY5P1AWFmNOy0aRDRoMdZfa71h8kg== + +monaco-yaml@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/monaco-yaml/-/monaco-yaml-5.2.3.tgz#990fcf697bfa54e684a2d2f7df6d81a889843db4" + integrity sha512-GEplKC+YYmS0TOlJdv0FzbqkDN/IG2FSwEw+95myECVxTlhty2amwERYjzvorvJXmIagP1grd3Lleq7aQEJpPg== + dependencies: + jsonc-parser "^3.0.0" + monaco-languageserver-types "^0.4.0" + monaco-marker-data-provider "^1.0.0" + monaco-types "^0.1.0" + monaco-worker-manager "^2.0.0" + path-browserify "^1.0.0" + prettier "^2.0.0" + vscode-languageserver-textdocument "^1.0.0" + vscode-languageserver-types "^3.0.0" + vscode-uri "^3.0.0" + yaml "^2.0.0" moo@^0.5.0: version "0.5.2" @@ -13282,7 +13319,7 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= -path-browserify@^1.0.1: +path-browserify@^1.0.0, path-browserify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== @@ -13591,7 +13628,7 @@ prettier@2.0.5: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4" integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg== -prettier@^2.6.2: +prettier@^2.0.0, prettier@^2.6.2: version "2.8.8" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== @@ -13665,7 +13702,7 @@ prisma-yml@1.34.10: scuid "^1.0.2" yaml-ast-parser "^0.0.40" -private@^0.1.7, private@~0.1.5: +private@^0.1.7: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" @@ -14121,12 +14158,12 @@ react-modal@^3.12.1: react-lifecycles-compat "^3.0.0" warning "^4.0.3" -react-monaco-editor@0.46.x: - version "0.46.0" - resolved "https://registry.yarnpkg.com/react-monaco-editor/-/react-monaco-editor-0.46.0.tgz#ac97d5429cd8821d466f0e8e0536ea2a90bbc6d0" - integrity sha512-/GyQ0tQLbjHAuMUNRfKecBYN68o8TwA4fnwH9P+lHbF80ayMAo0PQ60joTQH6R6j839kMn6o9Kk/cbzOxK5DzA== +react-monaco-editor@^0.56.2: + version "0.56.2" + resolved "https://registry.yarnpkg.com/react-monaco-editor/-/react-monaco-editor-0.56.2.tgz#9703424b7d956085c4b74450345cc83c8de9176c" + integrity sha512-Tp5U3QF9h92Cuf0eIhGd8Jyef8tPMlEJC2Dk1GeuR/hj6WoFn8AgjVX/2dv+3l5DvpMUpAECcFarc3eFKTBZ5w== dependencies: - prop-types "^15.7.2" + prop-types "^15.8.1" react-redux@7.2.2: version "7.2.2" @@ -14388,16 +14425,6 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -recast@^0.11.17: - version "0.11.23" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.23.tgz#451fd3004ab1e4df9b4e4b66376b2a21912462d3" - integrity sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM= - dependencies: - ast-types "0.9.6" - esprima "~3.1.0" - private "~0.1.5" - source-map "~0.5.0" - rechoir@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" @@ -14630,15 +14657,6 @@ replaceall@^0.1.6: resolved "https://registry.yarnpkg.com/replaceall/-/replaceall-0.1.6.tgz#81d81ac7aeb72d7f5c4942adf2697a3220688d8e" integrity sha1-gdgax663LX9cSUKt8ml6MiBojY4= -request-light@^0.2.4: - version "0.2.5" - resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.2.5.tgz#38a3da7b2e56f7af8cbba57e8a94930ee2380746" - integrity sha512-eBEh+GzJAftUnex6tcL6eV2JCifY0+sZMIUpUPOVXbs2nV5hla4ZMmO3icYKGuGVuQ2zHE9evh4OrRcH4iyYYw== - dependencies: - http-proxy-agent "^2.1.0" - https-proxy-agent "^2.2.3" - vscode-nls "^4.1.1" - request-progress@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" @@ -15514,7 +15532,7 @@ source-map@^0.4.4: dependencies: amdefine ">=0.0.4" -source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0, source-map@~0.5.1, source-map@~0.5.3: +source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.1, source-map@~0.5.3: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -15654,6 +15672,11 @@ stacktrace-js@^2.0.0: stack-generator "^2.0.5" stacktrace-gps "^3.0.4" +state-local@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/state-local/-/state-local-1.0.7.tgz#da50211d07f05748d53009bee46307a37db386d5" + integrity sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w== + static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -16611,15 +16634,6 @@ uglify-to-browserify@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" -umd-compat-loader@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/umd-compat-loader/-/umd-compat-loader-2.1.2.tgz#abf89be1591940a236cf8fa87f88d6d6f5a8da35" - integrity sha512-RkTlsfrCxUISWqiTtYFFJank7b2Hhl4V2pc29nl0xOEGvvuVkpy1xnufhXfTituxgpW0HSrDk0JHlvPYZxEXKQ== - dependencies: - ast-types "^0.9.2" - loader-utils "^1.0.3" - recast "^0.11.17" - umd@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.3.tgz#aa9fe653c42b9097678489c01000acb69f0b26cf" @@ -17360,7 +17374,7 @@ void-elements@3.1.0: resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w== -vscode-json-languageservice@^3.10.0, vscode-json-languageservice@^3.3.5: +vscode-json-languageservice@^3.3.5: version "3.10.0" resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.10.0.tgz#19eed884fd0f234f8ed2fa0a96e772f293ccc5c4" integrity sha512-8IvuRSQnjznu+obqy6Dy4S4H68Ke7a3Kb+A0FcdctyAMAWEnrORpCpMOMqEYiPLm/OTYLVWJ7ql3qToDTozu4w== @@ -17371,88 +17385,54 @@ vscode-json-languageservice@^3.10.0, vscode-json-languageservice@^3.3.5: vscode-nls "^5.0.0" vscode-uri "^2.1.2" -vscode-jsonrpc@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9" - integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg== - -vscode-jsonrpc@^5.0.0, vscode-jsonrpc@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz#9bab9c330d89f43fc8c1e8702b5c36e058a01794" - integrity sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A== - -vscode-languageclient@^6.0.0: - version "6.1.3" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-6.1.3.tgz#c979c5bb5855714a0307e998c18ca827c1b3953a" - integrity sha512-YciJxk08iU5LmWu7j5dUt9/1OLjokKET6rME3cI4BRpiF6HZlusm2ZwPt0MYJ0lV5y43sZsQHhyon2xBg4ZJVA== - dependencies: - semver "^6.3.0" - vscode-languageserver-protocol "^3.15.3" +vscode-jsonrpc@8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz#f43dfa35fb51e763d17cd94dcca0c9458f35abf9" + integrity sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA== -vscode-languageserver-protocol@3.14.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz#b8aab6afae2849c84a8983d39a1cf742417afe2f" - integrity sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g== +vscode-languageserver-protocol@^3.0.0: + version "3.17.5" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz#864a8b8f390835572f4e13bd9f8313d0e3ac4bea" + integrity sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg== dependencies: - vscode-jsonrpc "^4.0.0" - vscode-languageserver-types "3.14.0" + vscode-jsonrpc "8.2.0" + vscode-languageserver-types "3.17.5" -vscode-languageserver-protocol@^3.15.3: - version "3.15.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz#3fa9a0702d742cf7883cb6182a6212fcd0a1d8bb" - integrity sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw== - dependencies: - vscode-jsonrpc "^5.0.1" - vscode-languageserver-types "3.15.1" +vscode-languageserver-textdocument@^1.0.0: + version "1.0.12" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz#457ee04271ab38998a093c68c2342f53f6e4a631" + integrity sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA== vscode-languageserver-textdocument@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz#178168e87efad6171b372add1dea34f53e5d330f" integrity sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA== -vscode-languageserver-types@3.14.0: - version "3.14.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" - integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== - -vscode-languageserver-types@3.15.1, vscode-languageserver-types@^3.10.0, vscode-languageserver-types@^3.15.1: - version "3.15.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz#17be71d78d2f6236d414f0001ce1ef4d23e6b6de" - integrity sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ== - vscode-languageserver-types@3.16.0-next.2: version "3.16.0-next.2" resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz#940bd15c992295a65eae8ab6b8568a1e8daa3083" integrity sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q== -vscode-languageserver@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.2.1.tgz#0d2feddd33f92aadf5da32450df498d52f6f14eb" - integrity sha512-GuayqdKZqAwwaCUjDvMTAVRPJOp/SLON3mJ07eGsx/Iq9HjRymhKWztX41rISqDKhHVVyFM+IywICyZDla6U3A== - dependencies: - vscode-languageserver-protocol "3.14.1" - vscode-uri "^1.0.6" - -vscode-nls@^4.1.1, vscode-nls@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167" - integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw== +vscode-languageserver-types@3.17.5, vscode-languageserver-types@^3.0.0: + version "3.17.5" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz#3273676f0cf2eab40b3f44d085acbb7f08a39d8a" + integrity sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg== vscode-nls@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA== -vscode-uri@^1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.8.tgz#9769aaececae4026fb6e22359cb38946580ded59" - integrity sha512-obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ== - -vscode-uri@^2.1.1, vscode-uri@^2.1.2: +vscode-uri@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.2.tgz#c8d40de93eb57af31f3c715dd650e2ca2c096f1c" integrity sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A== +vscode-uri@^3.0.0: + version "3.0.8" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.8.tgz#1770938d3e72588659a172d0fd4642780083ff9f" + integrity sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw== + vue-template-compiler@^2.6.11: version "2.6.11" resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz#c04704ef8f498b153130018993e56309d4698080" @@ -18025,33 +18005,16 @@ yaml-ast-parser@^0.0.40: resolved "https://registry.yarnpkg.com/yaml-ast-parser/-/yaml-ast-parser-0.0.40.tgz#08536d4e73d322b1c9ce207ab8dd70e04d20ae6e" integrity sha1-CFNtTnPTIrHJziB6uN1w4E0grm4= -yaml-language-server-parser@0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/yaml-language-server-parser/-/yaml-language-server-parser-0.1.1.tgz#02cc9c022a8b10ffa7ba92096ffee86433a50b07" - integrity sha512-2PememGb1SrPqXAxXTpBD39rwYZap6CJVSvkfULNv9uwV3VHp1TfkgpsylBb+mpuuivH0JZ52lChXPvNa6yVxw== - -yaml-language-server@0.13.0: - version "0.13.0" - resolved "https://registry.yarnpkg.com/yaml-language-server/-/yaml-language-server-0.13.0.tgz#ea18facf59618589b51675ee63e14e00c71c7906" - integrity sha512-5FHW7dUAyIjEM3mRzIplE0pZut2K30cA+K7coaOxFxi82LTk/oiVLS4/AQFnOtGicSyoi4YOiRqpMZ04vgtuew== - dependencies: - js-yaml "^3.13.1" - jsonc-parser "^2.2.1" - request-light "^0.2.4" - vscode-json-languageservice "^3.10.0" - vscode-languageserver "^5.2.1" - vscode-languageserver-types "^3.15.1" - vscode-nls "^4.1.2" - vscode-uri "^2.1.1" - yaml-language-server-parser "0.1.1" - optionalDependencies: - prettier "2.0.5" - yaml@^1.10.0, yaml@^1.7.2: version "1.10.0" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== +yaml@^2.0.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.1.tgz#42f2b1ba89203f374609572d5349fb8686500773" + integrity sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg== + yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" From ae126c3c1a0b703f47e7b65e1b3272d6809340c8 Mon Sep 17 00:00:00 2001 From: logonoff Date: Fri, 3 Jan 2025 09:55:43 -0500 Subject: [PATCH 02/13] CONSOLE-4407: Reload YAML on mount --- .../src/extensions/console-types.ts | 6 +-- .../src/components/editor/CodeEditor.tsx | 22 ++++++-- .../components/editor/CodeEditorSidebar.tsx | 17 ++++--- .../components/editor/yaml-editor-utils.ts | 2 +- frontend/public/components/edit-yaml.jsx | 50 ++++++++++--------- 5 files changed, 57 insertions(+), 40 deletions(-) diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts b/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts index f9f361e04a7..9953e17242d 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts @@ -3,6 +3,7 @@ import { QuickStartContextValues } from '@patternfly/quickstarts'; import { ButtonProps } from '@patternfly/react-core'; import { ICell, OnSelect, SortByDirection, TableGridBreakpoint } from '@patternfly/react-table'; import { LocationDescriptor } from 'history'; +import type { editor } from 'monaco-editor/esm/vs/editor/editor.api'; import { ExtensionK8sGroupKindModel, K8sModel, @@ -643,11 +644,10 @@ export type CodeEditorProps = { toolbarLinks?: React.ReactNodeArray; onChange?: (newValue, event) => void; onSave?: () => void; + onEditorDidMount?: (editor: editor.IStandaloneCodeEditor) => void; }; -export type CodeEditorRef = { - editor?: any; -}; +export type CodeEditorRef = { getEditor: () => editor.IStandaloneCodeEditor }; export type ResourceYAMLEditorProps = { initialResource: string | { [key: string]: any }; diff --git a/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx b/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx index c0223859688..8f042ebabb2 100644 --- a/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx +++ b/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx @@ -1,13 +1,14 @@ import * as React from 'react'; +import type { editor } from 'monaco-editor/esm/vs/editor/editor.api'; import Measure from 'react-measure'; import MonacoEditor, { EditorDidMount } from 'react-monaco-editor'; -import { CodeEditorProps } from '@console/dynamic-plugin-sdk'; +import { CodeEditorRef, CodeEditorProps } from '@console/dynamic-plugin-sdk'; import { ThemeContext } from '@console/internal/components/ThemeProvider'; import CodeEditorToolbar from './CodeEditorToolbar'; import { registerYAMLinMonaco, defaultEditorOptions } from './yaml-editor-utils'; import './CodeEditor.scss'; -const CodeEditor = React.forwardRef((props, ref) => { +const CodeEditor = React.forwardRef((props, ref) => { const { value, options = defaultEditorOptions, @@ -18,12 +19,15 @@ const CodeEditor = React.forwardRef((props, ref) => { onChange, onSave, language, + onEditorDidMount, } = props; const theme = React.useContext(ThemeContext); + const [editorRef, setEditorRef] = React.useState(null); const [usesValue] = React.useState(value !== undefined); const editorDidMount: EditorDidMount = React.useCallback( (editor, monaco) => { + setEditorRef(editor); const currentLanguage = editor.getModel()?.getLanguageId(); editor.layout(); editor.focus(); @@ -39,8 +43,9 @@ const CodeEditor = React.forwardRef((props, ref) => { } monaco.editor.getModels()[0]?.updateOptions({ tabSize: 2 }); onSave && editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, onSave); // eslint-disable-line no-bitwise + onEditorDidMount && onEditorDidMount(editor); }, - [onSave, usesValue], + [onSave, usesValue, onEditorDidMount], ); const editorOptions = React.useMemo(() => { @@ -52,13 +57,22 @@ const CodeEditor = React.forwardRef((props, ref) => { }; }, [options, showMiniMap]); + // use the ref of the editor to expose it to the parent component + React.useImperativeHandle( + ref, + () => ({ + getEditor: () => editorRef, + }), + [editorRef], + ); + return ( <> {({ measureRef, contentRect }) => (
-
+
; + editorRef: React.MutableRefObject; model?: K8sKind; samples?: Sample[]; schema?: JSONSchema7; @@ -27,13 +28,13 @@ const CodeEditorSidebar: React.FC = ({ sanitizeYamlContent, toggleSidebar, }) => { - const editor = editorRef.current?.editor; + const getEditor = React.useCallback(() => editorRef?.current?.getEditor(), [editorRef]); const insertYamlContent = React.useCallback( (id: string = 'default', yamlContent: string = '', kind) => { const yaml = sanitizeYamlContent ? sanitizeYamlContent(id, yamlContent, kind) : yamlContent; - const selection = editor.getSelection(); + const selection = getEditor()?.getSelection(); const range = new Range( selection.startLineNumber, selection.startColumn, @@ -63,18 +64,18 @@ const CodeEditorSidebar: React.FC = ({ ); const op = { range, text: indentedText, forceMoveMarkers: true }; - editor.executeEdits(id, [op], [newContentSelection]); - editor.focus(); + getEditor()?.executeEdits(id, [op], [newContentSelection]); + getEditor()?.focus(); }, - [editor, sanitizeYamlContent], + [sanitizeYamlContent, getEditor], ); const replaceYamlContent = React.useCallback( (id: string = 'default', yamlContent: string = '', kind: string) => { const yaml = sanitizeYamlContent ? sanitizeYamlContent(id, yamlContent, kind) : yamlContent; - editor.setValue(yaml); + getEditor()?.setValue(yaml); }, - [editor, sanitizeYamlContent], + [sanitizeYamlContent, getEditor], ); const downloadYamlContent = React.useCallback( diff --git a/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts b/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts index 0e4e6794f90..5b1b193adfb 100644 --- a/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts +++ b/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts @@ -1,5 +1,5 @@ import { Range } from 'monaco-editor'; -import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; +import type * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; import { configureMonacoYaml } from 'monaco-yaml'; import * as yaml from 'yaml-ast-parser'; import { openAPItoJSONSchema } from '@console/internal/module/k8s/openapi-to-json-schema'; diff --git a/frontend/public/components/edit-yaml.jsx b/frontend/public/components/edit-yaml.jsx index 302e2c7955b..4bd5e07e9bc 100644 --- a/frontend/public/components/edit-yaml.jsx +++ b/frontend/public/components/edit-yaml.jsx @@ -124,6 +124,7 @@ const EditYAMLInner = (props) => { const [notAllowed, setNotAllowed] = React.useState(); const [displayResults, setDisplayResults] = React.useState(); const [resourceObjects, setResourceObjects] = React.useState(); + const [editorMounted, setEditorMounted] = React.useState(false); const [callbackCommand, setCallbackCommand] = React.useState(''); const [showReplaceCodeModal, setShowReplaceCodeModal] = React.useState(false); @@ -144,9 +145,8 @@ const EditYAMLInner = (props) => { const displayedVersion = React.useRef('0'); const onCancel = 'onCancel' in props ? props.onCancel : navigateBack; - const getEditor = () => { - return monacoRef.current?.editor; - }; + /** @return {import('monaco-editor').editor.IStandaloneCodeEditor | null} */ + const getEditor = () => monacoRef.current?.getEditor(); const getModel = React.useCallback( (obj) => { @@ -208,16 +208,14 @@ const EditYAMLInner = (props) => { .then((resp) => { const notAll = !resp.status.allowed; setNotAllowed(notAll); - if (monacoRef.current) { - monacoRef.current.editor?.updateOptions({ readOnly: notAll }); - } + editorMounted && getEditor()?.updateOptions({ readOnly: notAll }); }) .catch((e) => { // eslint-disable-next-line no-console console.warn('Error while check edit access', e); }); }, - [props.readOnly, props.impersonate, create, getModel], + [props.readOnly, props.impersonate, create, getModel, editorMounted], ); const appendYAMLString = React.useCallback((yaml) => { @@ -268,11 +266,11 @@ const EditYAMLInner = (props) => { const yaml = convertObjToYAMLString(obj); displayedVersion.current = _.get(obj, 'metadata.resourceVersion'); - getEditor()?.setValue(yaml); + editorMounted && getEditor()?.setValue(yaml); setInitialized(true); setStale(false); }, - [convertObjToYAMLString, initialized, props.obj], + [convertObjToYAMLString, initialized, props.obj, editorMounted], ); const handleCodeReplace = (_event) => { @@ -305,7 +303,7 @@ const EditYAMLInner = (props) => { return; } - const currentYAML = getEditor()?.getValue(); + const currentYAML = editorMounted && getEditor()?.getValue(); if (_.isEmpty(currentYAML) || currentYAML === olsCode) { getEditor()?.setValue(olsCodeBlock?.value); @@ -319,7 +317,7 @@ const EditYAMLInner = (props) => { setOLSCode(olsCodeBlock?.value); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [olsCodeBlock, initialized, isCodeImportRedirect]); + }, [olsCodeBlock, initialized, isCodeImportRedirect, editorMounted]); const handleError = (err, value = null) => { setSuccess(value); @@ -361,9 +359,9 @@ const EditYAMLInner = (props) => { if (props.error) { handleError(props.error); } - loadYaml(); + editorMounted && loadYaml(); loadCSVs(); - }, [loadCSVs, loadYaml, props.error]); + }, [loadCSVs, loadYaml, props.error, editorMounted]); const prevProps = React.useRef(props); @@ -378,13 +376,14 @@ const EditYAMLInner = (props) => { setStale(s); handleError(props.error, success); if (props.sampleObj) { - loadYaml(!_.isEqual(sampleObj, props.sampleObj), props.sampleObj); + editorMounted && loadYaml(!_.isEqual(sampleObj, props.sampleObj), props.sampleObj); } else if (props.fileUpload) { - loadYaml(!_.isEqual(prevProps.current.fileUpload, props.fileUpload), props.fileUpload); + editorMounted && + loadYaml(!_.isEqual(prevProps.current.fileUpload, props.fileUpload), props.fileUpload); } else { - loadYaml(); + editorMounted && loadYaml(); } - }, [props, isOver, loadYaml, sampleObj, success]); + }, [props, isOver, loadYaml, sampleObj, success, editorMounted]); const reload = () => { loadYaml(true); @@ -507,12 +506,12 @@ const EditYAMLInner = (props) => { let obj; if (onSave) { - onSave(getEditor().getValue()); + onSave(editorMounted && getEditor()?.getValue()); return; } try { - obj = safeLoad(getEditor().getValue()); + obj = safeLoad(editorMounted && getEditor()?.getValue()); } catch (e) { handleError(t('public~Error parsing YAML: {{e}}', { e })); return; @@ -581,7 +580,7 @@ const EditYAMLInner = (props) => { } } updateYAML(obj); - }, [create, owner, t, updateYAML, validate, onSave, props.obj]); + }, [create, owner, t, updateYAML, validate, onSave, props.obj, editorMounted]); const save = () => { setErrors([]); @@ -594,7 +593,7 @@ const EditYAMLInner = (props) => { let hasErrors = false; try { - objs = safeLoadAll(getEditor().getValue()).filter((obj) => obj); + objs = safeLoadAll(editorMounted && getEditor()?.getValue()).filter((obj) => obj); } catch (e) { handleError(t('public~Error parsing YAML: {{e}}', { e })); return; @@ -646,7 +645,7 @@ const EditYAMLInner = (props) => { setResourceObjects(objs); setDisplay(true); } - }, [t, setDisplay, validate]); + }, [t, setDisplay, validate, editorMounted]); const saveAll = () => { setErrors([]); @@ -709,6 +708,10 @@ const EditYAMLInner = (props) => { return sanitizedYaml; }; + React.useEffect(() => { + editorMounted && getEditor()?.updateOptions({ hover: { enabled: showTooltips } }); + }, [showTooltips, editorMounted]); + if (!create && !props.obj) { return ; } @@ -717,8 +720,6 @@ const EditYAMLInner = (props) => { 'co-file-dropzone--drop-over': isOver, }); - monacoRef.current?.editor?.updateOptions({ hover: showTooltips }); - if (displayResults) { return ( { toolbarLinks={sidebarLink ? [tooltipCheckBox, sidebarLink] : [tooltipCheckBox]} onChange={onChange} onSave={() => (allowMultiple ? saveAll() : save())} + onEditorDidMount={() => setEditorMounted(true)} />
{customAlerts} From 1472a15070f736bafb3f7b7cab3a7ef770486ebe Mon Sep 17 00:00:00 2001 From: logonoff Date: Fri, 3 Jan 2025 10:04:51 -0500 Subject: [PATCH 03/13] CONSOLE-4407: Use @monaco-editor/react instead of react-monaco-editor e --- frontend/package.json | 3 +- .../src/components/editor/CodeEditor.tsx | 28 +++++++++---------- .../components/editor/yaml-editor-utils.ts | 12 ++++---- .../support/pageObjects/add-flow-po.ts | 2 +- .../buildconfig/sections/EditorField.tsx | 10 +++---- ...ce-from-deployment-or-deployment-config.ts | 10 ++----- .../support/page-objects/pipelines-po.ts | 2 +- .../sidebars/resource-sidebar-samples.tsx | 6 ++-- frontend/yarn.lock | 15 +++------- 9 files changed, 37 insertions(+), 51 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 55b12cbcbc3..abd4cc81d5c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -198,7 +198,6 @@ "react-linkify": "^0.2.2", "react-measure": "^2.2.6", "react-modal": "^3.12.1", - "react-monaco-editor": "^0.56.2", "react-redux": "7.2.2", "react-router": "5.3.x", "react-router-dom": "5.3.x", @@ -304,7 +303,7 @@ "mochawesome-merge": "^4.1.0", "mochawesome-report-generator": "^5.1.0", "mock-socket": "^9.0.3", - "monaco-editor": "^0.36.1", + "monaco-editor": "^0.52.2", "monaco-editor-webpack-plugin": "^7.1.0", "prettier": "2.0.5", "react-refresh": "^0.10.0", diff --git a/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx b/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx index 8f042ebabb2..f95c396b826 100644 --- a/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx +++ b/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; +import Editor, { OnMount } from '@monaco-editor/react'; import type { editor } from 'monaco-editor/esm/vs/editor/editor.api'; import Measure from 'react-measure'; -import MonacoEditor, { EditorDidMount } from 'react-monaco-editor'; import { CodeEditorRef, CodeEditorProps } from '@console/dynamic-plugin-sdk'; import { ThemeContext } from '@console/internal/components/ThemeProvider'; import CodeEditorToolbar from './CodeEditorToolbar'; @@ -25,7 +25,7 @@ const CodeEditor = React.forwardRef((props, ref) const theme = React.useContext(ThemeContext); const [editorRef, setEditorRef] = React.useState(null); const [usesValue] = React.useState(value !== undefined); - const editorDidMount: EditorDidMount = React.useCallback( + const editorDidMount: OnMount = React.useCallback( (editor, monaco) => { setEditorRef(editor); const currentLanguage = editor.getModel()?.getLanguageId(); @@ -57,7 +57,7 @@ const CodeEditor = React.forwardRef((props, ref) }; }, [options, showMiniMap]); - // use the ref of the editor to expose it to the parent component + // expose the editor instance to the parent component via ref React.useImperativeHandle( ref, () => ({ @@ -72,18 +72,16 @@ const CodeEditor = React.forwardRef((props, ref) {({ measureRef, contentRect }) => (
-
- -
+
)}
diff --git a/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts b/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts index 5b1b193adfb..3e9c704a2b2 100644 --- a/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts +++ b/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts @@ -96,15 +96,15 @@ export const registerYAMLinMonaco = ( alreadyInUse: boolean = false, ) => { /** - * This exists because react-monaco-editor passes the same monaco - * object each time. Without it you would be registering all the features again and - * getting duplicate results. + * This exists because we enabled globalAPI in the webpack config. This means that the + * the monaco instance may have already been setup with the YAML language features. + * Otherwise, you would register all the features again, getting duplicate results. * - * Monaco does not provide any apis for unregistering or checking if the features have already + * Monaco does not provide any APIs for unregistering or checking if the features have already * been registered for a language. * - * We check that > 1 YAML language exists because one is the default and one is the initial register - * that setups our features. + * We check that > 1 YAML language exists because one is the default and + * the other is the initial register that setups our features. */ if (monacoInstance.languages.getLanguages().filter((x) => x.id === 'yaml').length <= 1) { // Prepare the schema diff --git a/frontend/packages/dev-console/integration-tests/support/pageObjects/add-flow-po.ts b/frontend/packages/dev-console/integration-tests/support/pageObjects/add-flow-po.ts index bd290d32f7e..ba425ac23ba 100644 --- a/frontend/packages/dev-console/integration-tests/support/pageObjects/add-flow-po.ts +++ b/frontend/packages/dev-console/integration-tests/support/pageObjects/add-flow-po.ts @@ -390,7 +390,7 @@ export const channelPO = { }; export const yamlPO = { - yamlEditor: '.react-monaco-editor-container', + yamlEditor: '.ocs-yaml-editor', }; export const uploadJarFilePO = { diff --git a/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx b/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx index 4a328f8793e..77f5cf6f502 100644 --- a/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx +++ b/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; +import Editor, { OnChange, EditorProps } from '@monaco-editor/react'; import { FormGroup, FormHelperText, HelperText, HelperTextItem } from '@patternfly/react-core'; import { FormikValues, useFormikContext } from 'formik'; -import MonacoEditor, { ChangeHandler, MonacoEditorProps } from 'react-monaco-editor'; import { ThemeContext } from '@console/internal/components/ThemeProvider'; import { RedExclamationCircleIcon, useDebounceCallback } from '@console/shared/src'; @@ -11,7 +11,7 @@ type EditorFieldProps = { helpText?: React.ReactNode; required?: boolean; isDisabled?: boolean; -} & MonacoEditorProps; +} & EditorProps; const EditorField: React.FC = ({ name, @@ -26,7 +26,7 @@ const EditorField: React.FC = ({ const { getFieldMeta, setFieldValue, setFieldTouched } = useFormikContext(); const { error, value } = getFieldMeta(name); - const debouncedOnChange = useDebounceCallback((newValue, event) => { + const debouncedOnChange = useDebounceCallback((newValue, event) => { if (onChange) { onChange(newValue, event); } @@ -36,11 +36,11 @@ const EditorField: React.FC = ({ return ( - diff --git a/frontend/packages/knative-plugin/integration-tests/support/step-definitions/serverless/create-knative-service-from-deployment-or-deployment-config.ts b/frontend/packages/knative-plugin/integration-tests/support/step-definitions/serverless/create-knative-service-from-deployment-or-deployment-config.ts index 29f4be684c6..04f2f1d47ad 100644 --- a/frontend/packages/knative-plugin/integration-tests/support/step-definitions/serverless/create-knative-service-from-deployment-or-deployment-config.ts +++ b/frontend/packages/knative-plugin/integration-tests/support/step-definitions/serverless/create-knative-service-from-deployment-or-deployment-config.ts @@ -213,18 +213,14 @@ When( ); Then('user is able to see value of {string} as {string}', (util: string, utilValue: string) => { - cy.get('.ocs-yaml-editor__wrapper').contains(`${util}: '${utilValue}'`).should('be.visible'); + cy.get('.ocs-yaml-editor').contains(`${util}: '${utilValue}'`).should('be.visible'); }); Then( 'user is able to see the value of {string} and {string} as {string} and {string} percent respectively', (maxScale: string, minScale: string, minScaleValue: string, maxScaleValue: string) => { - cy.get('.ocs-yaml-editor__wrapper') - .contains(`${maxScale}: '${maxScaleValue}'`) - .should('be.visible'); + cy.get('.ocs-yaml-editor').contains(`${maxScale}: '${maxScaleValue}'`).should('be.visible'); - cy.get('.ocs-yaml-editor__wrapper') - .contains(`${minScale}: '${minScaleValue}'`) - .should('be.visible'); + cy.get('.ocs-yaml-editor').contains(`${minScale}: '${minScaleValue}'`).should('be.visible'); }, ); diff --git a/frontend/packages/pipelines-plugin/integration-tests/support/page-objects/pipelines-po.ts b/frontend/packages/pipelines-plugin/integration-tests/support/page-objects/pipelines-po.ts index fcc77cf9441..f1c336b01e3 100644 --- a/frontend/packages/pipelines-plugin/integration-tests/support/page-objects/pipelines-po.ts +++ b/frontend/packages/pipelines-plugin/integration-tests/support/page-objects/pipelines-po.ts @@ -57,7 +57,7 @@ export const pipelineBuilderPO = { }, yamlView: { switchToYAMLView: '[id="form-radiobutton-editorType-yaml-field"]', - editor: 'div.react-monaco-editor-container', + editor: 'div.ocs-yaml-editor', yamlEditor: 'div.monaco-scrollable-element.editor-scrollable.vs-dark', sideBar: '[data-test="resource-sidebar"]', createButton: '[data-test-id="submit-button"]', diff --git a/frontend/public/components/sidebars/resource-sidebar-samples.tsx b/frontend/public/components/sidebars/resource-sidebar-samples.tsx index 77aa398ae76..84a3467b84f 100644 --- a/frontend/public/components/sidebars/resource-sidebar-samples.tsx +++ b/frontend/public/components/sidebars/resource-sidebar-samples.tsx @@ -1,7 +1,7 @@ import * as _ from 'lodash-es'; import * as React from 'react'; import { Button, Level, LevelItem, Title } from '@patternfly/react-core'; -import MonacoEditor from 'react-monaco-editor'; +import Editor from '@monaco-editor/react'; import { ThemeContext } from '@console/internal/components/ThemeProvider'; import { ChevronDownIcon } from '@patternfly/react-icons/dist/esm/icons/chevron-down-icon'; import { ChevronRightIcon } from '@patternfly/react-icons/dist/esm/icons/chevron-right-icon'; @@ -64,11 +64,11 @@ const PreviewYAML = ({ maxPreviewLines = 20, yaml }) => { return (
- Date: Fri, 3 Jan 2025 11:59:42 -0500 Subject: [PATCH 04/13] CONSOLE-4407: Restore custom monaco theme --- frontend/package.json | 2 +- .../src/extensions/console-types.ts | 9 ++- .../src/components/editor/CodeEditor.tsx | 16 +++-- .../src/components/editor/theme.ts | 65 +++++++++++++++++++ .../buildconfig/sections/EditorField.tsx | 11 ++-- .../sidebars/resource-sidebar-samples.tsx | 9 +-- frontend/yarn.lock | 8 +-- 7 files changed, 99 insertions(+), 21 deletions(-) create mode 100644 frontend/packages/console-shared/src/components/editor/theme.ts diff --git a/frontend/package.json b/frontend/package.json index abd4cc81d5c..3e3f423d7c7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -303,7 +303,7 @@ "mochawesome-merge": "^4.1.0", "mochawesome-report-generator": "^5.1.0", "mock-socket": "^9.0.3", - "monaco-editor": "^0.52.2", + "monaco-editor": "^0.36.1", "monaco-editor-webpack-plugin": "^7.1.0", "prettier": "2.0.5", "react-refresh": "^0.10.0", diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts b/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts index 9953e17242d..bdcc6012ccf 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts @@ -3,7 +3,7 @@ import { QuickStartContextValues } from '@patternfly/quickstarts'; import { ButtonProps } from '@patternfly/react-core'; import { ICell, OnSelect, SortByDirection, TableGridBreakpoint } from '@patternfly/react-table'; import { LocationDescriptor } from 'history'; -import type { editor } from 'monaco-editor/esm/vs/editor/editor.api'; +import type * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; import { ExtensionK8sGroupKindModel, K8sModel, @@ -644,10 +644,13 @@ export type CodeEditorProps = { toolbarLinks?: React.ReactNodeArray; onChange?: (newValue, event) => void; onSave?: () => void; - onEditorDidMount?: (editor: editor.IStandaloneCodeEditor) => void; + onEditorDidMount?: (editor: monaco.editor.IStandaloneCodeEditor) => void; }; -export type CodeEditorRef = { getEditor: () => editor.IStandaloneCodeEditor }; +export type CodeEditorRef = { + getEditor: () => monaco.editor.IStandaloneCodeEditor; + getMonaco: () => typeof monaco; +}; export type ResourceYAMLEditorProps = { initialResource: string | { [key: string]: any }; diff --git a/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx b/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx index f95c396b826..905ba6e1bf1 100644 --- a/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx +++ b/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; import Editor, { OnMount } from '@monaco-editor/react'; -import type { editor } from 'monaco-editor/esm/vs/editor/editor.api'; +import type * as Monaco from 'monaco-editor/esm/vs/editor/editor.api'; import Measure from 'react-measure'; import { CodeEditorRef, CodeEditorProps } from '@console/dynamic-plugin-sdk'; -import { ThemeContext } from '@console/internal/components/ThemeProvider'; import CodeEditorToolbar from './CodeEditorToolbar'; +import { useConsoleMonacoTheme } from './theme'; import { registerYAMLinMonaco, defaultEditorOptions } from './yaml-editor-utils'; import './CodeEditor.scss'; @@ -22,12 +22,17 @@ const CodeEditor = React.forwardRef((props, ref) onEditorDidMount, } = props; - const theme = React.useContext(ThemeContext); - const [editorRef, setEditorRef] = React.useState(null); + const [editorRef, setEditorRef] = React.useState( + null, + ); + const [monacoRef, setMonacoRef] = React.useState(null); + useConsoleMonacoTheme(monacoRef?.editor); + const [usesValue] = React.useState(value !== undefined); const editorDidMount: OnMount = React.useCallback( (editor, monaco) => { setEditorRef(editor); + setMonacoRef(monaco); const currentLanguage = editor.getModel()?.getLanguageId(); editor.layout(); editor.focus(); @@ -62,8 +67,9 @@ const CodeEditor = React.forwardRef((props, ref) ref, () => ({ getEditor: () => editorRef, + getMonaco: () => monacoRef, }), - [editorRef], + [editorRef, monacoRef], ); return ( diff --git a/frontend/packages/console-shared/src/components/editor/theme.ts b/frontend/packages/console-shared/src/components/editor/theme.ts new file mode 100644 index 00000000000..3ca72248cbc --- /dev/null +++ b/frontend/packages/console-shared/src/components/editor/theme.ts @@ -0,0 +1,65 @@ +import { useContext, useEffect, useState } from 'react'; +import type { editor as monacoEditor } from 'monaco-editor/esm/vs/editor/editor.api'; +import { ThemeContext } from '@console/internal/components/ThemeProvider'; + +/** + * Define the themes `console-light` and `console-dark` for an instance of Monaco editor. + */ +const defineThemes = (editor: typeof monacoEditor) => { + editor.defineTheme('console-light', { + base: 'vs', + inherit: true, + colors: { + 'editor.background': '#ffffff', // global_BackgroundColor_light_100 + 'editorGutter.background': '#f5f5f5', // black-150 + 'editorLineNumber.activeForeground': '#151515', // global_Color_dark_100 + 'editorLineNumber.foreground': '#3c3f42', // global_BackgroundColor_dark_200 + }, + rules: [ + { token: 'number', foreground: '486b00' }, // light-green-600 + { token: 'type', foreground: '795600' }, // gold-500 + { token: 'string', foreground: '004080' }, // blue-600 + { token: 'keyword', foreground: '40199a' }, // purple-600 + ], + }); + + editor.defineTheme('console-dark', { + base: 'vs-dark', + inherit: true, + colors: { + 'editor.background': '#151515', // global_BackgroundColor_dark_100 + 'editorGutter.background': '#292e34', // no pf token defined + 'editorLineNumber.activeForeground': '#ffffff', // global_Color_light_100 + 'editorLineNumber.foreground': '#f0f0f0', // global_BackgroundColor_200 + }, + rules: [ + { token: 'number', foreground: 'ace12e' }, // light-green-600 + { token: 'type', foreground: '73bcf7' }, // blue-200 + { token: 'string', foreground: 'f0ab00' }, // gold-400 + { token: 'keyword', foreground: 'cbc1ff' }, // purple-100 + ], + }); +}; + +/** + * Sets the theme of a provided Monaco editor instance based on the current theme. + */ +export const useConsoleMonacoTheme = (editor: typeof monacoEditor | null) => { + const theme = useContext(ThemeContext); + const [themeLoaded, setThemeLoaded] = useState(false); + + useEffect(() => { + if (editor) { + if (!themeLoaded) { + defineThemes(editor); + setThemeLoaded(true); + } + + if (theme === 'light') { + editor.setTheme('console-light'); + } else { + editor.setTheme('console-dark'); + } + } + }, [theme, editor, themeLoaded]); +}; diff --git a/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx b/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx index 77f5cf6f502..7ecd9414f7b 100644 --- a/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx +++ b/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx @@ -1,9 +1,9 @@ import * as React from 'react'; -import Editor, { OnChange, EditorProps } from '@monaco-editor/react'; +import Editor, { OnChange, EditorProps, Monaco } from '@monaco-editor/react'; import { FormGroup, FormHelperText, HelperText, HelperTextItem } from '@patternfly/react-core'; import { FormikValues, useFormikContext } from 'formik'; -import { ThemeContext } from '@console/internal/components/ThemeProvider'; import { RedExclamationCircleIcon, useDebounceCallback } from '@console/shared/src'; +import { useConsoleMonacoTheme } from '@console/shared/src/components/editor/theme'; type EditorFieldProps = { name: string; @@ -22,9 +22,10 @@ const EditorField: React.FC = ({ onChange, ...otherProps }) => { - const theme = React.useContext(ThemeContext); const { getFieldMeta, setFieldValue, setFieldTouched } = useFormikContext(); const { error, value } = getFieldMeta(name); + const [monaco, setMonaco] = React.useState(null); + useConsoleMonacoTheme(monaco?.editor); const debouncedOnChange = useDebounceCallback((newValue, event) => { if (onChange) { @@ -40,7 +41,9 @@ const EditorField: React.FC = ({ {...otherProps} value={value} onChange={debouncedOnChange} - theme={theme === 'light' ? 'vs-light' : 'vs-dark'} + onMount={(_e, m) => { + setMonaco(m); + }} /> diff --git a/frontend/public/components/sidebars/resource-sidebar-samples.tsx b/frontend/public/components/sidebars/resource-sidebar-samples.tsx index 84a3467b84f..8add9c8a1e1 100644 --- a/frontend/public/components/sidebars/resource-sidebar-samples.tsx +++ b/frontend/public/components/sidebars/resource-sidebar-samples.tsx @@ -1,8 +1,8 @@ import * as _ from 'lodash-es'; import * as React from 'react'; import { Button, Level, LevelItem, Title } from '@patternfly/react-core'; -import Editor from '@monaco-editor/react'; -import { ThemeContext } from '@console/internal/components/ThemeProvider'; +import Editor, { Monaco } from '@monaco-editor/react'; +import { useConsoleMonacoTheme } from '@console/shared/src/components/editor/theme'; import { ChevronDownIcon } from '@patternfly/react-icons/dist/esm/icons/chevron-down-icon'; import { ChevronRightIcon } from '@patternfly/react-icons/dist/esm/icons/chevron-right-icon'; import { DownloadIcon } from '@patternfly/react-icons/dist/esm/icons/download-icon'; @@ -60,7 +60,8 @@ const ResourceSidebarSample: React.FC = ({ const lineHeight = 18; const PreviewYAML = ({ maxPreviewLines = 20, yaml }) => { - const theme = React.useContext(ThemeContext); + const [monaco, setMonaco] = React.useState(null); + useConsoleMonacoTheme(monaco?.editor); return (
@@ -68,7 +69,7 @@ const PreviewYAML = ({ maxPreviewLines = 20, yaml }) => { height={Math.min(yaml.split('\n').length, maxPreviewLines) * lineHeight} language="yaml" value={yaml} - theme={theme === 'light' ? 'vs-light' : 'vs-dark'} + onMount={(_e, m) => setMonaco(m)} options={{ lineHeight, readOnly: true, diff --git a/frontend/yarn.lock b/frontend/yarn.lock index e9bc864c118..eb73225d73f 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -12382,10 +12382,10 @@ monaco-editor-webpack-plugin@^7.1.0: dependencies: loader-utils "^2.0.2" -monaco-editor@^0.52.2: - version "0.52.2" - resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.52.2.tgz#53c75a6fcc6802684e99fd1b2700299857002205" - integrity sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ== +monaco-editor@^0.36.1: + version "0.36.1" + resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.36.1.tgz#aad528c815605307473a1634612946921d8079b5" + integrity sha512-/CaclMHKQ3A6rnzBzOADfwdSJ25BFoFT0Emxsc4zYVyav5SkK9iA6lEtIeuN/oRYbwPgviJT+t3l+sjFa28jYg== monaco-languageserver-types@^0.4.0: version "0.4.0" From 960e1ba0ee5f0dcf2646d69fcb3e0b41223f3dcd Mon Sep 17 00:00:00 2001 From: logonoff Date: Tue, 7 Jan 2025 16:17:48 -0500 Subject: [PATCH 05/13] CONSOLE-4407: maintain backwards compatibility with ref object --- .../packages/console-dynamic-plugin-sdk/README.md | 1 + .../src/extensions/console-types.ts | 4 ++-- .../src/components/editor/CodeEditor.tsx | 4 ++-- .../src/components/editor/CodeEditorSidebar.tsx | 14 +++++++------- .../src/components/editor/yaml-editor-utils.ts | 5 ++--- frontend/public/components/edit-yaml.jsx | 6 ++++-- 6 files changed, 18 insertions(+), 16 deletions(-) diff --git a/frontend/packages/console-dynamic-plugin-sdk/README.md b/frontend/packages/console-dynamic-plugin-sdk/README.md index a4f8c090fd4..4c5136d595a 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/README.md +++ b/frontend/packages/console-dynamic-plugin-sdk/README.md @@ -159,6 +159,7 @@ This section documents notable changes in the Console provided shared modules ac - Removed `@fortawesome/font-awesome` and `openshift-logos-icon`. Plugins should use PatternFly icons from `@patternfly/react-icons` instead. The `fa-spin` class remains but is deprecated and will be removed in the future. Plugins should provide their own CSS to spin icons if needed. +- Upgraded `monaco-editor` to version `0.36.1`. - Removed PatternFly 4.x shared modules. - Upgraded PatternFly to v6. - Removed styling for generic HTML heading elements (e.g., `

`). Use PatternFly components to achieve correct styling. diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts b/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts index bdcc6012ccf..50f2314e927 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts @@ -648,8 +648,8 @@ export type CodeEditorProps = { }; export type CodeEditorRef = { - getEditor: () => monaco.editor.IStandaloneCodeEditor; - getMonaco: () => typeof monaco; + editor: monaco.editor.IStandaloneCodeEditor; + monaco: typeof monaco; }; export type ResourceYAMLEditorProps = { diff --git a/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx b/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx index 905ba6e1bf1..b4cecfa32cc 100644 --- a/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx +++ b/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx @@ -66,8 +66,8 @@ const CodeEditor = React.forwardRef((props, ref) React.useImperativeHandle( ref, () => ({ - getEditor: () => editorRef, - getMonaco: () => monacoRef, + editor: editorRef, + monaco: monacoRef, }), [editorRef, monacoRef], ); diff --git a/frontend/packages/console-shared/src/components/editor/CodeEditorSidebar.tsx b/frontend/packages/console-shared/src/components/editor/CodeEditorSidebar.tsx index 9fb3df0e86f..2382c941233 100644 --- a/frontend/packages/console-shared/src/components/editor/CodeEditorSidebar.tsx +++ b/frontend/packages/console-shared/src/components/editor/CodeEditorSidebar.tsx @@ -28,13 +28,13 @@ const CodeEditorSidebar: React.FC = ({ sanitizeYamlContent, toggleSidebar, }) => { - const getEditor = React.useCallback(() => editorRef?.current?.getEditor(), [editorRef]); + const editor = editorRef.current?.editor; const insertYamlContent = React.useCallback( (id: string = 'default', yamlContent: string = '', kind) => { const yaml = sanitizeYamlContent ? sanitizeYamlContent(id, yamlContent, kind) : yamlContent; - const selection = getEditor()?.getSelection(); + const selection = editor?.getSelection(); const range = new Range( selection.startLineNumber, selection.startColumn, @@ -64,18 +64,18 @@ const CodeEditorSidebar: React.FC = ({ ); const op = { range, text: indentedText, forceMoveMarkers: true }; - getEditor()?.executeEdits(id, [op], [newContentSelection]); - getEditor()?.focus(); + editor?.executeEdits(id, [op], [newContentSelection]); + editor?.focus(); }, - [sanitizeYamlContent, getEditor], + [sanitizeYamlContent, editor], ); const replaceYamlContent = React.useCallback( (id: string = 'default', yamlContent: string = '', kind: string) => { const yaml = sanitizeYamlContent ? sanitizeYamlContent(id, yamlContent, kind) : yamlContent; - getEditor()?.setValue(yaml); + editor?.setValue(yaml); }, - [sanitizeYamlContent, getEditor], + [sanitizeYamlContent, editor], ); const downloadYamlContent = React.useCallback( diff --git a/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts b/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts index 3e9c704a2b2..e147edd76a5 100644 --- a/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts +++ b/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts @@ -70,10 +70,10 @@ export const fold = ( */ export const registerAutoFold = ( editor: monaco.editor.IStandaloneCodeEditor, - model: monaco.editor.ITextModel, alreadyInUse: boolean = false, ) => { let initialFoldingTriggered = false; + const model = editor.getModel(); const tryFolding = () => { const document = model.getValue(); if (!initialFoldingTriggered && document !== '') { @@ -131,7 +131,6 @@ export const registerYAMLinMonaco = ( } if (!alreadyInUse) { - const model = editor.getModel(); - registerAutoFold(editor, model); + registerAutoFold(editor); } }; diff --git a/frontend/public/components/edit-yaml.jsx b/frontend/public/components/edit-yaml.jsx index 4bd5e07e9bc..9503b9660ce 100644 --- a/frontend/public/components/edit-yaml.jsx +++ b/frontend/public/components/edit-yaml.jsx @@ -146,7 +146,9 @@ const EditYAMLInner = (props) => { const onCancel = 'onCancel' in props ? props.onCancel : navigateBack; /** @return {import('monaco-editor').editor.IStandaloneCodeEditor | null} */ - const getEditor = () => monacoRef.current?.getEditor(); + const getEditor = () => { + return monacoRef.current?.editor; + }; const getModel = React.useCallback( (obj) => { @@ -372,7 +374,7 @@ const EditYAMLInner = (props) => { } const newVersion = _.get(props.obj, 'metadata.resourceVersion'); - const s = displayedVersion.current !== newVersion; + const s = displayedVersion.current !== newVersion && editorMounted; setStale(s); handleError(props.error, success); if (props.sampleObj) { From f0984bc63092e3908606d5e8ed8e0cf2815e3f9c Mon Sep 17 00:00:00 2001 From: logonoff Date: Wed, 5 Feb 2025 16:25:43 -0500 Subject: [PATCH 06/13] CONSOLE-4407: remove `net` fallback --- frontend/webpack.config.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/frontend/webpack.config.ts b/frontend/webpack.config.ts index ddc1142b1f5..c6dfe06764d 100644 --- a/frontend/webpack.config.ts +++ b/frontend/webpack.config.ts @@ -95,9 +95,6 @@ const config: Configuration = { prettier: false, 'prettier/parser-yaml': false, }, - fallback: { - net: false, // for YAML language server - }, }, node: { global: true, // see https://github.com/browserify/randombytes/issues/36 From a2271fc8a2ab0d5373e88c0bfd2dd3c54ed06c63 Mon Sep 17 00:00:00 2001 From: logonoff Date: Fri, 7 Feb 2025 11:33:25 -0500 Subject: [PATCH 07/13] CONSOLE-4407: Use PF6 tokens for editor theme also verified that these new colors meet WCAG AAA standards for color contrast --- .../src/components/editor/theme.ts | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/frontend/packages/console-shared/src/components/editor/theme.ts b/frontend/packages/console-shared/src/components/editor/theme.ts index 3ca72248cbc..5dabd23d8c4 100644 --- a/frontend/packages/console-shared/src/components/editor/theme.ts +++ b/frontend/packages/console-shared/src/components/editor/theme.ts @@ -1,4 +1,19 @@ import { useContext, useEffect, useState } from 'react'; +import { + t_color_green_70, + t_color_yellow_70, + t_color_blue_70, + t_color_purple_70, + t_color_green_30, + t_color_blue_30, + t_color_yellow_30, + t_color_purple_30, + t_color_white, + t_color_gray_20, + t_color_gray_60, + t_color_gray_90, + t_color_black, +} from '@patternfly/react-tokens'; import type { editor as monacoEditor } from 'monaco-editor/esm/vs/editor/editor.api'; import { ThemeContext } from '@console/internal/components/ThemeProvider'; @@ -10,16 +25,15 @@ const defineThemes = (editor: typeof monacoEditor) => { base: 'vs', inherit: true, colors: { - 'editor.background': '#ffffff', // global_BackgroundColor_light_100 - 'editorGutter.background': '#f5f5f5', // black-150 - 'editorLineNumber.activeForeground': '#151515', // global_Color_dark_100 - 'editorLineNumber.foreground': '#3c3f42', // global_BackgroundColor_dark_200 + 'editor.background': t_color_white.value, + 'editorLineNumber.activeForeground': t_color_black.value, + 'editorLineNumber.foreground': t_color_gray_60.value, }, rules: [ - { token: 'number', foreground: '486b00' }, // light-green-600 - { token: 'type', foreground: '795600' }, // gold-500 - { token: 'string', foreground: '004080' }, // blue-600 - { token: 'keyword', foreground: '40199a' }, // purple-600 + { token: 'number', foreground: t_color_green_70.value }, + { token: 'type', foreground: t_color_yellow_70.value }, + { token: 'string', foreground: t_color_blue_70.value }, + { token: 'keyword', foreground: t_color_purple_70.value }, ], }); @@ -27,16 +41,15 @@ const defineThemes = (editor: typeof monacoEditor) => { base: 'vs-dark', inherit: true, colors: { - 'editor.background': '#151515', // global_BackgroundColor_dark_100 - 'editorGutter.background': '#292e34', // no pf token defined - 'editorLineNumber.activeForeground': '#ffffff', // global_Color_light_100 - 'editorLineNumber.foreground': '#f0f0f0', // global_BackgroundColor_200 + 'editor.background': t_color_gray_90.value, + 'editorLineNumber.activeForeground': t_color_white.value, + 'editorLineNumber.foreground': t_color_gray_20.value, }, rules: [ - { token: 'number', foreground: 'ace12e' }, // light-green-600 - { token: 'type', foreground: '73bcf7' }, // blue-200 - { token: 'string', foreground: 'f0ab00' }, // gold-400 - { token: 'keyword', foreground: 'cbc1ff' }, // purple-100 + { token: 'number', foreground: t_color_green_30.value }, + { token: 'type', foreground: t_color_blue_30.value }, + { token: 'string', foreground: t_color_yellow_30.value }, + { token: 'keyword', foreground: t_color_purple_30.value }, ], }); }; From 1b226ec1f46419ef218c8ec59450b44a90fc733b Mon Sep 17 00:00:00 2001 From: logonoff Date: Fri, 7 Feb 2025 15:15:04 -0500 Subject: [PATCH 08/13] CONSOLE-4409: Refactor CodeEditor to use the PF equivalent --- frontend/package.json | 2 +- .../src/extensions/console-types.ts | 6 +- .../locales/en/console-shared.json | 5 +- .../src/components/editor/CodeEditor.scss | 48 +++++++++-- .../src/components/editor/CodeEditor.tsx | 61 +++++++++----- .../components/editor/CodeEditorToolbar.scss | 16 ---- .../components/editor/CodeEditorToolbar.tsx | 79 +++++++----------- .../src/components/editor/ShortcutsLink.tsx | 83 +++++++------------ .../__tests__/CodeEditorToolbar.spec.tsx | 6 -- .../src/components/editor/theme.ts | 28 ++++++- .../formik-fields/CodeEditorField.tsx | 1 - .../buildconfig/sections/EditorField.tsx | 24 ++++-- .../buildconfig/sections/HooksSection.tsx | 4 - .../buildconfig/sections/SourceSection.tsx | 9 +- .../views/yaml-editor.ts | 22 +++-- frontend/public/components/_edit-yaml.scss | 16 ---- frontend/public/components/edit-yaml.jsx | 10 +-- .../sidebars/resource-sidebar-samples.tsx | 13 +-- frontend/public/locales/en/public.json | 2 +- frontend/scripts/check-patternfly-modules.sh | 1 + frontend/yarn.lock | 20 ++++- 21 files changed, 239 insertions(+), 217 deletions(-) delete mode 100644 frontend/packages/console-shared/src/components/editor/CodeEditorToolbar.scss diff --git a/frontend/package.json b/frontend/package.json index 3e3f423d7c7..a843e96f863 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -132,12 +132,12 @@ "resolver": "./jest-resolver.js" }, "dependencies": { - "@monaco-editor/react": "^4.6.0", "@patternfly-5/patternfly": "npm:@patternfly/patternfly@5.4.2", "@patternfly/patternfly": "^6.2.0-prerelease.2", "@patternfly/quickstarts": "^6.2.0-prerelease.4", "@patternfly/react-catalog-view-extension": "^6.1.0-prerelease.3", "@patternfly/react-charts": "^8.2.0-prerelease.13", + "@patternfly/react-code-editor": "^6.1.0", "@patternfly/react-component-groups": "^6.2.0-prerelease.4", "@patternfly/react-console": "^6.0.0", "@patternfly/react-core": "^6.2.0-prerelease.15", diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts b/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts index 50f2314e927..430198a4077 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts @@ -1,5 +1,6 @@ import * as React from 'react'; import { QuickStartContextValues } from '@patternfly/quickstarts'; +import { Language } from '@patternfly/react-code-editor'; import { ButtonProps } from '@patternfly/react-core'; import { ICell, OnSelect, SortByDirection, TableGridBreakpoint } from '@patternfly/react-table'; import { LocationDescriptor } from 'history'; @@ -636,7 +637,7 @@ export type UserInfo = { export type CodeEditorProps = { value?: string; - language?: string; + language?: Language; options?: object; minHeight?: string | number; showShortcuts?: boolean; @@ -645,6 +646,9 @@ export type CodeEditorProps = { onChange?: (newValue, event) => void; onSave?: () => void; onEditorDidMount?: (editor: monaco.editor.IStandaloneCodeEditor) => void; + isDownloadEnabled?: boolean; + isCopyEnabled?: boolean; + isLanguageLabelVisible?: boolean; }; export type CodeEditorRef = { diff --git a/frontend/packages/console-shared/locales/en/console-shared.json b/frontend/packages/console-shared/locales/en/console-shared.json index 6d30c3f5891..6c8ff950c35 100644 --- a/frontend/packages/console-shared/locales/en/console-shared.json +++ b/frontend/packages/console-shared/locales/en/console-shared.json @@ -108,6 +108,10 @@ "true": "true", "Select {{label}}": "Select {{label}}", "Cluster does not have resource {{groupVersionKind}}": "Cluster does not have resource {{groupVersionKind}}", + "View shortcuts": "View shortcuts", + "Copy code to clipboard": "Copy code to clipboard", + "Content copied to clipboard": "Content copied to clipboard", + "Download code": "Download code", "Ask OpenShift Lightspeed": "Ask OpenShift Lightspeed", "Accessibility help": "Accessibility help", "Shortcuts": "Shortcuts", @@ -117,7 +121,6 @@ "View document outline": "View document outline", "View property descriptions": "View property descriptions", "Save": "Save", - "View shortcuts": "View shortcuts", "Restricted access": "Restricted access", "You don't have access to this section due to cluster policy": "You don't have access to this section due to cluster policy", "Error details": "Error details", diff --git a/frontend/packages/console-shared/src/components/editor/CodeEditor.scss b/frontend/packages/console-shared/src/components/editor/CodeEditor.scss index 51a1c002fce..37d5eaabb78 100644 --- a/frontend/packages/console-shared/src/components/editor/CodeEditor.scss +++ b/frontend/packages/console-shared/src/components/editor/CodeEditor.scss @@ -1,14 +1,44 @@ +@import '../../../../../public/style/vars'; + +// match the token in theme.ts. it already matches for light theme +.pf-v6-theme-dark { + .pf-v6-c-code-editor__main { + --pf-v6-c-code-editor__main--BackgroundColor: var(--pf-t--color--gray--90); + } + + .pf-v6-c-code-editor__tab { + --pf-v6-c-code-editor__tab--BackgroundColor: var(--pf-t--color--gray--90); + } +} + .ocs-yaml-editor { - &__root { - flex: 1; - position: relative; + // makes automaticLayout work properly + .monaco-editor { + position: absolute !important; } - &__wrapper { - position: absolute; - top: 0; - right: 0; - left: 0; - bottom: 0; + .pf-v6-c-code-editor__controls, .ocs-yaml-editor-toolbar { + // needed to get the CodeEditorToolbar to place buttons on the right side + width: 100%; + height: 100% !important; + } + + .ocs-yaml-editor-toolbar { + align-items: center; + + .ocs-yaml-editor-toolbar__link { + @media (max-width: $screen-sm-max) { + display: none; + } + } + + .ocs-yaml-editor-toolbar__shortcuts { + padding: var(--pf-t--global--spacer--md) var(--pf-v6-global-gutter); + + .ocs-yaml-editor-shortcut__text { + color: var(--pf-t--global--text--color--subtle); + margin-left: var(--pf-t--global--spacer--sm); + } + } } } diff --git a/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx b/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx index b4cecfa32cc..21c7fc38f21 100644 --- a/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx +++ b/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx @@ -1,9 +1,10 @@ import * as React from 'react'; -import Editor, { OnMount } from '@monaco-editor/react'; +import { CodeEditor as PfEditor, Language } from '@patternfly/react-code-editor'; import type * as Monaco from 'monaco-editor/esm/vs/editor/editor.api'; -import Measure from 'react-measure'; +import { useTranslation } from 'react-i18next'; import { CodeEditorRef, CodeEditorProps } from '@console/dynamic-plugin-sdk'; import CodeEditorToolbar from './CodeEditorToolbar'; +import { useShortcutLink } from './ShortcutsLink'; import { useConsoleMonacoTheme } from './theme'; import { registerYAMLinMonaco, defaultEditorOptions } from './yaml-editor-utils'; import './CodeEditor.scss'; @@ -20,8 +21,12 @@ const CodeEditor = React.forwardRef((props, ref) onSave, language, onEditorDidMount, + isDownloadEnabled, + isCopyEnabled, + isLanguageLabelVisible, } = props; - + const { t } = useTranslation('console-shared'); + const shortcutPopover = useShortcutLink(); const [editorRef, setEditorRef] = React.useState( null, ); @@ -29,7 +34,7 @@ const CodeEditor = React.forwardRef((props, ref) useConsoleMonacoTheme(monacoRef?.editor); const [usesValue] = React.useState(value !== undefined); - const editorDidMount: OnMount = React.useCallback( + const editorDidMount = React.useCallback( (editor, monaco) => { setEditorRef(editor); setMonacoRef(monaco); @@ -59,6 +64,7 @@ const CodeEditor = React.forwardRef((props, ref) minimap: { enabled: showMiniMap, }, + automaticLayout: true, }; }, [options, showMiniMap]); @@ -72,26 +78,35 @@ const CodeEditor = React.forwardRef((props, ref) [editorRef, monacoRef], ); + const ToolbarLinks = React.useMemo(() => { + // fixes PF bug where empty toolbar renders if a component is null + if (!showShortcuts && !toolbarLinks?.length) return undefined; + + return ; + }, [toolbarLinks, showShortcuts]); + return ( - <> - - - {({ measureRef, contentRect }) => ( -
- -
- )} -
- + ); }); diff --git a/frontend/packages/console-shared/src/components/editor/CodeEditorToolbar.scss b/frontend/packages/console-shared/src/components/editor/CodeEditorToolbar.scss deleted file mode 100644 index 3e4ccd523e4..00000000000 --- a/frontend/packages/console-shared/src/components/editor/CodeEditorToolbar.scss +++ /dev/null @@ -1,16 +0,0 @@ -@import '../../../../../public/style/vars'; - -.ocs-yaml-editor-shortcut__text { - color: var(--pf-t--global--text--color--subtle); - margin-left: var(--pf-t--global--spacer--sm); -} - -.ocs-yaml-editor-toolbar { - align-items: center; -} - -@media (max-width: $screen-sm-max) { - .ocs-yaml-editor-toolbar__link { - display: none; - } -} diff --git a/frontend/packages/console-shared/src/components/editor/CodeEditorToolbar.tsx b/frontend/packages/console-shared/src/components/editor/CodeEditorToolbar.tsx index cdb8c79a2f0..be439ea830e 100644 --- a/frontend/packages/console-shared/src/components/editor/CodeEditorToolbar.tsx +++ b/frontend/packages/console-shared/src/components/editor/CodeEditorToolbar.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Button, Divider } from '@patternfly/react-core'; +import { Flex, FlexItem, Button, Divider } from '@patternfly/react-core'; import { MagicIcon } from '@patternfly/react-icons'; import { useTranslation } from 'react-i18next'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -9,9 +9,6 @@ import { action } from 'typesafe-actions'; import { ActionType } from '@console/internal/reducers/ols'; import { useOLSConfig } from '../../hooks/ols-hook'; import { isMac, ShortcutCommand } from '../shortcuts/Shortcut'; -import ShortcutsLink from './ShortcutsLink'; - -import './CodeEditorToolbar.scss'; interface CodeEditorToolbarProps { showShortcuts?: boolean; @@ -25,52 +22,28 @@ const CodeEditorToolbar: React.FC = ({ showShortcuts, to const dispatch = useDispatch(); if (!showShortcuts && !toolbarLinks?.length) return null; return ( -
-
+ + {showLightspeedButton && ( -
- - -
- )} -
-
- - {isMac ? '⌥ Opt' : 'Alt'} - F1 - - - {t('console-shared~Accessibility help')} - -
-
- {showShortcuts && ( -
- - -
+ )} {toolbarLinks && toolbarLinks.map((link, index) => ( // eslint-disable-next-line react/no-array-index-key -
+
{(showShortcuts || index > 0) && link ? ( = ({ showShortcuts, to }} /> ) : null} -
{link}
+ {link}
))} -
-
+ + + + + {isMac ? '⌥ Opt' : 'Alt'} + F1 + + + {t('console-shared~Accessibility help')} + + + ); }; export default CodeEditorToolbar; diff --git a/frontend/packages/console-shared/src/components/editor/ShortcutsLink.tsx b/frontend/packages/console-shared/src/components/editor/ShortcutsLink.tsx index 526ead1e407..709364e1c8c 100644 --- a/frontend/packages/console-shared/src/components/editor/ShortcutsLink.tsx +++ b/frontend/packages/console-shared/src/components/editor/ShortcutsLink.tsx @@ -1,58 +1,39 @@ import * as React from 'react'; -import { Popover, Button } from '@patternfly/react-core'; -import { QuestionCircleIcon } from '@patternfly/react-icons/dist/esm/icons/question-circle-icon'; +import { PopoverProps } from '@patternfly/react-core'; import { useTranslation } from 'react-i18next'; import { ShortcutTable, Shortcut } from '../shortcuts'; import { isMac } from '../shortcuts/Shortcut'; -interface ShortcutsLinkProps { - onHideShortcuts?: () => {}; -} - -const ShortcutsLink: React.FC = ({ onHideShortcuts }) => { +export const useShortcutLink = (onHideShortcuts?: () => {}): PopoverProps => { const { t } = useTranslation(); - return ( - - - {t('console-shared~Accessibility help')} - - {t('console-shared~View all editor shortcuts')} - - {t('console-shared~Activate auto complete')} - - - {t( - 'console-shared~Toggle Tab action between insert Tab character and move focus out of editor', - )} - - - {t('console-shared~View document outline')} - - {t('console-shared~View property descriptions')} - - {t('console-shared~Save')} - - - } - maxWidth="25rem" - distance={18} - onHide={onHideShortcuts} - > - - - ); -}; -export default ShortcutsLink; + return { + 'aria-label': t('console-shared~Shortcuts'), + bodyContent: ( + + + {t('console-shared~Accessibility help')} + + {t('console-shared~View all editor shortcuts')} + + {t('console-shared~Activate auto complete')} + + + {t( + 'console-shared~Toggle Tab action between insert Tab character and move focus out of editor', + )} + + + {t('console-shared~View document outline')} + + {t('console-shared~View property descriptions')} + + {t('console-shared~Save')} + + + ), + maxWidth: '25rem', + distance: 18, + onHide: onHideShortcuts, + }; +}; diff --git a/frontend/packages/console-shared/src/components/editor/__tests__/CodeEditorToolbar.spec.tsx b/frontend/packages/console-shared/src/components/editor/__tests__/CodeEditorToolbar.spec.tsx index 9392ebca58d..83aa9ee1b78 100644 --- a/frontend/packages/console-shared/src/components/editor/__tests__/CodeEditorToolbar.spec.tsx +++ b/frontend/packages/console-shared/src/components/editor/__tests__/CodeEditorToolbar.spec.tsx @@ -8,7 +8,6 @@ import { useDispatch } from 'react-redux'; import { ActionType } from '@console/internal/reducers/ols'; import { useOLSConfig } from '../../../hooks/ols-hook'; import CodeEditorToolbar from '../CodeEditorToolbar'; -import ShortcutsLink from '../ShortcutsLink'; jest.mock('react-i18next', () => ({ useTranslation: jest.fn(), @@ -37,11 +36,6 @@ describe('CodeEditorToolbar', () => { expect(wrapper.isEmptyRender()).toBe(true); }); - it('should render toolbar with shortcuts when showShortcuts is true', () => { - wrapper = shallow(); - expect(wrapper.find(ShortcutsLink).exists()).toBe(true); - }); - it('should render toolbar with custom links when toolbarLinks are provided', () => { const toolbarLinks = [
Custom Link
]; wrapper = shallow(); diff --git a/frontend/packages/console-shared/src/components/editor/theme.ts b/frontend/packages/console-shared/src/components/editor/theme.ts index 5dabd23d8c4..9c157017ce7 100644 --- a/frontend/packages/console-shared/src/components/editor/theme.ts +++ b/frontend/packages/console-shared/src/components/editor/theme.ts @@ -54,10 +54,32 @@ const defineThemes = (editor: typeof monacoEditor) => { }); }; +const useSystemTheme = () => { + const [theme, setTheme] = useState('light'); + + useEffect(() => { + const query = window.matchMedia('(prefers-color-scheme: dark)'); + + const updateTheme = () => { + setTheme(query.matches ? 'dark' : 'light'); + }; + + updateTheme(); + + query.addEventListener('change', updateTheme); + return () => { + query.removeEventListener('change', updateTheme); + }; + }, []); + + return theme; +}; + /** * Sets the theme of a provided Monaco editor instance based on the current theme. */ export const useConsoleMonacoTheme = (editor: typeof monacoEditor | null) => { + const systemTheme = useSystemTheme(); const theme = useContext(ThemeContext); const [themeLoaded, setThemeLoaded] = useState(false); @@ -70,9 +92,11 @@ export const useConsoleMonacoTheme = (editor: typeof monacoEditor | null) => { if (theme === 'light') { editor.setTheme('console-light'); - } else { + } else if (theme === 'dark') { editor.setTheme('console-dark'); + } else if (theme === 'systemDefault') { + editor.setTheme(`console-${systemTheme}`); } } - }, [theme, editor, themeLoaded]); + }, [theme, editor, themeLoaded, systemTheme]); }; diff --git a/frontend/packages/console-shared/src/components/formik-fields/CodeEditorField.tsx b/frontend/packages/console-shared/src/components/formik-fields/CodeEditorField.tsx index 7384d711aa7..ea4e39eea2f 100644 --- a/frontend/packages/console-shared/src/components/formik-fields/CodeEditorField.tsx +++ b/frontend/packages/console-shared/src/components/formik-fields/CodeEditorField.tsx @@ -99,7 +99,6 @@ const CodeEditorField: React.FC = ({ icon={ } - isInline variant="link" onClick={() => setSidebarOpen(true)} > diff --git a/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx b/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx index 7ecd9414f7b..6c85c2f17c5 100644 --- a/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx +++ b/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx @@ -1,7 +1,8 @@ import * as React from 'react'; -import Editor, { OnChange, EditorProps, Monaco } from '@monaco-editor/react'; +import { CodeEditor, ChangeHandler, Language } from '@patternfly/react-code-editor'; import { FormGroup, FormHelperText, HelperText, HelperTextItem } from '@patternfly/react-core'; import { FormikValues, useFormikContext } from 'formik'; +import type * as Monaco from 'monaco-editor/esm/vs/editor/editor.api'; import { RedExclamationCircleIcon, useDebounceCallback } from '@console/shared/src'; import { useConsoleMonacoTheme } from '@console/shared/src/components/editor/theme'; @@ -11,7 +12,10 @@ type EditorFieldProps = { helpText?: React.ReactNode; required?: boolean; isDisabled?: boolean; -} & EditorProps; + onChange?: ChangeHandler; + language?: Language; + options?: Monaco.editor.IEditorOptions; +}; const EditorField: React.FC = ({ name, @@ -24,10 +28,10 @@ const EditorField: React.FC = ({ }) => { const { getFieldMeta, setFieldValue, setFieldTouched } = useFormikContext(); const { error, value } = getFieldMeta(name); - const [monaco, setMonaco] = React.useState(null); + const [monaco, setMonaco] = React.useState(null); useConsoleMonacoTheme(monaco?.editor); - const debouncedOnChange = useDebounceCallback((newValue, event) => { + const debouncedOnChange = useDebounceCallback((newValue, event) => { if (onChange) { onChange(newValue, event); } @@ -37,13 +41,15 @@ const EditorField: React.FC = ({ return ( - { - setMonaco(m); - }} + onEditorDidMount={(_e, m) => setMonaco(m)} + isReadOnly={false} + isMinimapVisible={false} + height="sizeToFit" + language={otherProps?.language} + options={otherProps?.options} /> diff --git a/frontend/packages/dev-console/src/components/buildconfig/sections/HooksSection.tsx b/frontend/packages/dev-console/src/components/buildconfig/sections/HooksSection.tsx index a69737bbf27..193e223d388 100644 --- a/frontend/packages/dev-console/src/components/buildconfig/sections/HooksSection.tsx +++ b/frontend/packages/dev-console/src/components/buildconfig/sections/HooksSection.tsx @@ -55,12 +55,8 @@ const HooksSection: React.FC<{}> = () => { diff --git a/frontend/packages/dev-console/src/components/buildconfig/sections/SourceSection.tsx b/frontend/packages/dev-console/src/components/buildconfig/sections/SourceSection.tsx index cf18e5ef3d8..b382e2fbe2c 100644 --- a/frontend/packages/dev-console/src/components/buildconfig/sections/SourceSection.tsx +++ b/frontend/packages/dev-console/src/components/buildconfig/sections/SourceSection.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { Language } from '@patternfly/react-code-editor'; import { useField } from 'formik'; import { useTranslation } from 'react-i18next'; import { DropdownField } from '@console/shared'; @@ -33,8 +34,6 @@ const SourceSection: React.FC = () => { }; const lineHeight = 18; - const showLines = 10; // Math.max(3, Math.min(15, dockerfile?.split('/n')?.length)); - const height = lineHeight * showLines; return ( @@ -55,13 +54,9 @@ const SourceSection: React.FC = () => { diff --git a/frontend/packages/integration-tests-cypress/views/yaml-editor.ts b/frontend/packages/integration-tests-cypress/views/yaml-editor.ts index 7323760fc80..bec2f7fe900 100644 --- a/frontend/packages/integration-tests-cypress/views/yaml-editor.ts +++ b/frontend/packages/integration-tests-cypress/views/yaml-editor.ts @@ -1,13 +1,23 @@ +import type { editor } from 'monaco-editor/esm/vs/editor/editor.api'; + export const getEditorContent = () => { - return cy.window().then((win: any) => { - return win.monaco.editor.getModels()[0].getValue(); - }); + return cy + .window() + .its('monaco.editor.getModels') + .should('be.a', 'function') + .then((getModels: typeof editor.getModels) => { + return getModels()[0].getValue(); + }); }; export const setEditorContent = (text: string) => { - return cy.window().then((win: any) => { - win.monaco.editor.getModels()[0].setValue(text); - }); + return cy + .window() + .its('monaco.editor.getModels') + .should('be.a', 'function') + .then((getModels: typeof editor.getModels) => { + return getModels()[0].setValue(text); + }); }; // initially yamlEditor loads with all grey text, finished loading when editor is color coded diff --git a/frontend/public/components/_edit-yaml.scss b/frontend/public/components/_edit-yaml.scss index 5ef25402d7c..d85d07cc5d4 100644 --- a/frontend/public/components/_edit-yaml.scss +++ b/frontend/public/components/_edit-yaml.scss @@ -23,22 +23,6 @@ } } -// only used in packages and will be orphaned when packages move out -.yaml-editor__header { - border-bottom: var(--pf-t--global--border--width--divider--default) solid - var(--pf-t--global--border--color--default); - padding: 20px $pf-v6-global-gutter; - @media (min-width: $pf-v6-global--breakpoint--xl) { - padding-left: $pf-v6-global-gutter--md; - padding-right: $pf-v6-global-gutter--md; - } -} - -// only used in packages and will be orphaned when packages move out -.yaml-editor__header-text { - margin: 0; -} - .yaml-editor__link { display: inline-block; } diff --git a/frontend/public/components/edit-yaml.jsx b/frontend/public/components/edit-yaml.jsx index 9503b9660ce..f1930199427 100644 --- a/frontend/public/components/edit-yaml.jsx +++ b/frontend/public/components/edit-yaml.jsx @@ -8,7 +8,7 @@ import { useDispatch, useSelector, connect } from 'react-redux'; import { action } from 'typesafe-actions'; import { ActionType, getOLSCodeBlock } from '@console/internal/reducers/ols'; import { safeLoad, safeLoadAll, safeDump } from 'js-yaml'; -import { ActionGroup, Alert, Button, Checkbox } from '@patternfly/react-core'; +import { ActionGroup, Alert, Button, Switch } from '@patternfly/react-core'; import { DownloadIcon } from '@patternfly/react-icons/dist/esm/icons/download-icon'; import { InfoCircleIcon } from '@patternfly/react-icons/dist/esm/icons/info-circle-icon'; import { Trans, useTranslation } from 'react-i18next'; @@ -747,21 +747,20 @@ const EditYAMLInner = (props) => { !showSidebar && hasSidebarContent ? ( ) : null; const tooltipCheckBox = ( - ); @@ -807,6 +806,7 @@ const EditYAMLInner = (props) => { onChange={onChange} onSave={() => (allowMultiple ? saveAll() : save())} onEditorDidMount={() => setEditorMounted(true)} + isLanguageLabelVisible />
{customAlerts} diff --git a/frontend/public/components/sidebars/resource-sidebar-samples.tsx b/frontend/public/components/sidebars/resource-sidebar-samples.tsx index 8add9c8a1e1..311303fe856 100644 --- a/frontend/public/components/sidebars/resource-sidebar-samples.tsx +++ b/frontend/public/components/sidebars/resource-sidebar-samples.tsx @@ -1,7 +1,8 @@ import * as _ from 'lodash-es'; import * as React from 'react'; import { Button, Level, LevelItem, Title } from '@patternfly/react-core'; -import Editor, { Monaco } from '@monaco-editor/react'; +import type * as Monaco from 'monaco-editor/esm/vs/editor/editor.api'; +import { CodeEditor, Language } from '@patternfly/react-code-editor'; import { useConsoleMonacoTheme } from '@console/shared/src/components/editor/theme'; import { ChevronDownIcon } from '@patternfly/react-icons/dist/esm/icons/chevron-down-icon'; import { ChevronRightIcon } from '@patternfly/react-icons/dist/esm/icons/chevron-right-icon'; @@ -60,16 +61,16 @@ const ResourceSidebarSample: React.FC = ({ const lineHeight = 18; const PreviewYAML = ({ maxPreviewLines = 20, yaml }) => { - const [monaco, setMonaco] = React.useState(null); + const [monaco, setMonaco] = React.useState(null); useConsoleMonacoTheme(monaco?.editor); return (
- setMonaco(m)} + onEditorDidMount={(_e, m) => setMonaco(m)} options={{ lineHeight, readOnly: true, diff --git a/frontend/public/locales/en/public.json b/frontend/public/locales/en/public.json index 58dacc006b4..edabcc4c0c9 100644 --- a/frontend/public/locales/en/public.json +++ b/frontend/public/locales/en/public.json @@ -486,7 +486,7 @@ "Failed to parse YAML sample": "Failed to parse YAML sample", "An error occurred.": "An error occurred.", "View sidebar": "View sidebar", - "Show tooltips": "Show tooltips", + "Tooltips": "Tooltips", "Drop file here": "Drop file here", "Drag and drop YAML or JSON files into the editor, or manually enter files and use <2>--- to separate each definition.": "Drag and drop YAML or JSON files into the editor, or manually enter files and use <2>--- to separate each definition.", "Create by manually entering YAML or JSON definitions, or by dragging and dropping a file into the editor.": "Create by manually entering YAML or JSON definitions, or by dragging and dropping a file into the editor.", diff --git a/frontend/scripts/check-patternfly-modules.sh b/frontend/scripts/check-patternfly-modules.sh index 07da7e420cf..ba6bf1b8090 100755 --- a/frontend/scripts/check-patternfly-modules.sh +++ b/frontend/scripts/check-patternfly-modules.sh @@ -50,6 +50,7 @@ PKGS_TO_CHECK=( '@patternfly/quickstarts:6' '@patternfly/react-catalog-view-extension:6' '@patternfly/react-charts:8' + '@patternfly/react-code-editor:6' '@patternfly/react-component-groups:6' '@patternfly/react-console:6' '@patternfly/react-core:6' diff --git a/frontend/yarn.lock b/frontend/yarn.lock index eb73225d73f..40ebc73d8aa 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1880,6 +1880,18 @@ lodash "^4.17.21" tslib "^2.8.1" +"@patternfly/react-code-editor@^6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@patternfly/react-code-editor/-/react-code-editor-6.1.0.tgz#550ddf2cbd3f9f41595d4834731f34a704628936" + integrity sha512-ae04+DdkgXFn3wEzvNCncNa78ZK3Swh5ng8p7yqFrD6lhW69NoJf+DdQlHi8gM8Qy05DNnIemSbQWpWLpInyzw== + dependencies: + "@monaco-editor/react" "^4.6.0" + "@patternfly/react-core" "^6.1.0" + "@patternfly/react-icons" "^6.1.0" + "@patternfly/react-styles" "^6.1.0" + react-dropzone "14.3.5" + tslib "^2.8.1" + "@patternfly/react-component-groups@^6.2.0-prerelease.4": version "6.2.0-prerelease.4" resolved "https://registry.yarnpkg.com/@patternfly/react-component-groups/-/react-component-groups-6.2.0-prerelease.4.tgz#235fc34a40a05aba9b13007e18e523de5f0847ca" @@ -1905,7 +1917,7 @@ xterm "^5.1.0" xterm-addon-fit "^0.8.0" -"@patternfly/react-core@^6.0.0", "@patternfly/react-core@^6.0.0-prerelease.21", "@patternfly/react-core@^6.2.0-prerelease.15": +"@patternfly/react-core@^6.0.0", "@patternfly/react-core@^6.0.0-prerelease.21", "@patternfly/react-core@^6.1.0", "@patternfly/react-core@^6.2.0-prerelease.15": version "6.2.0-prerelease.15" resolved "https://registry.yarnpkg.com/@patternfly/react-core/-/react-core-6.2.0-prerelease.15.tgz#8f576605763e3410a411ca2681ddb7d66c47970d" integrity sha512-h8TdGxcX5Bkxb6c5cdP8Z0FEAeVcu4PO+dPncq2SXdMmFPvwUJfeklVGC8v81fclPPVtlijStFSM5aC5n1XjHQ== @@ -1917,7 +1929,7 @@ react-dropzone "^14.3.5" tslib "^2.8.1" -"@patternfly/react-icons@^6.0.0", "@patternfly/react-icons@^6.0.0-prerelease.7", "@patternfly/react-icons@^6.2.0-prerelease.2": +"@patternfly/react-icons@^6.0.0", "@patternfly/react-icons@^6.0.0-prerelease.7", "@patternfly/react-icons@^6.1.0", "@patternfly/react-icons@^6.2.0-prerelease.2": version "6.2.0-prerelease.2" resolved "https://registry.yarnpkg.com/@patternfly/react-icons/-/react-icons-6.2.0-prerelease.2.tgz#b695a12733d90004d243b37458bfe52947317d8e" integrity sha512-3ymG24ICMAvMqrDPzU8ycPcsHht4RfHjQil3ox43wwHI+nLrpywAgE9z6GtAzwtdm+DVZHBT1CWLWuAgdofX8w== @@ -1932,7 +1944,7 @@ "@patternfly/react-styles" "^6.0.0" memoize-one "^5.1.0" -"@patternfly/react-styles@^6.0.0", "@patternfly/react-styles@^6.0.0-prerelease.6", "@patternfly/react-styles@^6.2.0-prerelease.2": +"@patternfly/react-styles@^6.0.0", "@patternfly/react-styles@^6.0.0-prerelease.6", "@patternfly/react-styles@^6.1.0", "@patternfly/react-styles@^6.2.0-prerelease.2": version "6.2.0-prerelease.2" resolved "https://registry.yarnpkg.com/@patternfly/react-styles/-/react-styles-6.2.0-prerelease.2.tgz#32935b2e8d3bfb8aa4287e06a9aa3d4fbd550eea" integrity sha512-1YJH8Ozu05AHCBNGRZyNA5X1HxNGXuYTQvcR8FbVaNGuuWtvZxzb2EovN9xYVBu2joh+/LCLhiTAXI4eNKw3Ag== @@ -14056,7 +14068,7 @@ react-draggable@4.x: classnames "^2.2.5" prop-types "^15.6.0" -react-dropzone@^14.3.5: +react-dropzone@14.3.5, react-dropzone@^14.3.5: version "14.3.5" resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-14.3.5.tgz#1a8bd312c8a353ec78ef402842ccb3589c225add" integrity sha512-9nDUaEEpqZLOz5v5SUcFA0CjM4vq8YbqO0WRls+EYT7+DvxUdzDPKNCPLqGfj3YL9MsniCLCD4RFA6M95V6KMQ== From 1201ee57d242b5477cf54836722ed3cbf0e0498d Mon Sep 17 00:00:00 2001 From: logonoff Date: Tue, 11 Feb 2025 10:03:21 -0500 Subject: [PATCH 09/13] CONSOLE-4409: Address PR feedback and refactor refactor: - update monaco-editor to latest 0.52.2 - update monaco-yaml to latest 5.3.1 - align console CodeEditor prop names with PatternFly - extract all monaco imports to BasicCodeEditor, a new component which only includes theming and i18n, to prevent loading from CDN addressing PR feedback: - remove the language label - hide code editor toolbar on mobile - replace "View sidebar" with a switch - rename "View shortcuts" to "Shortcuts" - add gutter to top of yaml editor --- frontend/package.json | 4 +- .../console-dynamic-plugin-sdk/README.md | 2 +- .../src/extensions/console-types.ts | 22 ++--- .../locales/en/console-shared.json | 3 +- .../src/components/editor/BasicCodeEditor.tsx | 42 ++++++++++ .../src/components/editor/CodeEditor.scss | 42 ++++++---- .../src/components/editor/CodeEditor.tsx | 81 +++++++------------ .../components/editor/CodeEditorToolbar.tsx | 9 +-- .../components/editor/yaml-editor-utils.ts | 21 ++--- .../formik-fields/CodeEditorField.tsx | 4 +- .../components/formik-fields/field-types.ts | 2 +- .../buildconfig/sections/EditorField.tsx | 19 +++-- .../components/test-function/RequestPane.tsx | 2 +- .../components/test-function/ResponsePane.tsx | 2 +- frontend/public/components/_edit-yaml.scss | 2 + frontend/public/components/edit-yaml.jsx | 27 +++---- .../sidebars/resource-sidebar-samples.tsx | 13 +-- frontend/public/locales/en/public.json | 2 +- frontend/webpack.config.ts | 6 +- frontend/yarn.lock | 25 +++--- 20 files changed, 171 insertions(+), 159 deletions(-) create mode 100644 frontend/packages/console-shared/src/components/editor/BasicCodeEditor.tsx diff --git a/frontend/package.json b/frontend/package.json index a843e96f863..3e21032e2fc 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -181,7 +181,7 @@ "js-yaml": "^3.13.1", "json-schema": "^0.3.0", "lodash-es": "^4.17.21", - "monaco-yaml": "^5.2.3", + "monaco-yaml": "^5.3.1", "murmurhash-js": "1.0.x", "node-polyfill-webpack-plugin": "^4.0.0", "pluralize": "^8.0.0", @@ -303,7 +303,7 @@ "mochawesome-merge": "^4.1.0", "mochawesome-report-generator": "^5.1.0", "mock-socket": "^9.0.3", - "monaco-editor": "^0.36.1", + "monaco-editor": "^0.52.2", "monaco-editor-webpack-plugin": "^7.1.0", "prettier": "2.0.5", "react-refresh": "^0.10.0", diff --git a/frontend/packages/console-dynamic-plugin-sdk/README.md b/frontend/packages/console-dynamic-plugin-sdk/README.md index 4c5136d595a..7134f31159d 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/README.md +++ b/frontend/packages/console-dynamic-plugin-sdk/README.md @@ -159,7 +159,7 @@ This section documents notable changes in the Console provided shared modules ac - Removed `@fortawesome/font-awesome` and `openshift-logos-icon`. Plugins should use PatternFly icons from `@patternfly/react-icons` instead. The `fa-spin` class remains but is deprecated and will be removed in the future. Plugins should provide their own CSS to spin icons if needed. -- Upgraded `monaco-editor` to version `0.36.1`. +- Upgraded `monaco-editor` to version `0.52.2`. - Removed PatternFly 4.x shared modules. - Upgraded PatternFly to v6. - Removed styling for generic HTML heading elements (e.g., `

`). Use PatternFly components to achieve correct styling. diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts b/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts index 430198a4077..e33d04c9a50 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts @@ -1,6 +1,6 @@ import * as React from 'react'; import { QuickStartContextValues } from '@patternfly/quickstarts'; -import { Language } from '@patternfly/react-code-editor'; +import { CodeEditorProps as PfCodeEditorProps } from '@patternfly/react-code-editor'; import { ButtonProps } from '@patternfly/react-core'; import { ICell, OnSelect, SortByDirection, TableGridBreakpoint } from '@patternfly/react-table'; import { LocationDescriptor } from 'history'; @@ -635,20 +635,20 @@ export type UserInfo = { extra?: object; }; -export type CodeEditorProps = { +// Omit the ref as we have our own ref type, which is completely different +export type BaseCodeEditorProps = Partial>; + +export type CodeEditorProps = Omit & { + /** Code displayed in code editor. */ value?: string; - language?: Language; - options?: object; - minHeight?: string | number; + /** Minimum editor height in valid CSS height values. */ + minHeight?: CSSStyleDeclaration['minHeight']; + /** Whether to show a toolbar with shortcuts on top of the editor. */ showShortcuts?: boolean; - showMiniMap?: boolean; + /** Toolbar links section on top of the editor */ toolbarLinks?: React.ReactNodeArray; - onChange?: (newValue, event) => void; + /** Callback that is run when CTRL / CMD + S is pressed */ onSave?: () => void; - onEditorDidMount?: (editor: monaco.editor.IStandaloneCodeEditor) => void; - isDownloadEnabled?: boolean; - isCopyEnabled?: boolean; - isLanguageLabelVisible?: boolean; }; export type CodeEditorRef = { diff --git a/frontend/packages/console-shared/locales/en/console-shared.json b/frontend/packages/console-shared/locales/en/console-shared.json index 6c8ff950c35..b2f61dbdd5a 100644 --- a/frontend/packages/console-shared/locales/en/console-shared.json +++ b/frontend/packages/console-shared/locales/en/console-shared.json @@ -108,13 +108,12 @@ "true": "true", "Select {{label}}": "Select {{label}}", "Cluster does not have resource {{groupVersionKind}}": "Cluster does not have resource {{groupVersionKind}}", - "View shortcuts": "View shortcuts", + "Shortcuts": "Shortcuts", "Copy code to clipboard": "Copy code to clipboard", "Content copied to clipboard": "Content copied to clipboard", "Download code": "Download code", "Ask OpenShift Lightspeed": "Ask OpenShift Lightspeed", "Accessibility help": "Accessibility help", - "Shortcuts": "Shortcuts", "View all editor shortcuts": "View all editor shortcuts", "Activate auto complete": "Activate auto complete", "Toggle Tab action between insert Tab character and move focus out of editor": "Toggle Tab action between insert Tab character and move focus out of editor", diff --git a/frontend/packages/console-shared/src/components/editor/BasicCodeEditor.tsx b/frontend/packages/console-shared/src/components/editor/BasicCodeEditor.tsx new file mode 100644 index 00000000000..022605e6b33 --- /dev/null +++ b/frontend/packages/console-shared/src/components/editor/BasicCodeEditor.tsx @@ -0,0 +1,42 @@ +import * as React from 'react'; +import { loader, Monaco } from '@monaco-editor/react'; +import { CodeEditor as PfEditor } from '@patternfly/react-code-editor'; +import * as monaco from 'monaco-editor'; +import { useTranslation } from 'react-i18next'; +import { BaseCodeEditorProps } from '@console/dynamic-plugin-sdk'; +import { useConsoleMonacoTheme } from './theme'; + +// Avoid using monaco from CDN +loader.config({ monaco }); + +/** + * PatternFly CodeEditor with i18n and theme applied. Does not include + * YAML language integration or console-specific CSS. + * + * Note that it is important that this is the only component that imports + * monaco-editor, to avoid fetching files from a 3rd-party CDN. + */ +export const BasicCodeEditor: React.FC = (props) => { + const { t } = useTranslation('console-shared'); + const [monacoRef, setMonacoRef] = React.useState(null); + useConsoleMonacoTheme(monacoRef?.editor); + + return ( + { + setMonacoRef(monacoInstance); + props?.editorProps?.beforeMount?.(monacoInstance); + }, + }} + shortcutsPopoverButtonText={t('Shortcuts')} + copyButtonAriaLabel={t('Copy code to clipboard')} + copyButtonSuccessTooltipText={t('Content copied to clipboard')} + copyButtonToolTipText={t('Copy code to clipboard')} + downloadButtonAriaLabel={t('Download code')} + downloadButtonToolTipText={t('Download code')} + /> + ); +}; diff --git a/frontend/packages/console-shared/src/components/editor/CodeEditor.scss b/frontend/packages/console-shared/src/components/editor/CodeEditor.scss index 37d5eaabb78..ad68e269a60 100644 --- a/frontend/packages/console-shared/src/components/editor/CodeEditor.scss +++ b/frontend/packages/console-shared/src/components/editor/CodeEditor.scss @@ -12,33 +12,43 @@ } .ocs-yaml-editor { - // makes automaticLayout work properly + /* Makes automaticLayout work properly. note that this breaks height="sizeToFit" */ .monaco-editor { position: absolute !important; } + /* Hide CodeEditor toolbar on mobile */ + @media (max-width: $screen-sm-max) { + .pf-v6-c-code-editor__header { + display: none; + + + .pf-v6-c-code-editor__main { + border-block-start-width: var(--pf-t--global--border--width--box--default); + } + } + } + + /* Needed to get the CodeEditorToolbar to place buttons on the right side */ .pf-v6-c-code-editor__controls, .ocs-yaml-editor-toolbar { - // needed to get the CodeEditorToolbar to place buttons on the right side width: 100%; height: 100% !important; - } - - .ocs-yaml-editor-toolbar { align-items: center; + } - .ocs-yaml-editor-toolbar__link { - @media (max-width: $screen-sm-max) { - display: none; - } - } + /* Dividers in toolbar */ + .ocs-yaml-editor-toolbar__link:not(:last-child) { + border-inline-end: var(--pf-t--global--border--width--divider--default) solid var(--pf-t--global--border--color--default); + margin-inline-end: var(--pf-t--global--spacer--sm); + padding-inline-end: var(--pf-t--global--spacer--sm); + } - .ocs-yaml-editor-toolbar__shortcuts { - padding: var(--pf-t--global--spacer--md) var(--pf-v6-global-gutter); + /* Accessibility shortcuts hint styling */ + .ocs-yaml-editor-toolbar__shortcuts { + padding: var(--pf-t--global--spacer--md) var(--pf-v6-global-gutter); - .ocs-yaml-editor-shortcut__text { - color: var(--pf-t--global--text--color--subtle); - margin-left: var(--pf-t--global--spacer--sm); - } + .ocs-yaml-editor-shortcut__text { + color: var(--pf-t--global--text--color--subtle); + margin-inline-start: var(--pf-t--global--spacer--sm); } } } diff --git a/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx b/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx index 21c7fc38f21..b950bac3388 100644 --- a/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx +++ b/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx @@ -1,73 +1,59 @@ import * as React from 'react'; -import { CodeEditor as PfEditor, Language } from '@patternfly/react-code-editor'; -import type * as Monaco from 'monaco-editor/esm/vs/editor/editor.api'; -import { useTranslation } from 'react-i18next'; +import { EditorDidMount, Language } from '@patternfly/react-code-editor'; +import type * as monaco from 'monaco-editor'; import { CodeEditorRef, CodeEditorProps } from '@console/dynamic-plugin-sdk'; +import { BasicCodeEditor } from './BasicCodeEditor'; import CodeEditorToolbar from './CodeEditorToolbar'; import { useShortcutLink } from './ShortcutsLink'; import { useConsoleMonacoTheme } from './theme'; -import { registerYAMLinMonaco, defaultEditorOptions } from './yaml-editor-utils'; +import { registerYAMLinMonaco, registerAutoFold, defaultEditorOptions } from './yaml-editor-utils'; import './CodeEditor.scss'; const CodeEditor = React.forwardRef((props, ref) => { const { value, - options = defaultEditorOptions, + minHeight, showShortcuts, - showMiniMap, toolbarLinks, - minHeight, - onChange, onSave, + options, language, onEditorDidMount, - isDownloadEnabled, - isCopyEnabled, - isLanguageLabelVisible, } = props; - const { t } = useTranslation('console-shared'); const shortcutPopover = useShortcutLink(); - const [editorRef, setEditorRef] = React.useState( + const [editorRef, setEditorRef] = React.useState( null, ); - const [monacoRef, setMonacoRef] = React.useState(null); + const [monacoRef, setMonacoRef] = React.useState(null); + const [usesValue] = React.useState(value !== undefined); useConsoleMonacoTheme(monacoRef?.editor); - const [usesValue] = React.useState(value !== undefined); - const editorDidMount = React.useCallback( - (editor, monaco) => { - setEditorRef(editor); - setMonacoRef(monaco); - const currentLanguage = editor.getModel()?.getLanguageId(); - editor.layout(); - editor.focus(); + const editorDidMount: EditorDidMount = React.useCallback( + (mountedEditor, mountedMonaco) => { + setEditorRef(mountedEditor); + setMonacoRef(mountedMonaco); + mountedEditor.getModel()?.updateOptions({ tabSize: 2 }); + const currentLanguage = mountedEditor.getModel()?.getLanguageId(); + mountedEditor.layout(); + mountedEditor.focus(); switch (currentLanguage) { case 'yaml': - registerYAMLinMonaco(editor, monaco, usesValue); + registerYAMLinMonaco(mountedMonaco); + registerAutoFold(mountedEditor, usesValue); break; case 'json': - editor.getAction('editor.action.formatDocument').run(); + mountedEditor.getAction('editor.action.formatDocument').run(); break; default: break; } - monaco.editor.getModels()[0]?.updateOptions({ tabSize: 2 }); - onSave && editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, onSave); // eslint-disable-line no-bitwise - onEditorDidMount && onEditorDidMount(editor); + onSave && + mountedEditor.addCommand(mountedMonaco.KeyMod.CtrlCmd | mountedMonaco.KeyCode.KeyS, onSave); // eslint-disable-line no-bitwise + onEditorDidMount && onEditorDidMount(mountedEditor, mountedMonaco); }, [onSave, usesValue, onEditorDidMount], ); - const editorOptions = React.useMemo(() => { - return { - ...options, - minimap: { - enabled: showMiniMap, - }, - automaticLayout: true, - }; - }, [options, showMiniMap]); - // expose the editor instance to the parent component via ref React.useImperativeHandle( ref, @@ -86,26 +72,17 @@ const CodeEditor = React.forwardRef((props, ref) }, [toolbarLinks, showShortcuts]); return ( - ); }); diff --git a/frontend/packages/console-shared/src/components/editor/CodeEditorToolbar.tsx b/frontend/packages/console-shared/src/components/editor/CodeEditorToolbar.tsx index be439ea830e..3fcc98ee799 100644 --- a/frontend/packages/console-shared/src/components/editor/CodeEditorToolbar.tsx +++ b/frontend/packages/console-shared/src/components/editor/CodeEditorToolbar.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Flex, FlexItem, Button, Divider } from '@patternfly/react-core'; +import { Flex, FlexItem, Button } from '@patternfly/react-core'; import { MagicIcon } from '@patternfly/react-icons'; import { useTranslation } from 'react-i18next'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -44,13 +44,6 @@ const CodeEditorToolbar: React.FC = ({ showShortcuts, to toolbarLinks.map((link, index) => ( // eslint-disable-next-line react/no-array-index-key
- {(showShortcuts || index > 0) && link ? ( - - ) : null} {link}
))} diff --git a/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts b/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts index e147edd76a5..43b972b3b01 100644 --- a/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts +++ b/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts @@ -5,7 +5,11 @@ import * as yaml from 'yaml-ast-parser'; import { openAPItoJSONSchema } from '@console/internal/module/k8s/openapi-to-json-schema'; import { getSwaggerDefinitions } from '@console/internal/module/k8s/swagger'; -export const defaultEditorOptions = { readOnly: false, scrollBeyondLastLine: false }; +export const defaultEditorOptions: monaco.editor.IEditorOptions = { + readOnly: false, + scrollBeyondLastLine: false, + automaticLayout: true, +}; const findManagedMetadata = (model: monaco.editor.ITextModel) => { const modelValue = model.getValue(); @@ -66,7 +70,6 @@ export const fold = ( /** * Register auto fold for the editor - * This should probably be a React hook */ export const registerAutoFold = ( editor: monaco.editor.IStandaloneCodeEditor, @@ -90,21 +93,17 @@ export const registerAutoFold = ( }); }; -export const registerYAMLinMonaco = ( - editor: monaco.editor.IStandaloneCodeEditor, - monacoInstance: typeof monaco, - alreadyInUse: boolean = false, -) => { +export const registerYAMLinMonaco = (monacoInstance: typeof monaco) => { /** * This exists because we enabled globalAPI in the webpack config. This means that the * the monaco instance may have already been setup with the YAML language features. - * Otherwise, you would register all the features again, getting duplicate results. + * Otherwise, we might register all the features again, getting duplicate results. * * Monaco does not provide any APIs for unregistering or checking if the features have already * been registered for a language. * * We check that > 1 YAML language exists because one is the default and - * the other is the initial register that setups our features. + * the other is the language server that we register. */ if (monacoInstance.languages.getLanguages().filter((x) => x.id === 'yaml').length <= 1) { // Prepare the schema @@ -129,8 +128,4 @@ export const registerYAMLinMonaco = ( completion: true, }); } - - if (!alreadyInUse) { - registerAutoFold(editor); - } }; diff --git a/frontend/packages/console-shared/src/components/formik-fields/CodeEditorField.tsx b/frontend/packages/console-shared/src/components/formik-fields/CodeEditorField.tsx index ea4e39eea2f..c2754e7f9d7 100644 --- a/frontend/packages/console-shared/src/components/formik-fields/CodeEditorField.tsx +++ b/frontend/packages/console-shared/src/components/formik-fields/CodeEditorField.tsx @@ -32,7 +32,7 @@ const CodeEditorField: React.FC = ({ schema, showSamples, showShortcuts, - showMiniMap, + isMinimapVisible, minHeight, onSave, language, @@ -90,7 +90,7 @@ const CodeEditorField: React.FC = ({ onChange={(yaml: string) => setFieldValue(name, yaml)} onSave={onSave} showShortcuts={showShortcuts} - showMiniMap={showMiniMap} + isMinimapVisible={isMinimapVisible} language={language} toolbarLinks={ !sidebarOpen && diff --git a/frontend/packages/console-shared/src/components/formik-fields/field-types.ts b/frontend/packages/console-shared/src/components/formik-fields/field-types.ts index 0d88f62b0fa..533109ac305 100644 --- a/frontend/packages/console-shared/src/components/formik-fields/field-types.ts +++ b/frontend/packages/console-shared/src/components/formik-fields/field-types.ts @@ -115,7 +115,7 @@ export interface CodeEditorFieldProps extends FieldProps { schema?: JSONSchema7; showSamples: boolean; showShortcuts?: boolean; - showMiniMap?: boolean; + isMinimapVisible?: boolean; onSave?: () => void; } diff --git a/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx b/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx index 6c85c2f17c5..7d175b596a5 100644 --- a/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx +++ b/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; -import { CodeEditor, ChangeHandler, Language } from '@patternfly/react-code-editor'; +import { ChangeHandler, Language } from '@patternfly/react-code-editor'; import { FormGroup, FormHelperText, HelperText, HelperTextItem } from '@patternfly/react-core'; import { FormikValues, useFormikContext } from 'formik'; import type * as Monaco from 'monaco-editor/esm/vs/editor/editor.api'; import { RedExclamationCircleIcon, useDebounceCallback } from '@console/shared/src'; -import { useConsoleMonacoTheme } from '@console/shared/src/components/editor/theme'; +import { BasicCodeEditor } from '@console/shared/src/components/editor/BasicCodeEditor'; type EditorFieldProps = { name: string; @@ -28,8 +28,6 @@ const EditorField: React.FC = ({ }) => { const { getFieldMeta, setFieldValue, setFieldTouched } = useFormikContext(); const { error, value } = getFieldMeta(name); - const [monaco, setMonaco] = React.useState(null); - useConsoleMonacoTheme(monaco?.editor); const debouncedOnChange = useDebounceCallback((newValue, event) => { if (onChange) { @@ -41,15 +39,16 @@ const EditorField: React.FC = ({ return ( - setMonaco(m)} - isReadOnly={false} - isMinimapVisible={false} + isFullHeight={false} height="sizeToFit" language={otherProps?.language} - options={otherProps?.options} + options={{ + minimap: { enabled: false }, + ...otherProps?.options, + }} /> diff --git a/frontend/packages/knative-plugin/src/components/test-function/RequestPane.tsx b/frontend/packages/knative-plugin/src/components/test-function/RequestPane.tsx index 99bfafcac06..38cf30b8dfe 100644 --- a/frontend/packages/knative-plugin/src/components/test-function/RequestPane.tsx +++ b/frontend/packages/knative-plugin/src/components/test-function/RequestPane.tsx @@ -144,7 +144,7 @@ const RequestPane: React.FC> = ({ setFieldValue, value minHeight="34vh" showSamples={false} showShortcuts={false} - showMiniMap={false} + isMinimapVisible={false} language={getcurrentLanguage(contentType)} />

diff --git a/frontend/packages/knative-plugin/src/components/test-function/ResponsePane.tsx b/frontend/packages/knative-plugin/src/components/test-function/ResponsePane.tsx index 688a7bd1e1d..0a0bc9c7169 100644 --- a/frontend/packages/knative-plugin/src/components/test-function/ResponsePane.tsx +++ b/frontend/packages/knative-plugin/src/components/test-function/ResponsePane.tsx @@ -92,7 +92,7 @@ const ResponsePane: React.FC ) : ( diff --git a/frontend/public/components/_edit-yaml.scss b/frontend/public/components/_edit-yaml.scss index d85d07cc5d4..ee8f98a2cb9 100644 --- a/frontend/public/components/_edit-yaml.scss +++ b/frontend/public/components/_edit-yaml.scss @@ -4,6 +4,8 @@ height: 100%; flex: 1; flex-direction: column; + padding-top: $pf-v6-global-gutter; + @media (min-width: $pf-v6-global--breakpoint--md) { margin-left: $pf-v6-global-gutter; margin-right: $pf-v6-global-gutter; diff --git a/frontend/public/components/edit-yaml.jsx b/frontend/public/components/edit-yaml.jsx index f1930199427..1b0420dae58 100644 --- a/frontend/public/components/edit-yaml.jsx +++ b/frontend/public/components/edit-yaml.jsx @@ -10,7 +10,6 @@ import { ActionType, getOLSCodeBlock } from '@console/internal/reducers/ols'; import { safeLoad, safeLoadAll, safeDump } from 'js-yaml'; import { ActionGroup, Alert, Button, Switch } from '@patternfly/react-core'; import { DownloadIcon } from '@patternfly/react-icons/dist/esm/icons/download-icon'; -import { InfoCircleIcon } from '@patternfly/react-icons/dist/esm/icons/info-circle-icon'; import { Trans, useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom-v5-compat'; import { @@ -743,17 +742,18 @@ const EditYAMLInner = (props) => { const definition = model ? definitionFor(model) : { properties: [] }; const showSchema = definition && !_.isEmpty(definition.properties); const hasSidebarContent = showSchema || (create && !_.isEmpty(samples)) || !_.isEmpty(snippets); - const sidebarLink = - !showSidebar && hasSidebarContent ? ( - - ) : null; - const tooltipCheckBox = ( + const sidebarSwitch = hasSidebarContent && ( + + ); + + const tooltipSwitch = ( { options={options} showShortcuts={!genericYAML} minHeight="100px" - toolbarLinks={sidebarLink ? [tooltipCheckBox, sidebarLink] : [tooltipCheckBox]} + toolbarLinks={sidebarSwitch ? [tooltipSwitch, sidebarSwitch] : [tooltipSwitch]} onChange={onChange} onSave={() => (allowMultiple ? saveAll() : save())} onEditorDidMount={() => setEditorMounted(true)} - isLanguageLabelVisible />
{customAlerts} diff --git a/frontend/public/components/sidebars/resource-sidebar-samples.tsx b/frontend/public/components/sidebars/resource-sidebar-samples.tsx index 311303fe856..4acaa2b00de 100644 --- a/frontend/public/components/sidebars/resource-sidebar-samples.tsx +++ b/frontend/public/components/sidebars/resource-sidebar-samples.tsx @@ -1,9 +1,8 @@ import * as _ from 'lodash-es'; import * as React from 'react'; import { Button, Level, LevelItem, Title } from '@patternfly/react-core'; -import type * as Monaco from 'monaco-editor/esm/vs/editor/editor.api'; -import { CodeEditor, Language } from '@patternfly/react-code-editor'; -import { useConsoleMonacoTheme } from '@console/shared/src/components/editor/theme'; +import { Language } from '@patternfly/react-code-editor'; +import { BasicCodeEditor } from '@console/shared/src/components/editor/BasicCodeEditor'; import { ChevronDownIcon } from '@patternfly/react-icons/dist/esm/icons/chevron-down-icon'; import { ChevronRightIcon } from '@patternfly/react-icons/dist/esm/icons/chevron-right-icon'; import { DownloadIcon } from '@patternfly/react-icons/dist/esm/icons/download-icon'; @@ -61,16 +60,12 @@ const ResourceSidebarSample: React.FC = ({ const lineHeight = 18; const PreviewYAML = ({ maxPreviewLines = 20, yaml }) => { - const [monaco, setMonaco] = React.useState(null); - useConsoleMonacoTheme(monaco?.editor); - return (
- setMonaco(m)} + code={yaml} options={{ lineHeight, readOnly: true, diff --git a/frontend/public/locales/en/public.json b/frontend/public/locales/en/public.json index edabcc4c0c9..812555e2feb 100644 --- a/frontend/public/locales/en/public.json +++ b/frontend/public/locales/en/public.json @@ -485,7 +485,7 @@ "Resources in the same namespace and API group must have unique names": "Resources in the same namespace and API group must have unique names", "Failed to parse YAML sample": "Failed to parse YAML sample", "An error occurred.": "An error occurred.", - "View sidebar": "View sidebar", + "Sidebar": "Sidebar", "Tooltips": "Tooltips", "Drop file here": "Drop file here", "Drag and drop YAML or JSON files into the editor, or manually enter files and use <2>--- to separate each definition.": "Drag and drop YAML or JSON files into the editor, or manually enter files and use <2>--- to separate each definition.", diff --git a/frontend/webpack.config.ts b/frontend/webpack.config.ts index c6dfe06764d..840018a2de9 100644 --- a/frontend/webpack.config.ts +++ b/frontend/webpack.config.ts @@ -61,11 +61,7 @@ const sharedPluginModulesTest = getVendorModuleRegExp( const config: Configuration = { entry: { - main: [ - './public/components/app.jsx', - 'monaco-editor/esm/vs/editor/editor.worker.js', - '/node_modules/@patternfly-5/patternfly/patternfly.scss', - ], + main: ['./public/components/app.jsx', '/node_modules/@patternfly-5/patternfly/patternfly.scss'], }, cache: { type: 'filesystem', diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 40ebc73d8aa..7fe79dc2f9f 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -12394,10 +12394,10 @@ monaco-editor-webpack-plugin@^7.1.0: dependencies: loader-utils "^2.0.2" -monaco-editor@^0.36.1: - version "0.36.1" - resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.36.1.tgz#aad528c815605307473a1634612946921d8079b5" - integrity sha512-/CaclMHKQ3A6rnzBzOADfwdSJ25BFoFT0Emxsc4zYVyav5SkK9iA6lEtIeuN/oRYbwPgviJT+t3l+sjFa28jYg== +monaco-editor@^0.52.2: + version "0.52.2" + resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.52.2.tgz#53c75a6fcc6802684e99fd1b2700299857002205" + integrity sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ== monaco-languageserver-types@^0.4.0: version "0.4.0" @@ -12425,10 +12425,10 @@ monaco-worker-manager@^2.0.0: resolved "https://registry.yarnpkg.com/monaco-worker-manager/-/monaco-worker-manager-2.0.1.tgz#f67c54dfca34ed4b225d5de84e77b24b4e36de8a" integrity sha512-kdPL0yvg5qjhKPNVjJoym331PY/5JC11aPJXtCZNwWRvBr6jhkIamvYAyiY5P1AWFmNOy0aRDRoMdZfa71h8kg== -monaco-yaml@^5.2.3: - version "5.2.3" - resolved "https://registry.yarnpkg.com/monaco-yaml/-/monaco-yaml-5.2.3.tgz#990fcf697bfa54e684a2d2f7df6d81a889843db4" - integrity sha512-GEplKC+YYmS0TOlJdv0FzbqkDN/IG2FSwEw+95myECVxTlhty2amwERYjzvorvJXmIagP1grd3Lleq7aQEJpPg== +monaco-yaml@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/monaco-yaml/-/monaco-yaml-5.3.1.tgz#e37749ff8491924a7cabf9f597e746036cc6a9ea" + integrity sha512-1MN8i1Tnc8d8RugQGqv5jp+Ce2xtNhrnbm0ZZbe5ceExj9C2PkKZfHJhY9kbdUS4G7xSVwKlVdMTmLlStepOtw== dependencies: jsonc-parser "^3.0.0" monaco-languageserver-types "^0.4.0" @@ -12436,7 +12436,7 @@ monaco-yaml@^5.2.3: monaco-types "^0.1.0" monaco-worker-manager "^2.0.0" path-browserify "^1.0.0" - prettier "^2.0.0" + prettier "^3.0.0" vscode-languageserver-textdocument "^1.0.0" vscode-languageserver-types "^3.0.0" vscode-uri "^3.0.0" @@ -13640,11 +13640,16 @@ prettier@2.0.5: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4" integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg== -prettier@^2.0.0, prettier@^2.6.2: +prettier@^2.6.2: version "2.8.8" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +prettier@^3.0.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.0.tgz#50325a28887c6dfdf2ca3f8eaba02b66a8429ca7" + integrity sha512-quyMrVt6svPS7CjQ9gKb3GLEX/rl3BCL2oa/QkNcXv4YNVBC9olt3s+H7ukto06q7B1Qz46PbrKLO34PR6vXcA== + pretty-bytes@^5.6.0: version "5.6.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" From 8b97270da61bb3f90705f51573eb1386fac80f6b Mon Sep 17 00:00:00 2001 From: logonoff Date: Tue, 11 Feb 2025 17:29:29 -0500 Subject: [PATCH 10/13] CONSOLE-4409: Update CodeEditor styling and i18n --- frontend/@types/console/index.d.ts | 1 + .../src/extensions/console-types.ts | 26 ++--- .../locales/en/console-shared.json | 7 +- .../components/editor/BasicCodeEditor.scss | 7 ++ .../src/components/editor/BasicCodeEditor.tsx | 30 ++++-- .../src/components/editor/CodeEditor.scss | 23 ++--- .../src/components/editor/CodeEditor.tsx | 5 +- .../components/editor/CodeEditorToolbar.tsx | 98 ++++++++++--------- .../__tests__/CodeEditorToolbar.spec.tsx | 8 +- .../components/editor/yaml-editor-utils.ts | 1 - .../buildconfig/sections/EditorField.tsx | 19 ++-- 11 files changed, 125 insertions(+), 100 deletions(-) create mode 100644 frontend/packages/console-shared/src/components/editor/BasicCodeEditor.scss diff --git a/frontend/@types/console/index.d.ts b/frontend/@types/console/index.d.ts index 927f6147a93..023a486eae9 100644 --- a/frontend/@types/console/index.d.ts +++ b/frontend/@types/console/index.d.ts @@ -66,6 +66,7 @@ declare interface Window { webpackSharedScope?: {}; // webpack shared scope object, contains modules shared across plugins ResizeObserver: ResizeObserver.prototype; // polyfill used by react-measure Cypress?: {}; + monaco?: {}; } // From https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts b/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts index e33d04c9a50..019a160fe30 100644 --- a/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts +++ b/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts @@ -635,22 +635,26 @@ export type UserInfo = { extra?: object; }; -// Omit the ref as we have our own ref type, which is completely different -export type BaseCodeEditorProps = Partial>; - -export type CodeEditorProps = Omit & { - /** Code displayed in code editor. */ - value?: string; - /** Minimum editor height in valid CSS height values. */ - minHeight?: CSSStyleDeclaration['minHeight']; +export type CodeEditorToolbarProps = { /** Whether to show a toolbar with shortcuts on top of the editor. */ showShortcuts?: boolean; - /** Toolbar links section on top of the editor */ + /** Toolbar links section on the left side of the editor */ toolbarLinks?: React.ReactNodeArray; - /** Callback that is run when CTRL / CMD + S is pressed */ - onSave?: () => void; }; +// Omit the ref as we have our own ref type, which is completely different +export type BasicCodeEditorProps = Partial>; + +export type CodeEditorProps = Omit & + CodeEditorToolbarProps & { + /** Code displayed in code editor. */ + value?: string; + /** Minimum editor height in valid CSS height values. */ + minHeight?: CSSStyleDeclaration['minHeight']; + /** Callback that is run when CTRL / CMD + S is pressed */ + onSave?: () => void; + }; + export type CodeEditorRef = { editor: monaco.editor.IStandaloneCodeEditor; monaco: typeof monaco; diff --git a/frontend/packages/console-shared/locales/en/console-shared.json b/frontend/packages/console-shared/locales/en/console-shared.json index b2f61dbdd5a..58a713eac68 100644 --- a/frontend/packages/console-shared/locales/en/console-shared.json +++ b/frontend/packages/console-shared/locales/en/console-shared.json @@ -108,10 +108,15 @@ "true": "true", "Select {{label}}": "Select {{label}}", "Cluster does not have resource {{groupVersionKind}}": "Cluster does not have resource {{groupVersionKind}}", - "Shortcuts": "Shortcuts", "Copy code to clipboard": "Copy code to clipboard", "Content copied to clipboard": "Content copied to clipboard", "Download code": "Download code", + "Shortcuts": "Shortcuts", + "Upload code": "Upload code", + "Drag and drop a file or upload one.": "Drag and drop a file or upload one.", + "Browse": "Browse", + "Start from scratch": "Start from scratch", + "Start editing": "Start editing", "Ask OpenShift Lightspeed": "Ask OpenShift Lightspeed", "Accessibility help": "Accessibility help", "View all editor shortcuts": "View all editor shortcuts", diff --git a/frontend/packages/console-shared/src/components/editor/BasicCodeEditor.scss b/frontend/packages/console-shared/src/components/editor/BasicCodeEditor.scss new file mode 100644 index 00000000000..c2b76cc9de8 --- /dev/null +++ b/frontend/packages/console-shared/src/components/editor/BasicCodeEditor.scss @@ -0,0 +1,7 @@ +// match the token in theme.ts. it already matches for light theme +.pf-v6-theme-dark { + .co-code-editor:not(.pf-m-read-only) { + --pf-v6-c-code-editor__main--BackgroundColor: var(--pf-t--color--gray--90); + --pf-v6-c-code-editor__tab--BackgroundColor: var(--pf-t--color--gray--90); + } +} diff --git a/frontend/packages/console-shared/src/components/editor/BasicCodeEditor.tsx b/frontend/packages/console-shared/src/components/editor/BasicCodeEditor.tsx index 022605e6b33..e23b0560e58 100644 --- a/frontend/packages/console-shared/src/components/editor/BasicCodeEditor.tsx +++ b/frontend/packages/console-shared/src/components/editor/BasicCodeEditor.tsx @@ -1,10 +1,12 @@ import * as React from 'react'; import { loader, Monaco } from '@monaco-editor/react'; -import { CodeEditor as PfEditor } from '@patternfly/react-code-editor'; +import { CodeEditor } from '@patternfly/react-code-editor'; +import classNames from 'classnames'; import * as monaco from 'monaco-editor'; import { useTranslation } from 'react-i18next'; -import { BaseCodeEditorProps } from '@console/dynamic-plugin-sdk'; +import { BasicCodeEditorProps } from '@console/dynamic-plugin-sdk'; import { useConsoleMonacoTheme } from './theme'; +import './BasicCodeEditor.scss'; // Avoid using monaco from CDN loader.config({ monaco }); @@ -16,27 +18,35 @@ loader.config({ monaco }); * Note that it is important that this is the only component that imports * monaco-editor, to avoid fetching files from a 3rd-party CDN. */ -export const BasicCodeEditor: React.FC = (props) => { +export const BasicCodeEditor: React.FC = (props) => { const { t } = useTranslation('console-shared'); const [monacoRef, setMonacoRef] = React.useState(null); useConsoleMonacoTheme(monacoRef?.editor); return ( - { setMonacoRef(monacoInstance); + window.monaco = monacoInstance; // for e2e tests props?.editorProps?.beforeMount?.(monacoInstance); }, }} - shortcutsPopoverButtonText={t('Shortcuts')} - copyButtonAriaLabel={t('Copy code to clipboard')} - copyButtonSuccessTooltipText={t('Content copied to clipboard')} - copyButtonToolTipText={t('Copy code to clipboard')} - downloadButtonAriaLabel={t('Download code')} - downloadButtonToolTipText={t('Download code')} /> ); }; diff --git a/frontend/packages/console-shared/src/components/editor/CodeEditor.scss b/frontend/packages/console-shared/src/components/editor/CodeEditor.scss index ad68e269a60..ba66cfa8d21 100644 --- a/frontend/packages/console-shared/src/components/editor/CodeEditor.scss +++ b/frontend/packages/console-shared/src/components/editor/CodeEditor.scss @@ -1,16 +1,5 @@ @import '../../../../../public/style/vars'; -// match the token in theme.ts. it already matches for light theme -.pf-v6-theme-dark { - .pf-v6-c-code-editor__main { - --pf-v6-c-code-editor__main--BackgroundColor: var(--pf-t--color--gray--90); - } - - .pf-v6-c-code-editor__tab { - --pf-v6-c-code-editor__tab--BackgroundColor: var(--pf-t--color--gray--90); - } -} - .ocs-yaml-editor { /* Makes automaticLayout work properly. note that this breaks height="sizeToFit" */ .monaco-editor { @@ -36,10 +25,14 @@ } /* Dividers in toolbar */ - .ocs-yaml-editor-toolbar__link:not(:last-child) { - border-inline-end: var(--pf-t--global--border--width--divider--default) solid var(--pf-t--global--border--color--default); - margin-inline-end: var(--pf-t--global--spacer--sm); - padding-inline-end: var(--pf-t--global--spacer--sm); + .ocs-yaml-editor-toolbar__links { + padding-inline-start: var(--pf-t--global--spacer--xs); + + .ocs-yaml-editor-toolbar__link:not(:last-of-type) { + border-inline-end: var(--pf-t--global--border--width--divider--default) solid var(--pf-t--global--border--color--default); + margin-inline-end: var(--pf-t--global--spacer--sm); + padding-inline-end: var(--pf-t--global--spacer--sm); + } } /* Accessibility shortcuts hint styling */ diff --git a/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx b/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx index b950bac3388..01e19bf05a2 100644 --- a/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx +++ b/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx @@ -1,9 +1,10 @@ import * as React from 'react'; import { EditorDidMount, Language } from '@patternfly/react-code-editor'; +import classNames from 'classnames'; import type * as monaco from 'monaco-editor'; import { CodeEditorRef, CodeEditorProps } from '@console/dynamic-plugin-sdk'; import { BasicCodeEditor } from './BasicCodeEditor'; -import CodeEditorToolbar from './CodeEditorToolbar'; +import { CodeEditorToolbar } from './CodeEditorToolbar'; import { useShortcutLink } from './ShortcutsLink'; import { useConsoleMonacoTheme } from './theme'; import { registerYAMLinMonaco, registerAutoFold, defaultEditorOptions } from './yaml-editor-utils'; @@ -74,7 +75,7 @@ const CodeEditor = React.forwardRef((props, ref) return ( = ({ showShortcuts, toolbarLinks }) => { - const { t } = useTranslation(); +export const AskOpenShiftLightspeedButton: React.FC = () => { + const { t } = useTranslation('console-shared'); const openOLS = () => action(ActionType.OpenOLS); const showLightspeedButton = useOLSConfig(); const dispatch = useDispatch(); + + return showLightspeedButton ? ( + + ) : null; +}; + +export const CodeEditorToolbar: React.FC = ({ + showShortcuts, + toolbarLinks, +}) => { + const { t } = useTranslation('console-shared'); if (!showShortcuts && !toolbarLinks?.length) return null; return ( - - - {showLightspeedButton && ( - - )} - {toolbarLinks && - toolbarLinks.map((link, index) => ( - // eslint-disable-next-line react/no-array-index-key -
- {link} -
- ))} -
+ <> + - - - {isMac ? '⌥ Opt' : 'Alt'} - F1 - - - {t('console-shared~Accessibility help')} - - -
+ + + {toolbarLinks && + toolbarLinks.map((link, index) => ( + // eslint-disable-next-line react/no-array-index-key +
+ {link} +
+ ))} +
+ + + + {isMac ? '⌥ Opt' : 'Alt'} + F1 + + {t('Accessibility help')} + +
+ ); }; export default CodeEditorToolbar; diff --git a/frontend/packages/console-shared/src/components/editor/__tests__/CodeEditorToolbar.spec.tsx b/frontend/packages/console-shared/src/components/editor/__tests__/CodeEditorToolbar.spec.tsx index 83aa9ee1b78..a016d6bcd0b 100644 --- a/frontend/packages/console-shared/src/components/editor/__tests__/CodeEditorToolbar.spec.tsx +++ b/frontend/packages/console-shared/src/components/editor/__tests__/CodeEditorToolbar.spec.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; import { ActionType } from '@console/internal/reducers/ols'; import { useOLSConfig } from '../../../hooks/ols-hook'; -import CodeEditorToolbar from '../CodeEditorToolbar'; +import { AskOpenShiftLightspeedButton, CodeEditorToolbar } from '../CodeEditorToolbar'; jest.mock('react-i18next', () => ({ useTranslation: jest.fn(), @@ -44,19 +44,19 @@ describe('CodeEditorToolbar', () => { it('should render "Ask OpenShift Lightspeed" button when showLightspeedButton is true', () => { (useOLSConfig as jest.Mock).mockReturnValue(true); - wrapper = shallow(); + wrapper = shallow(); expect(wrapper.find(Button).prop('children')).toBe('console-shared~Ask OpenShift Lightspeed'); }); it('should not render "Ask OpenShift Lightspeed" button when showLightspeedButton is false', () => { (useOLSConfig as jest.Mock).mockReturnValue(false); - wrapper = shallow(); + wrapper = shallow(); expect(wrapper.find(Button).exists()).toBe(false); }); it('should dispatch OpenOLS action when "Ask OpenShift Lightspeed" button is clicked', () => { (useOLSConfig as jest.Mock).mockReturnValue(true); - wrapper = shallow(); + wrapper = shallow(); wrapper.find(Button).simulate('click'); expect(mockDispatch).toHaveBeenCalledWith({ type: ActionType.OpenOLS }); }); diff --git a/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts b/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts index 43b972b3b01..c183be5dc43 100644 --- a/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts +++ b/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts @@ -6,7 +6,6 @@ import { openAPItoJSONSchema } from '@console/internal/module/k8s/openapi-to-jso import { getSwaggerDefinitions } from '@console/internal/module/k8s/swagger'; export const defaultEditorOptions: monaco.editor.IEditorOptions = { - readOnly: false, scrollBeyondLastLine: false, automaticLayout: true, }; diff --git a/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx b/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx index 7d175b596a5..f53f6d810f8 100644 --- a/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx +++ b/frontend/packages/dev-console/src/components/buildconfig/sections/EditorField.tsx @@ -1,20 +1,18 @@ import * as React from 'react'; -import { ChangeHandler, Language } from '@patternfly/react-code-editor'; +import { ChangeHandler } from '@patternfly/react-code-editor'; import { FormGroup, FormHelperText, HelperText, HelperTextItem } from '@patternfly/react-core'; import { FormikValues, useFormikContext } from 'formik'; -import type * as Monaco from 'monaco-editor/esm/vs/editor/editor.api'; +import { BasicCodeEditorProps } from '@console/dynamic-plugin-sdk'; import { RedExclamationCircleIcon, useDebounceCallback } from '@console/shared/src'; import { BasicCodeEditor } from '@console/shared/src/components/editor/BasicCodeEditor'; -type EditorFieldProps = { +type EditorFieldProps = Partial & { name: string; label?: React.ReactNode; helpText?: React.ReactNode; required?: boolean; isDisabled?: boolean; onChange?: ChangeHandler; - language?: Language; - options?: Monaco.editor.IEditorOptions; }; const EditorField: React.FC = ({ @@ -40,15 +38,12 @@ const EditorField: React.FC = ({ return ( From 166e3921ba359f1a880bed80899a3bdc189a328a Mon Sep 17 00:00:00 2001 From: logonoff Date: Thu, 13 Feb 2025 17:13:29 -0500 Subject: [PATCH 11/13] CONSOLE-4407: Downgrade `monaco-editor` to `0.51.0` Unfortunately `monaco-editor` 0.52.2 causes e2e tests to flake: see https://github.com/microsoft/monaco-editor/issues/4702 --- frontend/package.json | 2 +- frontend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 3e21032e2fc..101c7ec4591 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -303,7 +303,7 @@ "mochawesome-merge": "^4.1.0", "mochawesome-report-generator": "^5.1.0", "mock-socket": "^9.0.3", - "monaco-editor": "^0.52.2", + "monaco-editor": "^0.51.0", "monaco-editor-webpack-plugin": "^7.1.0", "prettier": "2.0.5", "react-refresh": "^0.10.0", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 7fe79dc2f9f..38e9f319496 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -12394,10 +12394,10 @@ monaco-editor-webpack-plugin@^7.1.0: dependencies: loader-utils "^2.0.2" -monaco-editor@^0.52.2: - version "0.52.2" - resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.52.2.tgz#53c75a6fcc6802684e99fd1b2700299857002205" - integrity sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ== +monaco-editor@^0.51.0: + version "0.51.0" + resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.51.0.tgz#922a6103f6742b5a62fbb097276c5a6619d879db" + integrity sha512-xaGwVV1fq343cM7aOYB6lVE4Ugf0UyimdD/x5PWcWBMKENwectaEu77FAN7c5sFiyumqeJdX1RPTh1ocioyDjw== monaco-languageserver-types@^0.4.0: version "0.4.0" From b515d0e0fa46624bc9b822cc2b71861b69c61f44 Mon Sep 17 00:00:00 2001 From: logonoff Date: Thu, 13 Feb 2025 17:14:33 -0500 Subject: [PATCH 12/13] CONSOLE-4407: Style YAML hover tooltips to match OCP 4.18 --- .../src/components/editor/CodeEditor.scss | 13 ++++++++- .../src/components/editor/CodeEditor.tsx | 28 ++++++++++--------- .../components/editor/yaml-editor-utils.ts | 2 +- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/frontend/packages/console-shared/src/components/editor/CodeEditor.scss b/frontend/packages/console-shared/src/components/editor/CodeEditor.scss index ba66cfa8d21..d52a6e810d9 100644 --- a/frontend/packages/console-shared/src/components/editor/CodeEditor.scss +++ b/frontend/packages/console-shared/src/components/editor/CodeEditor.scss @@ -1,9 +1,20 @@ @import '../../../../../public/style/vars'; .ocs-yaml-editor { - /* Makes automaticLayout work properly. note that this breaks height="sizeToFit" */ .monaco-editor { + // Makes automaticLayout work properly. note that this breaks height="sizeToFit" position: absolute !important; + + .monaco-hover-content .markdown-hover { + // matches tooltip styling seen back in OpenShift 4.18 + max-width: 500px; + word-wrap: break-word; + + // hide "Source: yaml" in hover tooltip + p:last-of-type { + display: none; + } + } } /* Hide CodeEditor toolbar on mobile */ diff --git a/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx b/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx index 01e19bf05a2..c383300dd54 100644 --- a/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx +++ b/frontend/packages/console-shared/src/components/editor/CodeEditor.tsx @@ -3,6 +3,7 @@ import { EditorDidMount, Language } from '@patternfly/react-code-editor'; import classNames from 'classnames'; import type * as monaco from 'monaco-editor'; import { CodeEditorRef, CodeEditorProps } from '@console/dynamic-plugin-sdk'; +import { ErrorBoundaryInline } from '@console/shared/src/components/error'; import { BasicCodeEditor } from './BasicCodeEditor'; import { CodeEditorToolbar } from './CodeEditorToolbar'; import { useShortcutLink } from './ShortcutsLink'; @@ -17,7 +18,6 @@ const CodeEditor = React.forwardRef((props, ref) showShortcuts, toolbarLinks, onSave, - options, language, onEditorDidMount, } = props; @@ -73,18 +73,20 @@ const CodeEditor = React.forwardRef((props, ref) }, [toolbarLinks, showShortcuts]); return ( - + + + ); }); diff --git a/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts b/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts index c183be5dc43..de4f61ca8bd 100644 --- a/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts +++ b/frontend/packages/console-shared/src/components/editor/yaml-editor-utils.ts @@ -7,7 +7,7 @@ import { getSwaggerDefinitions } from '@console/internal/module/k8s/swagger'; export const defaultEditorOptions: monaco.editor.IEditorOptions = { scrollBeyondLastLine: false, - automaticLayout: true, + automaticLayout: true, // paired with position: absolute for auto-resizing }; const findManagedMetadata = (model: monaco.editor.ITextModel) => { From c14790b67abb7e9a307587c08f752cdf1a642e1e Mon Sep 17 00:00:00 2001 From: logonoff Date: Thu, 13 Feb 2025 17:44:20 -0500 Subject: [PATCH 13/13] CONSOLE-4407: Use `ResizeObserver` over automaticLayout `automaticLayout` has a few problems in this older version, see https://github.com/microsoft/monaco-editor/issues/4277 --- .../src/components/editor/BasicCodeEditor.tsx | 17 +++++++++++++++++ .../src/components/editor/CodeEditor.scss | 3 --- .../src/components/editor/yaml-editor-utils.ts | 1 - 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/frontend/packages/console-shared/src/components/editor/BasicCodeEditor.tsx b/frontend/packages/console-shared/src/components/editor/BasicCodeEditor.tsx index e23b0560e58..515f87a9c13 100644 --- a/frontend/packages/console-shared/src/components/editor/BasicCodeEditor.tsx +++ b/frontend/packages/console-shared/src/components/editor/BasicCodeEditor.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { loader, Monaco } from '@monaco-editor/react'; import { CodeEditor } from '@patternfly/react-code-editor'; +import { getResizeObserver } from '@patternfly/react-core'; import classNames from 'classnames'; import * as monaco from 'monaco-editor'; import { useTranslation } from 'react-i18next'; @@ -23,6 +24,22 @@ export const BasicCodeEditor: React.FC = (props) => { const [monacoRef, setMonacoRef] = React.useState(null); useConsoleMonacoTheme(monacoRef?.editor); + // TODO(PF6): remove this when https://github.com/patternfly/patternfly-react/issues/11531 is fixed + const handleResize = React.useCallback(() => { + monacoRef?.editor?.getEditors()?.forEach((editor) => { + editor.layout({ width: 0, height: 0 }); + editor.layout(); + }); + }, [monacoRef]); + + React.useEffect(() => { + const observer = getResizeObserver(undefined, handleResize, true); + + return () => { + observer(); + }; + }, [handleResize]); + return ( {