|
| 1 | +/* Copyright Contributors to the Open Cluster Management project */ |
| 2 | +import React from 'react' |
| 3 | +import { fireEvent } from '@testing-library/react' |
| 4 | + |
| 5 | +class Range { |
| 6 | + startLineNumber: number | undefined |
| 7 | + endLineNumber: number | undefined |
| 8 | + endColumn: number | undefined |
| 9 | + startColumn: number | undefined |
| 10 | + constructor(startLineNumber?: number, startColumn?: number, endLineNumber?: number, endColumn?: number) { |
| 11 | + this.endLineNumber = endLineNumber |
| 12 | + this.endColumn = endColumn |
| 13 | + this.startLineNumber = startLineNumber |
| 14 | + this.startColumn = startColumn |
| 15 | + } |
| 16 | + containsPosition(position) { |
| 17 | + return Range.containsPosition(this, position) |
| 18 | + } |
| 19 | + static containsPosition(range, position) { |
| 20 | + if (position.lineNumber < range.startLineNumber || position.lineNumber > range.endLineNumber) { |
| 21 | + return false |
| 22 | + } |
| 23 | + if (position.lineNumber === range.startLineNumber && position.column < range.startColumn) { |
| 24 | + return false |
| 25 | + } |
| 26 | + if (position.lineNumber === range.endLineNumber && position.column > range.endColumn) { |
| 27 | + return false |
| 28 | + } |
| 29 | + return true |
| 30 | + } |
| 31 | +} |
| 32 | +class Selection { |
| 33 | + startLineNumber: number | undefined |
| 34 | + selectionStartLineNumber: number | undefined |
| 35 | + selectionStartColumn: number | undefined |
| 36 | + endLineNumber: number | undefined |
| 37 | + endColumn: number | undefined |
| 38 | + startColumn: number | undefined |
| 39 | + constructor(startLineNumber?: number, startColumn?: number, endLineNumber?: number, endColumn?: number) { |
| 40 | + this.endLineNumber = endLineNumber |
| 41 | + this.endColumn = endColumn |
| 42 | + this.startLineNumber = startLineNumber |
| 43 | + this.selectionStartLineNumber = startLineNumber |
| 44 | + this.startColumn = startColumn |
| 45 | + this.selectionStartColumn = startColumn |
| 46 | + } |
| 47 | +} |
| 48 | + |
| 49 | +interface MockModel { |
| 50 | + _commandManager: { |
| 51 | + future: any[] |
| 52 | + past: any[] |
| 53 | + } |
| 54 | + forceTokenization: () => void |
| 55 | + getValue: () => string |
| 56 | + setValue: (value: string) => void |
| 57 | + getLineContent: (line: number) => string |
| 58 | + getAllDecorations: () => any[] |
| 59 | + getLineCount: () => void |
| 60 | + getFullModelRange: () => void |
| 61 | + getValueInRange: () => string |
| 62 | + canUndo: () => boolean |
| 63 | + canRedo: () => boolean |
| 64 | + onDidChangeContent: () => void |
| 65 | + findMatches: (find: string) => { range: Range }[] |
| 66 | +} |
| 67 | + |
| 68 | +interface MockEditor { |
| 69 | + layout: () => void |
| 70 | + focus: () => void |
| 71 | + trigger: () => void |
| 72 | + onKeyDown: () => void |
| 73 | + onMouseDown: () => void |
| 74 | + getVisibleRanges: () => any[] |
| 75 | + onDidBlurEditorWidget: () => void |
| 76 | + changeViewZones: () => void |
| 77 | + getDomNode: () => HTMLElement |
| 78 | + addCommand: () => void |
| 79 | + getSelection: () => void |
| 80 | + setSelection: () => void |
| 81 | + setSelections: () => void |
| 82 | + setTheme: (theme: any) => void |
| 83 | + saveViewState: () => void |
| 84 | + restoreViewState: () => any |
| 85 | + revealLineInCenter: () => void |
| 86 | + onDidChangeModelContent: (cb: any) => void |
| 87 | + deltaDecorations: () => void |
| 88 | + getModel: () => MockModel |
| 89 | + getValue: () => string |
| 90 | + executeEdits: (id: string, edits: [{ range: Range; text: string }]) => void |
| 91 | +} |
| 92 | + |
| 93 | +interface MockMonaco { |
| 94 | + editor: { setModelLanguage: () => void; defineTheme: () => void; setTheme: () => void } |
| 95 | + languages: { registerHoverProvider: () => void } |
| 96 | + KeyMod: any |
| 97 | + KeyCode: any |
| 98 | + Range: Range |
| 99 | + Selection: Selection |
| 100 | +} |
| 101 | + |
| 102 | +const MonacoEditor = (props: { |
| 103 | + value: string |
| 104 | + onChange(value: string, e: any): unknown |
| 105 | + onMount: (editor: MockEditor, monaco: MockMonaco) => void |
| 106 | + wrapperClassName: any |
| 107 | +}) => { |
| 108 | + const editorMockRef = React.useRef<any | null>(null) |
| 109 | + if (!editorMockRef.current) { |
| 110 | + editorMockRef.current = {} |
| 111 | + editorMockRef.current.lastTypeInx = -1 |
| 112 | + editorMockRef.current.undoStack = [props.value] |
| 113 | + editorMockRef.current.redoStack = [] |
| 114 | + editorMockRef.current.editorContent = props.value |
| 115 | + const model: MockModel = { |
| 116 | + _commandManager: { |
| 117 | + future: ['future'], |
| 118 | + past: ['past'], |
| 119 | + }, |
| 120 | + forceTokenization: () => {}, |
| 121 | + getLineCount: () => { |
| 122 | + const text = editorMockRef.current.editorContent |
| 123 | + return text.trim().split('\n').length |
| 124 | + }, |
| 125 | + getFullModelRange: () => {}, |
| 126 | + canUndo: () => true, |
| 127 | + canRedo: () => true, |
| 128 | + getValue: () => { |
| 129 | + return editorMockRef.current.editorContent |
| 130 | + }, |
| 131 | + setValue: (value: string) => { |
| 132 | + editorMockRef.current.editorContent = value |
| 133 | + editorMockRef.current.undoStack = [value] |
| 134 | + }, |
| 135 | + getLineContent: (line) => { |
| 136 | + return editorMockRef.current.editorContent.split('\n')[line] |
| 137 | + }, |
| 138 | + getAllDecorations: () => [], |
| 139 | + getValueInRange: () => '', |
| 140 | + onDidChangeContent: () => {}, |
| 141 | + findMatches: (find: string) => { |
| 142 | + return find === 'that' ? [{ range: new Range(0, 0, 0, 1) }, { range: new Range(0, 1, 0, 2) }] : [] |
| 143 | + }, |
| 144 | + } |
| 145 | + editorMockRef.current.mockEditor = { |
| 146 | + layout: () => {}, |
| 147 | + focus: () => {}, |
| 148 | + trigger: (_source, action) => { |
| 149 | + switch (action) { |
| 150 | + case 'undo': |
| 151 | + editorMockRef.current.undoRedo = true |
| 152 | + editorMockRef.current.redoStack.push(editorMockRef.current.undoStack.pop()) |
| 153 | + fireEvent.change(editorMockRef.current.textArea, { |
| 154 | + target: { |
| 155 | + value: editorMockRef.current.undoStack[editorMockRef.current.undoStack.length - 1], |
| 156 | + }, |
| 157 | + }) |
| 158 | + break |
| 159 | + case 'redo': |
| 160 | + editorMockRef.current.undoRedo = true |
| 161 | + const value = editorMockRef.current.redoStack.pop() |
| 162 | + editorMockRef.current.undoStack.push(value) |
| 163 | + fireEvent.change(editorMockRef.current.textArea, { |
| 164 | + target: { value }, |
| 165 | + }) |
| 166 | + break |
| 167 | + } |
| 168 | + }, |
| 169 | + onKeyDown: (handler) => { |
| 170 | + editorMockRef.current.onKeyDown = handler |
| 171 | + }, |
| 172 | + onClick: () => {}, |
| 173 | + onMouseDown: (handler) => { |
| 174 | + editorMockRef.current.onMouseDown = handler |
| 175 | + }, |
| 176 | + onDidBlurEditorWidget: (handler) => { |
| 177 | + editorMockRef.current.onDidBlurEditorWidget = handler |
| 178 | + }, |
| 179 | + getVisibleRanges: () => [], |
| 180 | + addCommand: () => {}, |
| 181 | + changeViewZones: () => {}, |
| 182 | + getDomNode: () => { |
| 183 | + return editorMockRef.current.textArea |
| 184 | + }, |
| 185 | + getSelection: () => { |
| 186 | + const ta = editorMockRef.current.textArea |
| 187 | + const value = ta.value |
| 188 | + const startLines = value.slice(0, ta.selectionStart).split('\n') |
| 189 | + const startLineNumber = startLines.length - 1 |
| 190 | + const startColumn = startLines[startLineNumber].length + 1 |
| 191 | + const endLines = value.slice(0, ta.selectionEnd).split('\n') |
| 192 | + const endLineNumber = endLines.length - 1 |
| 193 | + const endColumn = ta.selectionEnd - startLines.join('').length |
| 194 | + return new Selection(startLineNumber, startColumn, endLineNumber, endColumn) |
| 195 | + }, |
| 196 | + setSelection: () => {}, |
| 197 | + setSelections: () => {}, |
| 198 | + saveViewState: () => null, |
| 199 | + setTheme: () => null, |
| 200 | + restoreViewState: () => {}, |
| 201 | + revealLineInCenter: () => {}, |
| 202 | + onDidChangeModelContent: (cb: any) => { |
| 203 | + editorMockRef.current.changeModelCallback = cb |
| 204 | + }, |
| 205 | + deltaDecorations: (_oldDecorations: string[], newDecorations: any[]) => { |
| 206 | + editorMockRef.current.newDecorations = JSON.stringify(newDecorations) |
| 207 | + }, |
| 208 | + getModel: () => model, |
| 209 | + getValue: () => { |
| 210 | + return editorMockRef.current.editorContent |
| 211 | + }, |
| 212 | + executeEdits: (id, edits) => { |
| 213 | + const { text } = edits[0] |
| 214 | + const ta = editorMockRef.current.textArea |
| 215 | + const v = editorMockRef.current.textArea.value |
| 216 | + editorMockRef.current.textArea.value = |
| 217 | + v.substring(0, ta.selectionStart) + text + v.substring(ta.selectionEnd, v.length) |
| 218 | + }, |
| 219 | + } |
| 220 | + editorMockRef.current.mockMonaco = { |
| 221 | + editor: { setModelLanguage: () => {}, defineTheme: () => {}, setTheme: () => {} }, |
| 222 | + languages: { registerHoverProvider: () => {} }, |
| 223 | + KeyMod: {}, |
| 224 | + KeyCode: {}, |
| 225 | + Range: Range, |
| 226 | + Selection: Selection, |
| 227 | + } |
| 228 | + props.onMount(editorMockRef.current.mockEditor, editorMockRef.current.mockMonaco) |
| 229 | + } |
| 230 | + return ( |
| 231 | + <textarea |
| 232 | + aria-label="monaco" |
| 233 | + data-auto={props.wrapperClassName} |
| 234 | + data-decorators={editorMockRef.current.newDecorations} |
| 235 | + className="monaco-editor" |
| 236 | + ref={(ref) => { |
| 237 | + editorMockRef.current.textArea = ref |
| 238 | + }} |
| 239 | + onClick={(e) => {}} |
| 240 | + onMouseDown={(e) => { |
| 241 | + const editorEvent = { |
| 242 | + target: { |
| 243 | + position: { |
| 244 | + lineNumber: 12, |
| 245 | + column: 12, |
| 246 | + }, |
| 247 | + }, |
| 248 | + } |
| 249 | + editorMockRef.current.onMouseDown(editorEvent) |
| 250 | + }} |
| 251 | + onFocus={() => { |
| 252 | + editorMockRef.current.textArea.classList.add('focused') |
| 253 | + }} |
| 254 | + onBlur={() => { |
| 255 | + editorMockRef.current.textArea.classList.remove('focused') |
| 256 | + editorMockRef.current.onDidBlurEditorWidget() |
| 257 | + }} |
| 258 | + onChange={(e) => { |
| 259 | + if (editorMockRef.current.undoRedo === true) { |
| 260 | + editorMockRef.current.undoRedo = false |
| 261 | + } else { |
| 262 | + if (editorMockRef.current.textArea.selectionEnd !== editorMockRef.current.lastTypeInx + 1) { |
| 263 | + editorMockRef.current.undoStack.push(e.target.value) |
| 264 | + } else { |
| 265 | + editorMockRef.current.undoStack[editorMockRef.current.undoStack.length - 1] = e.target.value |
| 266 | + } |
| 267 | + editorMockRef.current.lastTypeInx = editorMockRef.current.textArea.selectionEnd |
| 268 | + } |
| 269 | + editorMockRef.current.editorContent = e.target.value |
| 270 | + props.onChange(editorMockRef.current.editorContent, e) |
| 271 | + //editorMockRef.current.changeModelCallback() |
| 272 | + }} |
| 273 | + value={editorMockRef.current.editorContent} |
| 274 | + ></textarea> |
| 275 | + ) |
| 276 | +} |
| 277 | + |
| 278 | +export default MonacoEditor |
0 commit comments