diff --git a/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap b/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap index 0f55367eea4..d405f1ca608 100644 --- a/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap +++ b/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap @@ -78,7 +78,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1` ], "refresh": undefined, "revision": undefined, - "schemaVersion": 25, + "schemaVersion": 26, "snapshot": undefined, "style": "dark", "tags": Array [], @@ -191,7 +191,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1` ], "refresh": undefined, "revision": undefined, - "schemaVersion": 25, + "schemaVersion": 26, "snapshot": undefined, "style": "dark", "tags": Array [], @@ -285,7 +285,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1` ], "refresh": undefined, "revision": undefined, - "schemaVersion": 25, + "schemaVersion": 26, "snapshot": undefined, "style": "dark", "tags": Array [], @@ -434,7 +434,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti ], "refresh": undefined, "revision": undefined, - "schemaVersion": 25, + "schemaVersion": 26, "snapshot": undefined, "style": "dark", "tags": Array [], @@ -547,7 +547,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti ], "refresh": undefined, "revision": undefined, - "schemaVersion": 25, + "schemaVersion": 26, "snapshot": undefined, "style": "dark", "tags": Array [], @@ -641,7 +641,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti ], "refresh": undefined, "revision": undefined, - "schemaVersion": 25, + "schemaVersion": 26, "snapshot": undefined, "style": "dark", "tags": Array [], @@ -741,7 +741,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti ], "refresh": undefined, "revision": undefined, - "schemaVersion": 25, + "schemaVersion": 26, "snapshot": undefined, "style": "dark", "tags": Array [], diff --git a/public/app/features/dashboard/dashgrid/__snapshots__/DashboardGrid.test.tsx.snap b/public/app/features/dashboard/dashgrid/__snapshots__/DashboardGrid.test.tsx.snap index c36e1fd60af..016ca4b23f0 100644 --- a/public/app/features/dashboard/dashgrid/__snapshots__/DashboardGrid.test.tsx.snap +++ b/public/app/features/dashboard/dashgrid/__snapshots__/DashboardGrid.test.tsx.snap @@ -235,7 +235,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = ` ], "refresh": undefined, "revision": undefined, - "schemaVersion": 25, + "schemaVersion": 26, "snapshot": undefined, "style": "dark", "tags": Array [], @@ -477,7 +477,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = ` ], "refresh": undefined, "revision": undefined, - "schemaVersion": 25, + "schemaVersion": 26, "snapshot": undefined, "style": "dark", "tags": Array [], @@ -719,7 +719,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = ` ], "refresh": undefined, "revision": undefined, - "schemaVersion": 25, + "schemaVersion": 26, "snapshot": undefined, "style": "dark", "tags": Array [], @@ -961,7 +961,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = ` ], "refresh": undefined, "revision": undefined, - "schemaVersion": 25, + "schemaVersion": 26, "snapshot": undefined, "style": "dark", "tags": Array [], diff --git a/public/app/features/dashboard/state/DashboardMigrator.test.ts b/public/app/features/dashboard/state/DashboardMigrator.test.ts index c7d1dab818b..b9634ad7dd0 100644 --- a/public/app/features/dashboard/state/DashboardMigrator.test.ts +++ b/public/app/features/dashboard/state/DashboardMigrator.test.ts @@ -132,7 +132,7 @@ describe('DashboardModel', () => { }); it('dashboard schema version should be set to latest', () => { - expect(model.schemaVersion).toBe(25); + expect(model.schemaVersion).toBe(26); }); it('graph thresholds should be migrated', () => { @@ -715,6 +715,89 @@ describe('DashboardModel', () => { ]); }); }); + + describe('when migrating to new Text Panel', () => { + let model: DashboardModel; + + beforeEach(() => { + model = new DashboardModel({ + panels: [ + { + id: 2, + type: 'text', + title: 'Angular Text Panel', + content: + '# Angular Text Panel\n# $constant\n\nFor markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)\n\n## $text\n\n', + mode: 'markdown', + }, + { + id: 3, + type: 'text2', + title: 'React Text Panel from scratch', + options: { + mode: 'markdown', + content: + '# React Text Panel from scratch\n# $constant\n\nFor markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)\n\n## $text', + }, + }, + { + id: 4, + type: 'text2', + title: 'React Text Panel from Angular Panel', + options: { + mode: 'markdown', + content: + '# React Text Panel from Angular Panel\n# $constant\n\nFor markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)\n\n## $text', + angular: { + content: + '# React Text Panel from Angular Panel\n# $constant\n\nFor markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)\n\n## $text\n', + mode: 'markdown', + options: {}, + }, + }, + }, + ], + }); + }); + + it('should have 3 panels after migration', () => { + expect(model.panels.length).toBe(3); + }); + + it('should not migrate panel with old Text Panel id', () => { + const oldAngularPanel: any = model.panels[0]; + expect(oldAngularPanel.id).toEqual(2); + expect(oldAngularPanel.type).toEqual('text'); + expect(oldAngularPanel.title).toEqual('Angular Text Panel'); + expect(oldAngularPanel.content).toEqual( + '# Angular Text Panel\n# $constant\n\nFor markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)\n\n## $text\n\n' + ); + expect(oldAngularPanel.mode).toEqual('markdown'); + }); + + it('should migrate panels with new Text Panel id', () => { + const reactPanel: any = model.panels[1]; + expect(reactPanel.id).toEqual(3); + expect(reactPanel.type).toEqual('text'); + expect(reactPanel.title).toEqual('React Text Panel from scratch'); + expect(reactPanel.options.content).toEqual( + '# React Text Panel from scratch\n# $constant\n\nFor markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)\n\n## $text' + ); + expect(reactPanel.options.mode).toEqual('markdown'); + }); + + it('should clean up old angular options for panels with new Text Panel id', () => { + const reactPanel: any = model.panels[2]; + expect(reactPanel.id).toEqual(4); + expect(reactPanel.type).toEqual('text'); + expect(reactPanel.title).toEqual('React Text Panel from Angular Panel'); + expect(reactPanel.options.content).toEqual( + '# React Text Panel from Angular Panel\n# $constant\n\nFor markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)\n\n## $text' + ); + expect(reactPanel.options.mode).toEqual('markdown'); + expect(reactPanel.options.angular).toBeUndefined(); + }); + }); }); function createRow(options: any, panelDescriptions: any[]) { diff --git a/public/app/features/dashboard/state/DashboardMigrator.ts b/public/app/features/dashboard/state/DashboardMigrator.ts index 1416f7f0621..dfde0af220d 100644 --- a/public/app/features/dashboard/state/DashboardMigrator.ts +++ b/public/app/features/dashboard/state/DashboardMigrator.ts @@ -31,7 +31,7 @@ export class DashboardMigrator { let i, j, k, n; const oldVersion = this.dashboard.schemaVersion; const panelUpgrades = []; - this.dashboard.schemaVersion = 25; + this.dashboard.schemaVersion = 26; if (oldVersion === this.dashboard.schemaVersion) { return; @@ -564,6 +564,18 @@ export class DashboardMigrator { } } + if (oldVersion < 26) { + panelUpgrades.push((panel: any) => { + const wasReactText = panel.type === 'text2'; + if (!wasReactText) { + return; + } + + panel.type = 'text'; + delete panel.options.angular; + }); + } + if (panelUpgrades.length === 0) { return; } diff --git a/public/app/features/plugins/built_in_plugins.ts b/public/app/features/plugins/built_in_plugins.ts index 5d4a3171743..38d695defce 100644 --- a/public/app/features/plugins/built_in_plugins.ts +++ b/public/app/features/plugins/built_in_plugins.ts @@ -37,7 +37,6 @@ const azureMonitorPlugin = async () => ); import * as textPanel from 'app/plugins/panel/text/module'; -import * as text2Panel from 'app/plugins/panel/text2/module'; import * as graph2Panel from 'app/plugins/panel/graph2/module'; import * as graphPanel from 'app/plugins/panel/graph/module'; import * as dashListPanel from 'app/plugins/panel/dashlist/module'; @@ -79,7 +78,6 @@ const builtInPlugins: any = { 'app/plugins/datasource/grafana-azure-monitor-datasource/module': azureMonitorPlugin, 'app/plugins/panel/text/module': textPanel, - 'app/plugins/panel/text2/module': text2Panel, 'app/plugins/panel/graph2/module': graph2Panel, 'app/plugins/panel/graph/module': graphPanel, 'app/plugins/panel/dashlist/module': dashListPanel, diff --git a/public/app/plugins/panel/text/README.md b/public/app/plugins/panel/text/README.md index 14751842990..667ab51784a 100644 --- a/public/app/plugins/panel/text/README.md +++ b/public/app/plugins/panel/text/README.md @@ -2,4 +2,4 @@ The Text Panel is **included** with Grafana. -The Text Panel is a very simple panel that displays text. The source text is written in the Markdown syntax meaning you can format the text. Read [GitHub's Mastering Markdown](https://guides.github.com/features/mastering-markdown/) to learn more. \ No newline at end of file +The Text Panel is a very simple panel that displays text. The source text is written in the Markdown syntax meaning you can format the text. Read [GitHub's Mastering Markdown](https://guides.github.com/features/mastering-markdown/) to learn more. diff --git a/public/app/plugins/panel/text2/TextPanel.tsx b/public/app/plugins/panel/text/TextPanel.tsx similarity index 100% rename from public/app/plugins/panel/text2/TextPanel.tsx rename to public/app/plugins/panel/text/TextPanel.tsx diff --git a/public/app/plugins/panel/text/editor.html b/public/app/plugins/panel/text/editor.html deleted file mode 100644 index 2105f0b4674..00000000000 --- a/public/app/plugins/panel/text/editor.html +++ /dev/null @@ -1,15 +0,0 @@ -
-
- Mode - - - -
-
- -
-
- - -
-
diff --git a/public/app/plugins/panel/text/module.html b/public/app/plugins/panel/text/module.html deleted file mode 100644 index 9fcf5a9f557..00000000000 --- a/public/app/plugins/panel/text/module.html +++ /dev/null @@ -1,2 +0,0 @@ -

-

diff --git a/public/app/plugins/panel/text/module.ts b/public/app/plugins/panel/text/module.ts deleted file mode 100644 index 12ff95779ff..00000000000 --- a/public/app/plugins/panel/text/module.ts +++ /dev/null @@ -1,101 +0,0 @@ -import _ from 'lodash'; -import { PanelCtrl } from 'app/plugins/sdk'; - -import config from 'app/core/config'; -import { auto, ISCEService } from 'angular'; -import { TemplateSrv } from 'app/features/templating/template_srv'; -import { PanelEvents, textUtil } from '@grafana/data'; -import { renderMarkdown } from '@grafana/data'; - -const defaultContent = ` -# Title - -For markdown syntax help: [commonmark.org/help](https://commonmark.org/help/) - - - -`; - -export class TextPanelCtrl extends PanelCtrl { - static templateUrl = `public/app/plugins/panel/text/module.html`; - static scrollable = true; - - content: string; - // Set and populate defaults - panelDefaults = { - mode: 'markdown', // 'html', 'markdown', 'text' - content: defaultContent, - }; - - /** @ngInject */ - constructor( - $scope: any, - $injector: auto.IInjectorService, - private templateSrv: TemplateSrv, - private $sce: ISCEService - ) { - super($scope, $injector); - - _.defaults(this.panel, this.panelDefaults); - - this.events.on(PanelEvents.editModeInitialized, this.onInitEditMode.bind(this)); - this.events.on(PanelEvents.refresh, this.onRefresh.bind(this)); - this.events.on(PanelEvents.render, this.onRender.bind(this)); - - const renderWhenChanged = (scope: any) => { - const { panel } = scope.ctrl; - return [panel.content, panel.mode].join(); - }; - - $scope.$watch( - renderWhenChanged, - _.throttle(() => { - this.render(); - }, 100) - ); - } - - onInitEditMode() { - this.addEditorTab('Options', 'public/app/plugins/panel/text/editor.html'); - - if (this.panel.mode === 'text') { - this.panel.mode = 'markdown'; - } - } - - onRefresh() { - this.render(); - } - - onRender() { - if (this.panel.mode === 'markdown') { - this.renderMarkdown(this.panel.content); - } else if (this.panel.mode === 'html') { - this.updateContent(this.panel.content); - } - this.renderingCompleted(); - } - - renderText(content: string) { - const safeContent = textUtil.escapeHtml(content).replace(/\n/g, '
'); - this.updateContent(safeContent); - } - - renderMarkdown(content: string) { - this.$scope.$applyAsync(() => { - this.updateContent(renderMarkdown(content)); - }); - } - - updateContent(html: string) { - try { - html = this.templateSrv.replace(html, this.panel.scopedVars, 'html'); - } catch (e) { - console.log('Text panel error: ', e); - } - - this.content = this.$sce.trustAsHtml(config.disableSanitizeHtml ? html : textUtil.sanitize(html)); - } -} - -export { TextPanelCtrl as PanelCtrl }; diff --git a/public/app/plugins/panel/text2/module.tsx b/public/app/plugins/panel/text/module.tsx similarity index 75% rename from public/app/plugins/panel/text2/module.tsx rename to public/app/plugins/panel/text/module.tsx index 24f1c93df1a..78b552128fe 100644 --- a/public/app/plugins/panel/text2/module.tsx +++ b/public/app/plugins/panel/text/module.tsx @@ -1,7 +1,8 @@ -import { PanelModel, PanelPlugin } from '@grafana/data'; +import { PanelPlugin } from '@grafana/data'; import { TextPanel } from './TextPanel'; import { TextOptions } from './types'; +import { textPanelMigrationHandler } from './textPanelMigrationHandler'; export const plugin = new PanelPlugin(TextPanel) .setPanelOptions(builder => { @@ -28,14 +29,9 @@ export const plugin = new PanelPlugin(TextPanel) rows: 5, }, defaultValue: `# Title - + For markdown syntax help: [commonmark.org/help](https://commonmark.org/help/) `, }); }) - .setPanelChangeHandler((panel: PanelModel, prevPluginId: string, prevOptions: any) => { - if (prevPluginId === 'text') { - return prevOptions as TextOptions; - } - return panel.options; - }); + .setMigrationHandler(textPanelMigrationHandler); diff --git a/public/app/plugins/panel/text/textPanelMigrationHandler.test.ts b/public/app/plugins/panel/text/textPanelMigrationHandler.test.ts new file mode 100644 index 00000000000..b591bbe4403 --- /dev/null +++ b/public/app/plugins/panel/text/textPanelMigrationHandler.test.ts @@ -0,0 +1,43 @@ +import { textPanelMigrationHandler } from './textPanelMigrationHandler'; +import { TextOptions } from './types'; +import { FieldConfigSource, PanelModel } from '@grafana/data'; + +describe('textPanelMigrationHandler', () => { + describe('when invoked and previous version was old Angular text panel', () => { + it('then should migrate options', () => { + const panel: any = { + content: 'Hello World', + mode: 'html', + }; + + const result = textPanelMigrationHandler(panel); + + expect(result.content).toEqual('Hello World'); + expect(result.mode).toEqual('html'); + }); + }); + + describe('when invoked and previous version was not old Angular text panel', () => { + it('then should just pass options through', () => { + const panel: PanelModel = { + id: 1, + fieldConfig: ({} as unknown) as FieldConfigSource, + options: { + content: `# Title + + For markdown syntax help: [commonmark.org/help](https://commonmark.org/help/) + `, + mode: 'markdown', + }, + }; + + const result = textPanelMigrationHandler(panel); + + expect(result.content).toEqual(`# Title + + For markdown syntax help: [commonmark.org/help](https://commonmark.org/help/) + `); + expect(result.mode).toEqual('markdown'); + }); + }); +}); diff --git a/public/app/plugins/panel/text/textPanelMigrationHandler.ts b/public/app/plugins/panel/text/textPanelMigrationHandler.ts new file mode 100644 index 00000000000..e2d6531a89c --- /dev/null +++ b/public/app/plugins/panel/text/textPanelMigrationHandler.ts @@ -0,0 +1,15 @@ +import { PanelModel } from '@grafana/data'; +import { TextMode, TextOptions } from './types'; + +export const textPanelMigrationHandler = (panel: PanelModel): Partial => { + // Migrates old Angular based text panel props to new props + if (panel.hasOwnProperty('content') && panel.hasOwnProperty('mode')) { + const oldTextPanel: { content: string; mode: string } = (panel as unknown) as any; + const content = oldTextPanel.content; + const mode = (oldTextPanel.mode as unknown) as TextMode; + + return { content, mode }; + } + + return panel.options as TextOptions; +}; diff --git a/public/app/plugins/panel/text2/types.ts b/public/app/plugins/panel/text/types.ts similarity index 100% rename from public/app/plugins/panel/text2/types.ts rename to public/app/plugins/panel/text/types.ts diff --git a/public/app/plugins/panel/text2/README.md b/public/app/plugins/panel/text2/README.md deleted file mode 100644 index 667ab51784a..00000000000 --- a/public/app/plugins/panel/text2/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Text Panel - Native Plugin - -The Text Panel is **included** with Grafana. - -The Text Panel is a very simple panel that displays text. The source text is written in the Markdown syntax meaning you can format the text. Read [GitHub's Mastering Markdown](https://guides.github.com/features/mastering-markdown/) to learn more. diff --git a/public/app/plugins/panel/text2/img/icn-text-panel.svg b/public/app/plugins/panel/text2/img/icn-text-panel.svg deleted file mode 100644 index eebe7819e68..00000000000 --- a/public/app/plugins/panel/text2/img/icn-text-panel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/app/plugins/panel/text2/plugin.json b/public/app/plugins/panel/text2/plugin.json deleted file mode 100644 index 753a88d6dd8..00000000000 --- a/public/app/plugins/panel/text2/plugin.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "panel", - "name": "Text v2", - "id": "text2", - "state": "alpha", - - "skipDataQuery": true, - - "info": { - "author": { - "name": "Grafana Labs", - "url": "https://grafana.com" - }, - "logos": { - "small": "img/icn-text-panel.svg", - "large": "img/icn-text-panel.svg" - } - } -}