From 1ad4b44ec10be6d4da5454af8adf1e723a7eb163 Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Tue, 4 Dec 2018 14:19:55 +0100 Subject: [PATCH 1/3] POC on how to save away settings from a viztype and restore when switching back to it #14274 --- public/app/features/dashboard/panel_model.ts | 41 ++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/public/app/features/dashboard/panel_model.ts b/public/app/features/dashboard/panel_model.ts index 9643c23dbc1..223226a8f26 100644 --- a/public/app/features/dashboard/panel_model.ts +++ b/public/app/features/dashboard/panel_model.ts @@ -15,6 +15,7 @@ const notPersistedProperties: { [str: string]: boolean } = { fullscreen: true, isEditing: true, hasRefreshed: true, + cachedPluginOptions: true, }; // For angular panels we need to clean up properties when changing type @@ -51,12 +52,14 @@ const mustKeepProps: { [str: string]: boolean } = { events: true, cacheTimeout: true, nullPointMode: true, + cachedPluginOptions: true, }; const defaults: any = { gridPos: { x: 0, y: 0, h: 3, w: 6 }, datasource: null, targets: [{}], + cachedPluginOptions: {}, }; export class PanelModel { @@ -96,6 +99,9 @@ export class PanelModel { events: Emitter; cacheTimeout?: any; + // cache props between plugins + cachedPluginOptions?: any; + constructor(model) { this.events = new Emitter(); @@ -184,7 +190,40 @@ export class PanelModel { this.events.emit('panel-initialized'); } + getPanelOptions() { + return Object.keys(this).reduce((acc, property) => { + if (notPersistedProperties[property]) { + return acc; + } + return { + ...acc, + [property]: this[property], + }; + }, {}); + } + + saveCurrentPanelOptions() { + const currentOptions = this.getPanelOptions(); + this.cachedPluginOptions[this.type] = currentOptions; + } + + restorePanelOptions(pluginId: string) { + const currentOptions = this.getPanelOptions(); + const prevOptions = this.cachedPluginOptions[pluginId]; + const newOptions = Object.keys(prevOptions).reduce((acc, currKey: string) => { + return { + ...acc, + [currKey]: currentOptions[currKey] || prevOptions[currKey], + }; + }, {}); + + Object.keys(newOptions).map(property => { + this[property] = newOptions[property]; + }); + } + changeType(pluginId: string, fromAngularPanel: boolean) { + this.saveCurrentPanelOptions(); this.type = pluginId; // for now we need to remove alert rules when changing type @@ -202,6 +241,8 @@ export class PanelModel { delete this[key]; } } + + this.restorePanelOptions(pluginId); } destroy() { From b8c6577626ad546101ff6f68c30f0794fc6bb359 Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Tue, 4 Dec 2018 14:39:56 +0100 Subject: [PATCH 2/3] Let the cached props from previous visualization be the masters, unless specified in keepLatestProps const --- public/app/features/dashboard/panel_model.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/public/app/features/dashboard/panel_model.ts b/public/app/features/dashboard/panel_model.ts index 223226a8f26..9892c33bc4f 100644 --- a/public/app/features/dashboard/panel_model.ts +++ b/public/app/features/dashboard/panel_model.ts @@ -55,6 +55,12 @@ const mustKeepProps: { [str: string]: boolean } = { cachedPluginOptions: true, }; +// Keep current option value when switching visualization +const keepLatestProps: { [str: string]: boolean } = { + title: true, + description: true, +}; + const defaults: any = { gridPos: { x: 0, y: 0, h: 3, w: 6 }, datasource: null, @@ -209,11 +215,11 @@ export class PanelModel { restorePanelOptions(pluginId: string) { const currentOptions = this.getPanelOptions(); - const prevOptions = this.cachedPluginOptions[pluginId]; + const prevOptions = this.cachedPluginOptions[pluginId] || {}; const newOptions = Object.keys(prevOptions).reduce((acc, currKey: string) => { return { ...acc, - [currKey]: currentOptions[currKey] || prevOptions[currKey], + [currKey]: keepLatestProps[currKey] ? currentOptions[currKey] : prevOptions[currKey], }; }, {}); From 4872cef808bf0149e42fab6efbe8b6a1a36ad890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 5 Dec 2018 07:33:21 +0100 Subject: [PATCH 3/3] refactored and added tests for panel model remember properties --- public/app/features/dashboard/panel_model.ts | 35 ++++-------- .../dashboard/specs/panel_model.test.ts | 54 +++++++++++++++++++ 2 files changed, 63 insertions(+), 26 deletions(-) create mode 100644 public/app/features/dashboard/specs/panel_model.test.ts diff --git a/public/app/features/dashboard/panel_model.ts b/public/app/features/dashboard/panel_model.ts index 9892c33bc4f..ab4ff02def4 100644 --- a/public/app/features/dashboard/panel_model.ts +++ b/public/app/features/dashboard/panel_model.ts @@ -55,12 +55,6 @@ const mustKeepProps: { [str: string]: boolean } = { cachedPluginOptions: true, }; -// Keep current option value when switching visualization -const keepLatestProps: { [str: string]: boolean } = { - title: true, - description: true, -}; - const defaults: any = { gridPos: { x: 0, y: 0, h: 3, w: 6 }, datasource: null, @@ -132,7 +126,7 @@ export class PanelModel { } private getOptionsKey() { - return 'options-' + this.type; + return PANEL_OPTIONS_KEY_PREFIX + this.type; } getSaveModel() { @@ -196,9 +190,9 @@ export class PanelModel { this.events.emit('panel-initialized'); } - getPanelOptions() { + private getOptionsToRemember() { return Object.keys(this).reduce((acc, property) => { - if (notPersistedProperties[property]) { + if (notPersistedProperties[property] || mustKeepProps[property]) { return acc; } return { @@ -208,23 +202,15 @@ export class PanelModel { }, {}); } - saveCurrentPanelOptions() { - const currentOptions = this.getPanelOptions(); - this.cachedPluginOptions[this.type] = currentOptions; + private saveCurrentPanelOptions() { + this.cachedPluginOptions[this.type] = this.getOptionsToRemember(); } - restorePanelOptions(pluginId: string) { - const currentOptions = this.getPanelOptions(); + private restorePanelOptions(pluginId: string) { const prevOptions = this.cachedPluginOptions[pluginId] || {}; - const newOptions = Object.keys(prevOptions).reduce((acc, currKey: string) => { - return { - ...acc, - [currKey]: keepLatestProps[currKey] ? currentOptions[currKey] : prevOptions[currKey], - }; - }, {}); - Object.keys(newOptions).map(property => { - this[property] = newOptions[property]; + Object.keys(prevOptions).map(property => { + this[property] = prevOptions[property]; }); } @@ -232,15 +218,12 @@ export class PanelModel { this.saveCurrentPanelOptions(); this.type = pluginId; - // for now we need to remove alert rules when changing type - delete this.alert; - // for angular panels only we need to remove all events and let angular panels do some cleanup if (fromAngularPanel) { this.destroy(); for (const key of _.keys(this)) { - if (mustKeepProps[key] || key.indexOf(PANEL_OPTIONS_KEY_PREFIX) === 0) { + if (mustKeepProps[key]) { continue; } diff --git a/public/app/features/dashboard/specs/panel_model.test.ts b/public/app/features/dashboard/specs/panel_model.test.ts new file mode 100644 index 00000000000..36bb8d6297e --- /dev/null +++ b/public/app/features/dashboard/specs/panel_model.test.ts @@ -0,0 +1,54 @@ +import _ from 'lodash'; +import { PanelModel } from '../panel_model'; + +describe('PanelModel', () => { + describe('when creating new panel model', () => { + let model; + + beforeEach(() => { + model = new PanelModel({ + type: 'table', + showColumns: true, + }); + }); + + it('should apply defaults', () => { + expect(model.gridPos.h).toBe(3); + }); + + it('should set model props on instance', () => { + expect(model.showColumns).toBe(true); + }); + + it('getSaveModel should remove defaults', () => { + const saveModel = model.getSaveModel(); + expect(saveModel.gridPos).toBe(undefined); + }); + + it('getSaveModel should remove nonPersistedProperties', () => { + const saveModel = model.getSaveModel(); + expect(saveModel.events).toBe(undefined); + }); + + describe('when changing panel type', () => { + beforeEach(() => { + model.changeType('graph', true); + model.alert = { id: 2 }; + }); + + it('should remove table properties but keep core props', () => { + expect(model.showColumns).toBe(undefined); + }); + + it('should restore table properties when changing back', () => { + model.changeType('table', true); + expect(model.showColumns).toBe(true); + }); + + it('should remove alert rule when changing type that does not support it', () => { + model.changeType('table', true); + expect(model.alert).toBe(undefined); + }); + }); + }); +});