From e651bfb0cd10dc187b8913c46131b539d8b723f8 Mon Sep 17 00:00:00 2001 From: Mikhail Volkov <47795110+mikhail-vl@users.noreply.github.com> Date: Mon, 25 Dec 2023 11:40:17 -0500 Subject: [PATCH] Update to Grafana 10.2.2 and Volkov labs packages (#250) * Update to Grafana 10.2.2 and Volkov labs packages * Add suggestions for helper and after render editors * Add context object to before content rendering * Formatting --------- Co-authored-by: asimonok --- CHANGELOG.md | 3 +- package-lock.json | 91 ++++++++------- package.json | 20 ++-- src/components/CustomEditor/CustomEditor.tsx | 24 +++- .../CustomEditor/CutomEditor.test.tsx | 68 ++++++++++- src/components/Row/Row.tsx | 3 +- src/constants/editor.ts | 108 +++++++++++++++++- src/helpers/html.ts | 26 ++++- src/module.ts | 4 +- src/types/editor.ts | 65 +++++++++++ 10 files changed, 346 insertions(+), 66 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5ecc27..4acaff8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 4.3.0 (IN PROGRESS) +## 4.3.0 (2023-12-25) ### Features / Enhancements @@ -8,6 +8,7 @@ - Update Collapse from @volkovlabs/components (#239) - Update Introduction video in README (#240) - Add data render mode and passing selected data frame (#246) +- Update to Grafana 10.2.2 and Volkov labs packages (#247) ### Bug fixes diff --git a/package-lock.json b/package-lock.json index 8eb1054..4d7a7c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,12 +10,13 @@ "license": "Apache-2.0", "dependencies": { "@emotion/css": "^11.11.2", - "@grafana/data": "^10.2.1", - "@grafana/runtime": "^10.2.1", - "@grafana/schema": "^10.2.1", - "@grafana/ui": "^10.2.1", + "@grafana/data": "^10.2.2", + "@grafana/runtime": "^10.2.2", + "@grafana/schema": "^10.2.2", + "@grafana/ui": "^10.2.2", "@types/highlight.js": "^10.1.0", "@types/markdown-it": "^12.2.3", + "@volkovlabs/components": "^1.2.1", "dayjs": "^1.11.10", "handlebars": "^4.7.8", "helper-date": "^1.0.1", @@ -31,8 +32,8 @@ "@babel/core": "^7.23.3", "@babel/preset-typescript": "^7.23.3", "@babel/register": "^7.22.15", - "@grafana/e2e": "^10.2.1", - "@grafana/e2e-selectors": "^10.2.1", + "@grafana/e2e": "^10.2.2", + "@grafana/e2e-selectors": "^10.2.2", "@grafana/eslint-config": "^6.0.1", "@grafana/tsconfig": "^1.3.0-rc1", "@swc/core": "^1.3.96", @@ -45,8 +46,7 @@ "@types/node": "^18.18.10", "@types/react-beautiful-dnd": "^13.1.7", "@types/uuid": "^9.0.7", - "@volkovlabs/components": "^1.1.0", - "@volkovlabs/eslint-config": "^1.2.1", + "@volkovlabs/eslint-config": "^1.2.2", "copy-webpack-plugin": "^11.0.0", "css-loader": "^6.8.1", "eslint-webpack-plugin": "^4.0.1", @@ -55,7 +55,7 @@ "identity-obj-proxy": "3.0.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "prettier": "^3.1.0", + "prettier": "^3.1.1", "replace-in-file-webpack-plugin": "^1.0.6", "sass": "^1.69.5", "sass-loader": "^13.3.2", @@ -2377,12 +2377,12 @@ } }, "node_modules/@grafana/data": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/@grafana/data/-/data-10.2.1.tgz", - "integrity": "sha512-7T4akx7PBq/aUj98q0U7mX1hJk9KGNglBr8+rqvkAsXc0l1WPykJDOGr2tWmTuAz73aUrjB7cbSUGEIQWNGWYA==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@grafana/data/-/data-10.2.2.tgz", + "integrity": "sha512-ofiUOQw8E9qG9FKIERWSSHdAU2p+w1jzuqy49giDJfxDaskcin5We5VWamSONqYPYP/tCjZcGkvnsVa/uwU37A==", "dependencies": { "@braintree/sanitize-url": "6.0.2", - "@grafana/schema": "10.2.1", + "@grafana/schema": "10.2.2", "@types/d3-interpolate": "^3.0.0", "@types/string-hash": "1.1.1", "d3-interpolate": "3.0.1", @@ -2423,16 +2423,16 @@ "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" }, "node_modules/@grafana/e2e": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/@grafana/e2e/-/e2e-10.2.1.tgz", - "integrity": "sha512-7/4/ZMEBRXvfmaqzxsYkv07ZObGRFLeNt2xmpG2wLWN6xLn0xikCn9Rgzr7J8S23KyG5zjuhLW0/hkISbU/yTQ==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@grafana/e2e/-/e2e-10.2.2.tgz", + "integrity": "sha512-wPxC18zwuvHq3o0Aheg8e7VV9NWzWwHAB1cOozC9sZi0MScuqSKerY6ej3+wCWntg5dVhnt6pj31il0cIjm5Tw==", "dev": true, "dependencies": { "@babel/core": "7.23.0", "@babel/preset-env": "7.23.2", "@cypress/webpack-preprocessor": "5.17.1", - "@grafana/e2e-selectors": "10.2.1", - "@grafana/schema": "10.2.1", + "@grafana/e2e-selectors": "10.2.2", + "@grafana/schema": "10.2.2", "@grafana/tsconfig": "^1.2.0-rc1", "@mochajs/json-file-reporter": "^1.2.0", "babel-loader": "9.1.3", @@ -2459,9 +2459,9 @@ } }, "node_modules/@grafana/e2e-selectors": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/@grafana/e2e-selectors/-/e2e-selectors-10.2.1.tgz", - "integrity": "sha512-5zuwe6NbFIs1C3CzQVdARG/GWvTFSb+KPQmcQvaF65eUVw3GF/LKUnQ1bPsMpdZjyyfnF+ROJTD19+9JpP2GxQ==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@grafana/e2e-selectors/-/e2e-selectors-10.2.2.tgz", + "integrity": "sha512-Ee/+AfjHSoEshdlm7WeLzWpTSLGuhsarx9Q3K9ar8NxfQmDYFHyp1dW3YGyWsxiDHdnEYt9jLw8wbvr5kM6iOA==", "dependencies": { "@grafana/tsconfig": "^1.2.0-rc1", "tslib": "2.6.0", @@ -3009,14 +3009,14 @@ } }, "node_modules/@grafana/runtime": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/@grafana/runtime/-/runtime-10.2.1.tgz", - "integrity": "sha512-U5oQZ7g+rem2uuJ3aDVu6V2FICHK332Tx/GgbLVap1UG2WzV7788mBUDvS/7YIXUP2keypWtwbqzDMsn2HdT5A==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@grafana/runtime/-/runtime-10.2.2.tgz", + "integrity": "sha512-AYpGujmqFfB58oAQITI1o6P2+zSWWUSoWaXout8N+ZhcES9CSh5Tbr/LXFqu/gAC4oOsOIEM/N2Nje5wSRB8Iw==", "dependencies": { - "@grafana/data": "10.2.1", - "@grafana/e2e-selectors": "10.2.1", + "@grafana/data": "10.2.2", + "@grafana/e2e-selectors": "10.2.2", "@grafana/faro-web-sdk": "1.2.1", - "@grafana/ui": "10.2.1", + "@grafana/ui": "10.2.2", "history": "4.10.1", "lodash": "4.17.21", "rxjs": "7.8.1", @@ -3035,9 +3035,9 @@ "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" }, "node_modules/@grafana/schema": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/@grafana/schema/-/schema-10.2.1.tgz", - "integrity": "sha512-yyytYasg2nZzTiwq5QQ4NW+iuC+qNp3ZXgm7LuzH8WZPq9t6P8fxWS+QW9icL4dmocFardn1jVW1s/tpVqHgaw==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@grafana/schema/-/schema-10.2.2.tgz", + "integrity": "sha512-VDMO2Ev/mSsQxOkwo2u3uQocoyfxJGaGTfAMRGgeejmYJVQsK3Ka6/ImGmqvCViE5uade5/rx7kKfLnj6Yc0Yg==", "dependencies": { "tslib": "2.6.0" } @@ -3054,16 +3054,16 @@ "dev": true }, "node_modules/@grafana/ui": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/@grafana/ui/-/ui-10.2.1.tgz", - "integrity": "sha512-RrI3BiukxnC5PtL1Gw7LkIVvonc/AeM/2f0Gry8SpZJlao05gME9XyxXU18LlsfqMzFxrioJOMjf6BfU+r48kQ==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@grafana/ui/-/ui-10.2.2.tgz", + "integrity": "sha512-45aZ9PL0lQeTSRfW916VWyTAm2u3a5bKihgiT8GX5a5Dm9+++xGwcOXVOwlIRIuLpWMJXqgg9MIrslZwfzCzhQ==", "dependencies": { "@emotion/css": "11.11.2", "@emotion/react": "11.11.1", - "@grafana/data": "10.2.1", - "@grafana/e2e-selectors": "10.2.1", + "@grafana/data": "10.2.2", + "@grafana/e2e-selectors": "10.2.2", "@grafana/faro-web-sdk": "1.2.1", - "@grafana/schema": "10.2.1", + "@grafana/schema": "10.2.2", "@leeoniya/ufuzzy": "1.0.8", "@monaco-editor/react": "4.6.0", "@popperjs/core": "2.11.8", @@ -6542,10 +6542,9 @@ "peer": true }, "node_modules/@volkovlabs/components": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@volkovlabs/components/-/components-1.1.0.tgz", - "integrity": "sha512-pNTfieF4pYvyRfvhO0tvkM5FK0b19w6jhjdMgbviwPdMLD7HlIj/DVmOjW5d9v2s6arIvz03LylHpACUD4Iy0Q==", - "dev": true, + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@volkovlabs/components/-/components-1.2.1.tgz", + "integrity": "sha512-uEawg9hC4314FNOcwsalNOymp3xE71xfIEDHxdXRhYMRoTKsr350pFIEBuEt2j7UnWtLvttv+lXSTh6UnuAQ4w==", "dependencies": { "@emotion/css": "^11.11.2", "@grafana/data": "^10.2.1", @@ -6557,9 +6556,9 @@ } }, "node_modules/@volkovlabs/eslint-config": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@volkovlabs/eslint-config/-/eslint-config-1.2.1.tgz", - "integrity": "sha512-O/0kwVIlmWkqQpCgZQ4ZwzUBOabYTZEa0VXlXwDXjq2ACpmyCFNBpupyudbSEiwdjJ18skf5EAG3sLTs3HYUGA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@volkovlabs/eslint-config/-/eslint-config-1.2.2.tgz", + "integrity": "sha512-GBoAvbc+nLlYCH0pc61zMqt0f2iUNZB0oJPX8inHXYeayDf1Bpys/Dh/t9RgilYL4vnoqHEi9/e3/W9NGKSLiQ==", "dev": true, "dependencies": { "@typescript-eslint/eslint-plugin": "^6.0.0", @@ -17895,9 +17894,9 @@ } }, "node_modules/prettier": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", - "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz", + "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" diff --git a/package.json b/package.json index 53a67fa..23345a8 100644 --- a/package.json +++ b/package.json @@ -2,12 +2,13 @@ "author": "Volkov Labs", "dependencies": { "@emotion/css": "^11.11.2", - "@grafana/data": "^10.2.1", - "@grafana/runtime": "^10.2.1", - "@grafana/schema": "^10.2.1", - "@grafana/ui": "^10.2.1", + "@grafana/data": "^10.2.2", + "@grafana/runtime": "^10.2.2", + "@grafana/schema": "^10.2.2", + "@grafana/ui": "^10.2.2", "@types/highlight.js": "^10.1.0", "@types/markdown-it": "^12.2.3", + "@volkovlabs/components": "^1.2.1", "dayjs": "^1.11.10", "handlebars": "^4.7.8", "helper-date": "^1.0.1", @@ -24,22 +25,21 @@ "@babel/core": "^7.23.3", "@babel/preset-typescript": "^7.23.3", "@babel/register": "^7.22.15", - "@grafana/e2e": "^10.2.1", - "@grafana/e2e-selectors": "^10.2.1", + "@grafana/e2e": "^10.2.2", + "@grafana/e2e-selectors": "^10.2.2", "@grafana/eslint-config": "^6.0.1", "@grafana/tsconfig": "^1.3.0-rc1", "@swc/core": "^1.3.96", "@swc/helpers": "^0.5.3", "@swc/jest": "^0.2.29", - "@testing-library/jest-dom": "^6.1.4", + "@testing-library/jest-dom": "^6.1.5", "@testing-library/react": "^14.1.2", "@types/jest": "^29.5.8", "@types/lodash": "^4.14.201", "@types/node": "^18.18.10", "@types/react-beautiful-dnd": "^13.1.7", "@types/uuid": "^9.0.7", - "@volkovlabs/components": "^1.1.0", - "@volkovlabs/eslint-config": "^1.2.1", + "@volkovlabs/eslint-config": "^1.2.2", "copy-webpack-plugin": "^11.0.0", "css-loader": "^6.8.1", "eslint-webpack-plugin": "^4.0.1", @@ -48,7 +48,7 @@ "identity-obj-proxy": "3.0.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "prettier": "^3.1.0", + "prettier": "^3.1.1", "replace-in-file-webpack-plugin": "^1.0.6", "sass": "^1.69.5", "sass-loader": "^13.3.2", diff --git a/src/components/CustomEditor/CustomEditor.tsx b/src/components/CustomEditor/CustomEditor.tsx index 9b91294..205073e 100644 --- a/src/components/CustomEditor/CustomEditor.tsx +++ b/src/components/CustomEditor/CustomEditor.tsx @@ -7,7 +7,14 @@ import { CodeEditor, CodeEditorSuggestionItem, CodeEditorSuggestionItemKind } fr import type * as monacoType from 'monaco-editor/esm/vs/editor/editor.api'; import React, { useCallback, useMemo } from 'react'; -import { CodeLanguage, EditorType, Format, HELPERS_EDITOR_SUGGESTIONS, TEST_IDS } from '../../constants'; +import { + AFTER_RENDER_EDITOR_SUGGESTIONS, + CodeLanguage, + EditorType, + Format, + HELPERS_EDITOR_SUGGESTIONS, + TEST_IDS, +} from '../../constants'; /** * Properties @@ -64,6 +71,10 @@ export const CustomEditor: React.FC = ({ value, onChange, context, type = return suggestions; } + if (type === EditorType.AFTER_RENDER) { + return AFTER_RENDER_EDITOR_SUGGESTIONS.concat(suggestions); + } + return HELPERS_EDITOR_SUGGESTIONS.concat(suggestions); }, [templateSrv, type]); @@ -80,7 +91,7 @@ export const CustomEditor: React.FC = ({ value, onChange, context, type = * Language */ const language = useMemo(() => { - if (type === EditorType.HELPERS) { + if (type === EditorType.HELPERS || type === EditorType.AFTER_RENDER) { return CodeLanguage.JAVASCRIPT; } @@ -125,6 +136,15 @@ export const HelpersEditor: React.FC = (props) => ( ); +/** + * After Render Editor + * @param props + * @constructor + */ +export const AfterRenderEditor: React.FC = (props) => ( + +); + /** * Styles Editor * @param props diff --git a/src/components/CustomEditor/CutomEditor.test.tsx b/src/components/CustomEditor/CutomEditor.test.tsx index 8535d46..c0c6d2f 100644 --- a/src/components/CustomEditor/CutomEditor.test.tsx +++ b/src/components/CustomEditor/CutomEditor.test.tsx @@ -3,8 +3,14 @@ import { CodeEditor, CodeEditorSuggestionItemKind } from '@grafana/ui'; import { render, screen } from '@testing-library/react'; import React from 'react'; -import { CodeLanguage, Format, HELPERS_EDITOR_SUGGESTIONS, TEST_IDS } from '../../constants'; -import { CustomEditor, HelpersEditor, StylesEditor, TextEditor } from './CustomEditor'; +import { + AFTER_RENDER_EDITOR_SUGGESTIONS, + CodeLanguage, + Format, + HELPERS_EDITOR_SUGGESTIONS, + TEST_IDS, +} from '../../constants'; +import { CustomEditor, HelpersEditor, StylesEditor, TextEditor, AfterRenderEditor } from './CustomEditor'; /** * Mock @grafana/ui @@ -271,6 +277,64 @@ describe('Custom Editor', () => { }); }); + describe('After Render Editor', () => { + /** + * Properties + */ + const props: any = { context: { options: { editor: {} } }, onChange }; + + it('Should use javascript language', () => { + render(); + + expect(CodeEditor).toHaveBeenCalledWith( + expect.objectContaining({ + language: CodeLanguage.JAVASCRIPT, + }), + expect.anything() + ); + }); + + it('Should make correct suggestions', () => { + let suggestionsResult; + const variableWithDescription = { name: 'var1', description: 'Var description', label: 'Var Label' }; + const variableWithoutDescription = { name: 'var2', description: '', label: 'Var 2' }; + const variables = [variableWithDescription, variableWithoutDescription]; + + jest.mocked(CodeEditor).mockImplementation(({ getSuggestions }: any) => { + suggestionsResult = getSuggestions(); + return null; + }); + jest.mocked(getTemplateSrv).mockImplementation( + () => + ({ + getVariables: jest.fn().mockImplementation(() => variables), + }) as any + ); + + render(); + + expect(suggestionsResult).toEqual(expect.arrayContaining(AFTER_RENDER_EDITOR_SUGGESTIONS)); + expect(suggestionsResult).toEqual( + expect.arrayContaining([ + { + label: `\$\{${variableWithDescription.name}\}`, + kind: CodeEditorSuggestionItemKind.Property, + detail: variableWithDescription.description, + }, + ]) + ); + expect(suggestionsResult).toEqual( + expect.arrayContaining([ + { + label: `\$\{${variableWithoutDescription.name}\}`, + kind: CodeEditorSuggestionItemKind.Property, + detail: variableWithoutDescription.label, + }, + ]) + ); + }); + }); + describe('Styles Editor', () => { /** * Properties diff --git a/src/components/Row/Row.tsx b/src/components/Row/Row.tsx index c9faae5..a8324fd 100644 --- a/src/components/Row/Row.tsx +++ b/src/components/Row/Row.tsx @@ -66,6 +66,7 @@ export const Row: React.FC = ({ className, item, afterRender, replaceVari element: ref.current, data: item.data, panelData: item.panelData, + dataFrame: item.dataFrame, grafana: { replaceVariables, eventBus, @@ -79,7 +80,7 @@ export const Row: React.FC = ({ className, item, afterRender, replaceVari unsubscribe(); } }; - }, [afterRender, eventBus, item.data, item.panelData, replaceVariables]); + }, [afterRender, eventBus, item.data, item.dataFrame, item.panelData, replaceVariables]); return (
(TextPanel) description: 'Allows to execute code after content is ready. E.g. use element for drawing chart or event listeners.', defaultValue: DEFAULT_OPTIONS.afterRender, - editor: HelpersEditor, + editor: AfterRenderEditor, category: ['JavaScript'], showIf: (config) => config.editors.includes(EditorType.AFTER_RENDER) || config.afterRender !== DEFAULT_OPTIONS.afterRender, diff --git a/src/types/editor.ts b/src/types/editor.ts index 9eb6f49..2d5e05d 100644 --- a/src/types/editor.ts +++ b/src/types/editor.ts @@ -1,3 +1,8 @@ +import { DataFrame, EventBus, getLocale, InterpolateFunction, PanelData, TimeRange } from '@grafana/data'; +import { LocationService } from '@grafana/runtime'; +import { TimeZone } from '@grafana/schema'; +import Handlebars from 'handlebars'; + import { CodeLanguage, Format } from '../constants'; /** @@ -25,3 +30,63 @@ export interface EditorOptions { */ language: CodeLanguage; } + +/** + * Helpers Editor Context + */ +export interface HelpersEditorContext { + /** + * Row Data + */ + data: Record; + + /** + * Handlebars + */ + handlebars: typeof Handlebars; + + /** + * Panel Data + */ + panelData: PanelData; + + /** + * Selected Data Frame + */ + dataFrame?: DataFrame; + + /** + * Grafana + */ + grafana: { + /** + * Get Locale + */ + getLocale: typeof getLocale; + + /** + * Time Zone + */ + timeZone: TimeZone; + + /** + * Time Range + */ + timeRange: TimeRange; + + /** + * Replace Variables + */ + replaceVariables: InterpolateFunction; + + /** + * Location Service + */ + locationService: LocationService; + + /** + * Event Bus + */ + eventBus: EventBus; + }; +}