Skip to content

Commit 10e076a

Browse files
Merge pull request #3437 from RedisInsight/fe/feature/RI-5530_add_sql_editor_btn
#RI-5530 - add sql editor btn
2 parents 645c976 + 6899146 commit 10e076a

File tree

11 files changed

+209
-25
lines changed

11 files changed

+209
-25
lines changed

redisinsight/__mocks__/monacoMock.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,19 @@ const editor = {
1616
updateOptions: jest.fn(),
1717
setSelection: jest.fn(),
1818
createDecorationsCollection: jest.fn(),
19+
getValue: jest.fn().mockReturnValue(''),
20+
getModel: jest.fn().mockReturnValue({}),
21+
getPosition: jest.fn(),
22+
trigger: jest.fn(),
1923
}
2024

2125
const monacoEditor = {
22-
Range: jest.fn().mockImplementation(() => ({})),
26+
Range: jest.fn().mockImplementation((startLineNumber, startColumn, endLineNumber, endColumn) => ({
27+
startLineNumber,
28+
startColumn,
29+
endLineNumber,
30+
endColumn,
31+
})),
2332
languages: {
2433
getLanguages: jest.fn(),
2534
register: jest.fn(),
@@ -71,5 +80,6 @@ export const monaco = {
7180
colorize: jest.fn().mockImplementation((data) => Promise.resolve(data)),
7281
defineTheme: jest.fn(),
7382
setTheme: jest.fn()
74-
}
83+
},
84+
Range: monacoEditor.Range
7585
}

redisinsight/ui/src/components/monaco-editor/MonacoEditor.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ export interface CommonProps {
3131
dedicatedEditorKeywords?: string[]
3232
dedicatedEditorFunctions?: monacoEditor.languages.CompletionItem[]
3333
onChangeLanguage?: (langId: DSL) => void
34+
shouldOpenDedicatedEditor?: boolean
35+
onOpenDedicatedEditor?: () => void
36+
onSubmitDedicatedEditor?: (langId: DSL) => void
37+
onCloseDedicatedEditor?: (langId: DSL) => void
3438
'data-testid'?: string
3539
}
3640

@@ -60,6 +64,10 @@ const MonacoEditor = (props: Props) => {
6064
dedicatedEditorLanguages = [],
6165
dedicatedEditorKeywords = [],
6266
dedicatedEditorFunctions = [],
67+
shouldOpenDedicatedEditor,
68+
onOpenDedicatedEditor,
69+
onSubmitDedicatedEditor,
70+
onCloseDedicatedEditor,
6371
'data-testid': dataTestId = 'monaco-editor'
6472
} = props
6573

@@ -82,6 +90,13 @@ const MonacoEditor = (props: Props) => {
8290
monacoObjects.current?.editor.updateOptions({ readOnly: !isEditing && (disabled || readOnly) })
8391
}, [disabled, readOnly, isEditing])
8492

93+
useEffect(() => {
94+
if (shouldOpenDedicatedEditor) {
95+
setIsDedicatedEditorOpen(true)
96+
onOpenDedicatedEditor?.()
97+
}
98+
}, [shouldOpenDedicatedEditor])
99+
85100
const editorDidMount = (
86101
editor: monacoEditor.editor.IStandaloneCodeEditor,
87102
monaco: typeof monacoEditor,
@@ -130,6 +145,7 @@ const MonacoEditor = (props: Props) => {
130145
const { editor } = monacoObjects?.current
131146

132147
setIsDedicatedEditorOpen(true)
148+
onOpenDedicatedEditor?.()
133149
editor.updateOptions({ readOnly: true })
134150
}
135151

@@ -140,13 +156,12 @@ const MonacoEditor = (props: Props) => {
140156
editor.focus()
141157
}
142158

143-
const updateArgFromDedicatedEditor = (value: string = '') => {
159+
const updateArgFromDedicatedEditor = (value: string, selectedLang: DSL) => {
144160
if (!monacoObjects.current) return
145161
const { editor } = monacoObjects?.current
146162

147163
const model = editor.getModel()
148164
if (!model) return
149-
150165
const position = editor.getPosition()
151166

152167
editor.updateOptions({ readOnly: false })
@@ -163,15 +178,17 @@ const MonacoEditor = (props: Props) => {
163178
])
164179
setIsDedicatedEditorOpen(false)
165180
triggerUpdateCursorPosition(editor)
181+
onSubmitDedicatedEditor?.(selectedLang)
166182
}
167183

168-
const onCancelDedicatedEditor = () => {
184+
const onCancelDedicatedEditor = (selectedLang: DSL) => {
169185
setIsDedicatedEditorOpen(false)
170186
if (!monacoObjects.current) return
171187
const { editor } = monacoObjects?.current
172188

173189
editor.updateOptions({ readOnly: false })
174190
triggerUpdateCursorPosition(editor)
191+
onCloseDedicatedEditor?.(selectedLang)
175192
}
176193

177194
if (monacoEditor?.editor) {

redisinsight/ui/src/components/monaco-editor/components/dedicated-editor/DedicatedEditor.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ export interface Props {
2424
langId?: DSL
2525
langs?: DSL[]
2626
onChangeLanguage?: (langId: DSL) => void
27-
onSubmit: (query?: string) => void
28-
onCancel: () => void
27+
onSubmit: (query: string, langId: DSL) => void
28+
onCancel: (langId: DSL) => void
2929
initialHeight: number
3030
customOptions?: monacoEditor.editor.IStandaloneEditorConstructionOptions
3131
keywords?: string[]
@@ -109,7 +109,7 @@ const DedicatedEditor = (props: Props) => {
109109

110110
const handleKeyDown = (e: React.KeyboardEvent) => {
111111
if (e.key === 'Escape') {
112-
onCancel()
112+
onCancel(selectedLang.id as DSL)
113113
}
114114
}
115115

@@ -119,7 +119,7 @@ const DedicatedEditor = (props: Props) => {
119119
.split('\n')
120120
.map((line: string, i: number) => ((i > 0 && !notCommandRegEx.test(line)) ? `\t${line}` : line))
121121
.join('\n')
122-
onSubmit(val || '')
122+
onSubmit(val || '', selectedLang.id as DSL)
123123
}
124124

125125
const editorDidMount = (
@@ -258,7 +258,7 @@ const DedicatedEditor = (props: Props) => {
258258
color="primary"
259259
aria-label="Cancel editing"
260260
className={styles.declineBtn}
261-
onClick={onCancel}
261+
onClick={() => onCancel(selectedLang.id as DSL)}
262262
data-testid="cancel-btn"
263263
/>
264264
<EuiButtonIcon

redisinsight/ui/src/components/query/Query/Query.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ const Query = (props: Props) => {
466466
>
467467
<div className={styles.input} data-testid="query-input-container" ref={input}>
468468
<MonacoEditor
469-
language={MonacoLanguage.Redis}
469+
language={MonacoLanguage.Redis as string}
470470
theme={theme === Theme.Dark ? 'dark' : 'light'}
471471
value={query}
472472
options={options}

redisinsight/ui/src/constants/keyboardShortcuts.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ const COMMON_SHORTCUTS = {
6262
description: 'Use Non-Redis Editor',
6363
keys: ['Shift', 'Space'],
6464
}
65+
},
66+
rdi: {
67+
openDedicatedEditor: {
68+
description: 'Open a dedicated SQL or JMESPath editor:',
69+
keys: ['Shift', 'Space']
70+
}
6571
}
6672
}
6773

@@ -125,6 +131,12 @@ const MAC_SHORTCUTS = {
125131
description: 'Use Non-Redis Editor',
126132
keys: [(<span className="shiftSymbol"></span>), 'Space'],
127133
}
134+
},
135+
rdi: {
136+
openDedicatedEditor: {
137+
description: 'Open a dedicated SQL or JMESPath editor',
138+
keys: [(<span className="shiftSymbol"></span>), 'Space']
139+
}
128140
}
129141
}
130142

redisinsight/ui/src/pages/rdi/pipeline-management/components/fetch-pipeline-popover/FetchPipelinePopover.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ const FetchPipelinePopover = () => {
7676
aria-labelledby="Upload pipeline button"
7777
data-testid="upload-pipeline-btn"
7878
>
79-
Upload from server
79+
Download from server
8080
</EuiButtonEmpty>
8181
)}
8282
onButtonClick={handleRefreshWarning}

redisinsight/ui/src/pages/rdi/pipeline-management/components/source-pipeline-dialog/SourcePipelineModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ const SourcePipelineDialog = () => {
103103
data-testid="server-source-pipeline-dialog"
104104
>
105105
<EuiIcon type={UploadIcon} size="xl" className={styles.icon} />
106-
<EuiText className={styles.text}>Upload from server</EuiText>
106+
<EuiText className={styles.text}>Download from server</EuiText>
107107
</div>
108108
<div
109109
role="button"

redisinsight/ui/src/pages/rdi/pipeline-management/pages/job/Job.spec.tsx

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ import {
99
setChangedFile,
1010
deleteChangedFile,
1111
} from 'uiSrc/slices/rdi/pipeline'
12-
import { cleanup, fireEvent, mockedStore, render, screen } from 'uiSrc/utils/test-utils'
12+
import { act, cleanup, fireEvent, mockedStore, render, screen } from 'uiSrc/utils/test-utils'
1313

1414
import { MOCK_RDI_PIPELINE_DATA } from 'uiSrc/mocks/data/rdi'
1515
import { FileChangeType } from 'uiSrc/slices/interfaces'
16+
import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
1617
import Job, { Props } from './Job'
1718

1819
const mockedProps = mock<Props>()
@@ -135,6 +136,73 @@ describe('Job', () => {
135136
expect(store.getActions()).toEqual(expectedActions)
136137
})
137138

139+
it('should open dedicated editor', () => {
140+
render(<Job {...instance(mockedProps)} deployedJobValue="value" name="jobName" />)
141+
142+
expect(screen.queryByTestId('draggable-area')).not.toBeInTheDocument()
143+
144+
fireEvent.click(screen.getByTestId('open-dedicated-editor-btn'))
145+
146+
expect(screen.getByTestId('draggable-area')).toBeInTheDocument()
147+
})
148+
149+
it('should call proper telemetry events on open dedicated editor', () => {
150+
const sendEventTelemetryMock = jest.fn();
151+
(sendEventTelemetry as jest.Mock).mockImplementation(() => sendEventTelemetryMock)
152+
153+
render(<Job {...instance(mockedProps)} value="value" rdiInstanceId="id" />)
154+
155+
fireEvent.click(screen.getByTestId('open-dedicated-editor-btn'))
156+
157+
expect(sendEventTelemetry).toBeCalledWith({
158+
event: TelemetryEvent.RDI_DEDICATED_EDITOR_OPENED,
159+
eventData: {
160+
rdiInstanceId: 'id'
161+
}
162+
});
163+
(sendEventTelemetry as jest.Mock).mockRestore()
164+
})
165+
166+
it('should call proper telemetry events on cancel dedicated editor', () => {
167+
const sendEventTelemetryMock = jest.fn();
168+
(sendEventTelemetry as jest.Mock).mockImplementation(() => sendEventTelemetryMock)
169+
170+
render(<Job {...instance(mockedProps)} value="value" rdiInstanceId="id" />)
171+
172+
fireEvent.click(screen.getByTestId('open-dedicated-editor-btn'))
173+
fireEvent.click(screen.getByTestId('cancel-btn'))
174+
175+
expect(sendEventTelemetry).toBeCalledWith({
176+
event: TelemetryEvent.RDI_DEDICATED_EDITOR_CANCELLED,
177+
eventData: {
178+
rdiInstanceId: 'id',
179+
selectedLanguageSyntax: 'sql',
180+
}
181+
});
182+
(sendEventTelemetry as jest.Mock).mockRestore()
183+
})
184+
185+
it('should call proper telemetry events on submit dedicated editor', async () => {
186+
const sendEventTelemetryMock = jest.fn();
187+
(sendEventTelemetry as jest.Mock).mockImplementation(() => sendEventTelemetryMock)
188+
189+
render(<Job {...instance(mockedProps)} value="value" rdiInstanceId="id" />)
190+
191+
fireEvent.click(screen.getByTestId('open-dedicated-editor-btn'))
192+
await act(() => {
193+
fireEvent.click(screen.getByTestId('apply-btn'))
194+
})
195+
196+
expect(sendEventTelemetry).toBeCalledWith({
197+
event: TelemetryEvent.RDI_DEDICATED_EDITOR_SAVED,
198+
eventData: {
199+
rdiInstanceId: 'id',
200+
selectedLanguageSyntax: 'sql',
201+
}
202+
});
203+
(sendEventTelemetry as jest.Mock).mockRestore()
204+
})
205+
138206
it('should render loading spinner', () => {
139207
const rdiPipelineSelectorMock = jest.fn().mockReturnValue({
140208
loading: true,

0 commit comments

Comments
 (0)