Skip to content
Merged
2 changes: 1 addition & 1 deletion configs/webpack.config.renderer.dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ const configuration: webpack.Configuration = {
new ReactRefreshWebpackPlugin(),

new MonacoWebpackPlugin({
languages: ['yaml', 'typescript', 'javascript', 'json'],
languages: ['yaml', 'typescript', 'javascript', 'json', 'sql'],
customLanguages: [
{
label: 'yaml',
Expand Down
2 changes: 1 addition & 1 deletion configs/webpack.config.renderer.prod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ const configuration: webpack.Configuration = {

plugins: [
new MonacoWebpackPlugin({
languages: ['yaml', 'typescript', 'javascript', 'json'],
languages: ['yaml', 'typescript', 'javascript', 'json', 'sql'],
customLanguages: [
{
label: 'yaml',
Expand Down
2 changes: 1 addition & 1 deletion configs/webpack.config.web.common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export default {
new HtmlWebpackPlugin({ template: 'index.html.ejs' }),

new MonacoWebpackPlugin({
languages: ['yaml', 'typescript', 'javascript', 'json'],
languages: ['yaml', 'typescript', 'javascript', 'json', 'sql'],
customLanguages: [
{
label: 'yaml',
Expand Down
1 change: 1 addition & 0 deletions redisinsight/__mocks__/monacoMock.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const editor = {
executeEdits: jest.fn(),
updateOptions: jest.fn(),
setSelection: jest.fn(),
createDecorationsCollection: jest.fn(),
}

const monacoEditor = {
Expand Down
73 changes: 71 additions & 2 deletions redisinsight/ui/src/components/monaco-editor/MonacoEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { darkTheme, lightTheme, MonacoThemes } from 'uiSrc/constants/monaco/cyph

import { Nullable } from 'uiSrc/utils'
import { IEditorMount, ISnippetController } from 'uiSrc/pages/workbench/interfaces'
import { Theme } from 'uiSrc/constants'
import { DSL, Theme } from 'uiSrc/constants'
import { ThemeContext } from 'uiSrc/contexts/themeContext'
import InlineItemEditor from 'uiSrc/components/inline-item-editor'
import DedicatedEditor from './components/dedicated-editor'
import styles from './styles.modules.scss'

export interface CommonProps {
Expand All @@ -32,6 +33,7 @@ export interface Props extends CommonProps {
onEditorWillMount?: (monaco: typeof monacoEditor) => void
className?: string
language: string
dedicatedEditorLanguages?: DSL[]
}
const MonacoEditor = (props: Props) => {
const {
Expand All @@ -48,12 +50,15 @@ const MonacoEditor = (props: Props) => {
wrapperClassName,
className,
options = {},
dedicatedEditorLanguages = [],
'data-testid': dataTestId = 'monaco-editor'
} = props

let contribution: Nullable<ISnippetController> = null
const [isEditing, setIsEditing] = useState(!readOnly && !disabled)
const [isDedicatedEditorOpen, setIsDedicatedEditorOpen] = useState(false)
const monacoObjects = useRef<Nullable<IEditorMount>>(null)
const input = useRef<HTMLDivElement>(null)

const { theme } = useContext(ThemeContext)

Expand All @@ -79,6 +84,13 @@ const MonacoEditor = (props: Props) => {
contribution = editor.getContribution<ISnippetController>('snippetController2')

editor.onKeyDown(onKeyDownMonaco)

if (dedicatedEditorLanguages?.length) {
editor.addCommand(monaco.KeyMod.Shift | monaco.KeyCode.Space, () => {
onPressWidget()
})
}

onEditorDidMount?.(editor, monaco)
}

Expand All @@ -104,6 +116,55 @@ const MonacoEditor = (props: Props) => {
}
}

const onPressWidget = () => {
if (!monacoObjects.current) return
const { editor } = monacoObjects?.current

setIsDedicatedEditorOpen(true)
editor.updateOptions({ readOnly: true })
}

const triggerUpdateCursorPosition = (editor: monacoEditor.editor.IStandaloneCodeEditor) => {
const position = editor.getPosition()
editor.trigger('mouse', '_moveTo', { position: { lineNumber: 1, column: 1 } })
editor.trigger('mouse', '_moveTo', { position })
editor.focus()
}

const updateArgFromDedicatedEditor = (value: string = '') => {
if (!monacoObjects.current) return
const { editor } = monacoObjects?.current

const model = editor.getModel()
if (!model) return

const position = editor.getPosition()

editor.updateOptions({ readOnly: false })
editor.executeEdits(null, [
{
range: new monacoEditor.Range(
position?.lineNumber!,
position?.column!,
position?.lineNumber!,
position?.column! + value.length,
),
text: value.replaceAll('\n', ' ')
}
])
setIsDedicatedEditorOpen(false)
triggerUpdateCursorPosition(editor)
}

const onCancelDedicatedEditor = () => {
setIsDedicatedEditorOpen(false)
if (!monacoObjects.current) return
const { editor } = monacoObjects?.current

editor.updateOptions({ readOnly: false })
triggerUpdateCursorPosition(editor)
}

if (monacoEditor?.editor) {
monacoEditor.editor.defineTheme(MonacoThemes.Dark, darkTheme)
monacoEditor.editor.defineTheme(MonacoThemes.Light, lightTheme)
Expand Down Expand Up @@ -149,7 +210,7 @@ const MonacoEditor = (props: Props) => {
declineOnUnmount={false}
preventOutsideClick
>
<div className="inlineMonacoEditor" data-testid={`wrapper-${dataTestId}`}>
<div className="inlineMonacoEditor" data-testid={`wrapper-${dataTestId}`} ref={input}>
<ReactMonacoEditor
language={language}
theme={theme === Theme.Dark ? 'dark' : 'light'}
Expand All @@ -163,6 +224,14 @@ const MonacoEditor = (props: Props) => {
/>
</div>
</InlineItemEditor>
{isDedicatedEditorOpen && (
<DedicatedEditor
initialHeight={input?.current?.scrollHeight || 0}
langs={dedicatedEditorLanguages}
onSubmit={updateArgFromDedicatedEditor}
onCancel={onCancelDedicatedEditor}
/>
)}
{isEditable && readOnly && !isEditing && (
<EuiButton
fill
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import { cloneDeep } from 'lodash'
import React from 'react'
import { instance, mock } from 'ts-mockito'
import { cleanup, mockedStore, render } from 'uiSrc/utils/test-utils'
import { DSL } from 'uiSrc/constants'
import DedicatedEditor, { Props } from './DedicatedEditor'

const mockedProps = mock<Props>()
const SELECT_LANGUAGES_TEST_ID = 'dedicated-editor-language-select'

const mockedProps = mock<Props>()
let store: typeof mockedStore
beforeEach(() => {
cleanup()
Expand All @@ -25,4 +27,14 @@ describe('DedicatedEditor', () => {
it('should render', () => {
expect(render(<DedicatedEditor {...instance(mockedProps)} />)).toBeTruthy()
})
it('should not render select languages if langs.length < 2', () => {
const { queryByTestId } = render(<DedicatedEditor {...instance(mockedProps)} langs={[DSL.sql]} />)

expect(queryByTestId(SELECT_LANGUAGES_TEST_ID!)).not.toBeInTheDocument()
})
it('should render select languages if langs.length >= 2', () => {
const { queryByTestId } = render(<DedicatedEditor {...instance(mockedProps)} langs={[DSL.sql, DSL.jmespath]} />)

expect(queryByTestId(SELECT_LANGUAGES_TEST_ID!)).toBeInTheDocument()
})
})
Loading