diff --git a/packages/grafana-ui/src/types/panel.ts b/packages/grafana-ui/src/types/panel.ts index 269d86ecdb7..6f008f31bcb 100644 --- a/packages/grafana-ui/src/types/panel.ts +++ b/packages/grafana-ui/src/types/panel.ts @@ -32,7 +32,7 @@ export type PanelMigrationHandler = (exiting: any, oldVersion?: export type PanelTypeChangedHandler = ( options: Partial, prevPluginId: string, - prevOptions?: any + prevOptions: any ) => Partial; export class ReactPanelPlugin { diff --git a/public/app/core/utils/emitter.ts b/public/app/core/utils/emitter.ts index 9a5c671f574..95258e7552e 100644 --- a/public/app/core/utils/emitter.ts +++ b/public/app/core/utils/emitter.ts @@ -1,7 +1,7 @@ import { EventEmitter } from 'eventemitter3'; export class Emitter { - emitter: any; + private emitter: EventEmitter; constructor() { this.emitter = new EventEmitter(); @@ -29,4 +29,8 @@ export class Emitter { off(name, handler) { this.emitter.off(name, handler); } + + getEventCount(): number { + return (this.emitter as any)._eventsCount; + } } diff --git a/public/app/features/dashboard/state/PanelModel.test.ts b/public/app/features/dashboard/state/PanelModel.test.ts index 1b75844809c..1da0c786753 100644 --- a/public/app/features/dashboard/state/PanelModel.test.ts +++ b/public/app/features/dashboard/state/PanelModel.test.ts @@ -1,5 +1,6 @@ import { PanelModel } from './PanelModel'; import { getPanelPlugin } from '../../plugins/__mocks__/pluginMocks'; +import { ReactPanelPlugin } from '@grafana/ui/src/types/panel'; describe('PanelModel', () => { describe('when creating new panel model', () => { @@ -96,6 +97,44 @@ describe('PanelModel', () => { }); }); + describe('when changing from angular panel', () => { + let tearDownPublished = false; + + beforeEach(() => { + model.events.on('panel-teardown', () => { + tearDownPublished = true; + }); + model.changePlugin(getPanelPlugin({ id: 'graph', exports: {} })); + }); + + it('should teardown / destroy panel so angular panels event subscriptions are removed', () => { + expect(tearDownPublished).toBe(true); + expect(model.events.getEventCount()).toBe(0); + }); + }); + + describe('when changing to react panel', () => { + const onPanelTypeChanged = jest.fn(); + const reactPanel = new ReactPanelPlugin({} as any).setPanelChangeHandler(onPanelTypeChanged as any); + + beforeEach(() => { + model.changePlugin( + getPanelPlugin({ + id: 'react', + exports: { + reactPanel, + }, + }) + ); + }); + + it('should call react onPanelTypeChanged', () => { + expect(onPanelTypeChanged.mock.calls.length).toBe(1); + expect(onPanelTypeChanged.mock.calls[0][1]).toBe('table'); + expect(onPanelTypeChanged.mock.calls[0][2].thresholds).toBeDefined(); + }); + }); + describe('get panel options', () => { it('should apply defaults', () => { model.options = { existingProp: 10 }; diff --git a/public/app/features/dashboard/state/PanelModel.ts b/public/app/features/dashboard/state/PanelModel.ts index a7ce1fdb250..dc6b502f447 100644 --- a/public/app/features/dashboard/state/PanelModel.ts +++ b/public/app/features/dashboard/state/PanelModel.ts @@ -282,11 +282,11 @@ export class PanelModel { this.type = pluginId; this.plugin = newPlugin; - // Callback that can validate and migrate any existing settings + // Let panel plugins inspect options from previous panel and keep any that it can use const onPanelTypeChanged = reactPanel ? reactPanel.onPanelTypeChanged : null; if (onPanelTypeChanged) { this.options = this.options || {}; - const old = oldOptions ? oldOptions.options : null; + const old = oldOptions ? oldOptions.options : {}; Object.assign(this.options, onPanelTypeChanged(this.options, oldPluginId, old)); } }