diff --git a/package.json b/package.json index 58989df..184ae01 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "@grafana/runtime": "7.3.6", "@grafana/toolkit": "7.3.6", "@grafana/ui": "7.3.6", + "@monaco-editor/react": "^4.0.9", "@types/enzyme": "^3.10.8", "@types/enzyme-adapter-react-16": "^1.0.6", "emotion": "10.0.27", diff --git a/src/dashboards/redis-gears.json b/src/dashboards/redis-gears.json index 81dae16..2414003 100644 --- a/src/dashboards/redis-gears.json +++ b/src/dashboards/redis-gears.json @@ -23,7 +23,7 @@ "type": "panel", "id": "redis-gears-panel", "name": "RedisGears", - "version": "1.3.1" + "version": "1.1.0" }, { "type": "panel", @@ -49,7 +49,7 @@ "gnetId": null, "graphTooltip": 0, "id": null, - "iteration": 1611706223976, + "iteration": 1612064044040, "links": [], "panels": [ { @@ -200,10 +200,6 @@ { "color": "green", "value": null - }, - { - "color": "red", - "value": 80 } ] } @@ -293,18 +289,6 @@ } ] }, - { - "matcher": { - "id": "byName", - "options": "Last Error" - }, - "properties": [ - { - "id": "custom.width", - "value": 99 - } - ] - }, { "matcher": { "id": "byName", @@ -360,7 +344,8 @@ "command": "rg.dumpregistrations", "query": "", "refId": "A", - "streaming": false, + "streaming": true, + "streamingDataType": "DataFrame", "type": "gears" } ], @@ -462,5 +447,5 @@ "timezone": "", "title": "RedisGears", "uid": "xFPiNzLMz", - "version": 2 + "version": 1 } diff --git a/src/redis-gears-panel/components/code-editor/code-editor.test.tsx b/src/redis-gears-panel/components/code-editor/code-editor.test.tsx new file mode 100644 index 0000000..47dda4b --- /dev/null +++ b/src/redis-gears-panel/components/code-editor/code-editor.test.tsx @@ -0,0 +1,90 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import Editor from '@monaco-editor/react'; +import { UnthemedCodeEditor } from './code-editor'; + +/** + * Unthemed Code Editor + */ +describe('UnthemedCodeEditor', () => { + it('Should pass correct props', () => { + const onChange = jest.fn(); + const wrapper = shallow( + + ); + const component = wrapper.find(Editor); + expect(component.prop('width')).toEqual(100); + expect(component.prop('height')).toEqual(200); + expect(component.prop('value')).toEqual('hello'); + expect(component.prop('theme')).toEqual('vs-dark'); + expect(component.prop('onChange')).toEqual(onChange); + expect(component.prop('options')).toEqual({ + wordWrap: 'off', + codeLens: false, + minimap: { + enabled: undefined, + renderCharacters: false, + }, + readOnly: false, + overviewRulerBorder: false, + automaticLayout: true, + glyphMargin: false, + folding: false, + lineNumbers: 'off', + lineDecorationsWidth: 5, + lineNumbersMinChars: 0, + }); + }); + + it('Should pass correct props when lineNumbers are shown', () => { + const onChange = jest.fn(); + const value = + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book."; + const wrapper = shallow( + + ); + const component = wrapper.find(Editor); + expect(component.prop('theme')).toEqual('vs-light'); + expect(component.prop('language')).toEqual('python'); + expect(component.prop('options')).toEqual({ + wordWrap: 'off', + codeLens: false, + minimap: { + enabled: true, + renderCharacters: false, + }, + readOnly: false, + overviewRulerBorder: false, + automaticLayout: true, + lineDecorationsWidth: 0, + lineNumbersMinChars: 4, + }); + }); + + it('Should use correctly if value is undefined', () => { + const onChange = jest.fn(); + const wrapper = shallow( + + ); + const component = wrapper.find(Editor); + expect(component.prop('value')).toEqual(''); + }); +}); diff --git a/src/redis-gears-panel/components/code-editor/code-editor.tsx b/src/redis-gears-panel/components/code-editor/code-editor.tsx new file mode 100644 index 0000000..2b0cb5a --- /dev/null +++ b/src/redis-gears-panel/components/code-editor/code-editor.tsx @@ -0,0 +1,115 @@ +import React, { PureComponent } from 'react'; +import { Themeable, withTheme } from '@grafana/ui'; +import Editor from '@monaco-editor/react'; + +/** + * Properties + */ +interface Props { + /** + * Width + * + * @type {number} + */ + width: number; + + /** + * Height + * + * @type {number} + */ + height: number; + + /** + * Value + * + * @type {string} + */ + value?: string; + + /** + * Show Mini map + * + * @type {boolean} + */ + showMiniMap?: boolean; + + /** + * Show Line numbers + * + * @type {boolean} + */ + showLineNumbers?: boolean; + + /** + * Language + * + * @type {string} + */ + language?: string; + + /** + * Read-only + * + * @type {boolean} + */ + readOnly?: boolean; + + /** + * On Change + */ + onChange: (value?: string) => void; +} + +/** + * Unthemed Code Editor + * + * @see https://github.com/suren-atoyan/monaco-react + */ +export class UnthemedCodeEditor extends PureComponent { + render() { + const { width, height, theme, showMiniMap, showLineNumbers, language = 'python', onChange, readOnly } = this.props; + + /** + * Options similar to Grafana + */ + const options: any = { + wordWrap: 'off', + codeLens: false, // not included in the bundle + minimap: { + enabled: showMiniMap, + renderCharacters: false, + }, + readOnly: !!readOnly, + lineNumbersMinChars: 4, + lineDecorationsWidth: 0, + overviewRulerBorder: false, + automaticLayout: true, + }; + + /** + * Line numbers similar to Grafana + */ + if (!showLineNumbers) { + options.glyphMargin = false; + options.folding = false; + options.lineNumbers = 'off'; + options.lineDecorationsWidth = 5; // left margin when not showing line numbers + options.lineNumbersMinChars = 0; + } + + return ( + + ); + } +} + +export const CodeEditor = withTheme(UnthemedCodeEditor); diff --git a/src/redis-gears-panel/components/code-editor/index.ts b/src/redis-gears-panel/components/code-editor/index.ts new file mode 100644 index 0000000..7201cee --- /dev/null +++ b/src/redis-gears-panel/components/code-editor/index.ts @@ -0,0 +1 @@ +export { CodeEditor } from './code-editor'; diff --git a/src/redis-gears-panel/components/index.ts b/src/redis-gears-panel/components/index.ts index 97804fd..d8da358 100644 --- a/src/redis-gears-panel/components/index.ts +++ b/src/redis-gears-panel/components/index.ts @@ -1 +1,2 @@ +export * from './code-editor'; export * from './redis-gears-panel'; diff --git a/src/redis-gears-panel/components/redis-gears-panel/redis-gears-panel.test.tsx b/src/redis-gears-panel/components/redis-gears-panel/redis-gears-panel.test.tsx index d99648d..708f646 100644 --- a/src/redis-gears-panel/components/redis-gears-panel/redis-gears-panel.test.tsx +++ b/src/redis-gears-panel/components/redis-gears-panel/redis-gears-panel.test.tsx @@ -2,7 +2,8 @@ import React from 'react'; import { shallow } from 'enzyme'; import { Observable } from 'rxjs'; import { FieldType, LoadingState, toDataFrame } from '@grafana/data'; -import { Alert, Button, CodeEditor, Input, Switch, Table } from '@grafana/ui'; +import { Alert, Button, Input, Switch, Table } from '@grafana/ui'; +import { CodeEditor } from '../code-editor'; import { RedisGearsPanel } from './redis-gears-panel'; /** @@ -52,7 +53,7 @@ describe('RedisGearsPanel', () => { it('Should update script', () => { const wrapper = shallow(getComponent()); const component = wrapper.find(CodeEditor); - component.simulate('blur', 'myscript'); + component.simulate('change', 'myscript'); expect(wrapper.state().script).toEqual('myscript'); }); diff --git a/src/redis-gears-panel/components/redis-gears-panel/redis-gears-panel.tsx b/src/redis-gears-panel/components/redis-gears-panel/redis-gears-panel.tsx index 5171648..5768fd1 100644 --- a/src/redis-gears-panel/components/redis-gears-panel/redis-gears-panel.tsx +++ b/src/redis-gears-panel/components/redis-gears-panel/redis-gears-panel.tsx @@ -15,8 +15,9 @@ import { toDataFrame, } from '@grafana/data'; import { getDataSourceSrv, toDataQueryError } from '@grafana/runtime'; -import { Alert, Button, CodeEditor, InlineField, InlineFormLabel, Input, Switch, Table } from '@grafana/ui'; +import { Alert, Button, InlineField, InlineFormLabel, Input, Switch, Table } from '@grafana/ui'; import { PanelOptions } from '../../types'; +import { CodeEditor } from '../code-editor'; /** * Properties @@ -117,7 +118,7 @@ export class RedisGearsPanel extends PureComponent { * * @param script */ - onChangeScript = (script: string) => { + onChangeScript = (script = '') => { this.setState({ script, }); @@ -293,9 +294,8 @@ export class RedisGearsPanel extends PureComponent { language="python" width={width} height={height - footerHeight} - onBlur={this.onChangeScript} - onSave={this.onChangeScript} - showMiniMap={false} + onChange={this.onChangeScript} + showMiniMap={true} showLineNumbers={true} /> diff --git a/src/redis-gears-panel/constants.ts b/src/redis-gears-panel/constants.ts index a7a1d1c..44399c4 100644 --- a/src/redis-gears-panel/constants.ts +++ b/src/redis-gears-panel/constants.ts @@ -1,4 +1,4 @@ /** * Default script */ -export const DefaultScript = 'GB().run()'; +export const DefaultScript = 'gb = GearsBuilder()'; diff --git a/yarn.lock b/yarn.lock index efa0195..9002702 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1470,6 +1470,22 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@monaco-editor/loader@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.0.0.tgz#c7ea78ef07cebcae83d92bbfe2bddab563468102" + integrity sha512-CA35bf5Y7wFWfeJZfkLslOxiatln3oTBKkfbdo+TF5H+UdPP96qHvhOUjjd1DeQ2NWsRkVPSnoSYek7NE5B26w== + dependencies: + state-local "^1.0.6" + +"@monaco-editor/react@^4.0.9": + version "4.0.9" + resolved "https://registry.yarnpkg.com/@monaco-editor/react/-/react-4.0.9.tgz#77f3170c6c8409efe6b450467748f0df4f64d6b2" + integrity sha512-mTDqf47FsPnud/Ct3ekKqSFxmytkk4oMVIPUFMJF9iJcKH5AoqXs1oobXtPoVWiL9uWePatBNVCFv1pg4AqZuA== + dependencies: + "@monaco-editor/loader" "^1.0.0" + prop-types "^15.7.2" + state-local "^1.0.7" + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -11612,6 +11628,11 @@ stack-utils@^1.0.1: dependencies: escape-string-regexp "^2.0.0" +state-local@^1.0.6, state-local@^1.0.7: + 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"