From b3c073ab6c0050ebee68e0f29c36aa5dcf05e49f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 23 Mar 2016 12:50:56 +0100 Subject: [PATCH] feat(panels): more panel refactoring, using events instead of overriding base class methods --- public/app/core/utils/emitter.ts | 2 +- public/app/features/dashboard/viewStateSrv.js | 4 +- .../app/features/panel/metrics_panel_ctrl.ts | 8 ++-- public/app/features/panel/panel_ctrl.ts | 18 ++++--- public/app/plugins/panel/dashlist/module.ts | 14 +++--- public/app/plugins/panel/graph/graph.js | 6 +-- public/app/plugins/panel/graph/legend.js | 2 +- public/app/plugins/panel/graph/module.ts | 17 +++---- .../plugins/panel/graph/specs/graph_specs.ts | 48 ++++++++++--------- public/app/plugins/panel/singlestat/module.ts | 14 +++--- public/app/plugins/panel/table/module.ts | 13 +++-- public/app/plugins/panel/text/module.ts | 12 ++--- public/test/core/utils/emitter_specs.ts | 30 ++++++++++++ 13 files changed, 111 insertions(+), 77 deletions(-) create mode 100644 public/test/core/utils/emitter_specs.ts diff --git a/public/app/core/utils/emitter.ts b/public/app/core/utils/emitter.ts index 1b7941dd094..6195f7c5e4b 100644 --- a/public/app/core/utils/emitter.ts +++ b/public/app/core/utils/emitter.ts @@ -14,7 +14,7 @@ export class Emitter { this.subjects = {}; } - emit(name, data) { + emit(name, data?) { var fnName = createName(name); this.subjects[fnName] || (this.subjects[fnName] = new Subject()); this.subjects[fnName].next(data); diff --git a/public/app/features/dashboard/viewStateSrv.js b/public/app/features/dashboard/viewStateSrv.js index f3112d6db50..2738105553a 100644 --- a/public/app/features/dashboard/viewStateSrv.js +++ b/public/app/features/dashboard/viewStateSrv.js @@ -139,7 +139,7 @@ function (angular, _, $) { self.$scope.broadcastRefresh(); } else { - self.fullscreenPanel.$broadcast('render'); + ctrl.render(); } delete self.fullscreenPanel; }); @@ -159,7 +159,7 @@ function (angular, _, $) { this.$scope.appEvent('panel-fullscreen-enter', {panelId: ctrl.panel.id}); $timeout(function() { - panelScope.$broadcast('render'); + ctrl.render(); }); }; diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts index b0857f066d1..8415172e3d1 100644 --- a/public/app/features/panel/metrics_panel_ctrl.ts +++ b/public/app/features/panel/metrics_panel_ctrl.ts @@ -44,16 +44,18 @@ class MetricsPanelCtrl extends PanelCtrl { if (!this.panel.targets) { this.panel.targets = [{}]; } + + this.events.on('refresh', this.onMetricsPanelRefresh.bind(this)); + this.events.on('init-edit-mode', this.onInitMetricsPanelEditMode.bind(this)); } - initEditMode() { - super.initEditMode(); + private onInitMetricsPanelEditMode() { this.addEditorTab('Metrics', 'public/app/partials/metrics.html'); this.addEditorTab('Time range', 'public/app/features/panel/partials/panelTime.html'); this.datasources = this.datasourceSrv.getMetricSources(); } - refresh() { + private onMetricsPanelRefresh() { // ignore fetching data if another panel is in fullscreen if (this.otherPanelInFullscreenMode()) { return; } diff --git a/public/app/features/panel/panel_ctrl.ts b/public/app/features/panel/panel_ctrl.ts index 824b1b87159..2821164bb10 100644 --- a/public/app/features/panel/panel_ctrl.ts +++ b/public/app/features/panel/panel_ctrl.ts @@ -18,7 +18,6 @@ export class PanelCtrl { editorTabIndex: number; pluginName: string; pluginId: string; - icon: string; editorTabs: any; $scope: any; $injector: any; @@ -60,7 +59,7 @@ export class PanelCtrl { } refresh() { - this.render(); + this.events.emit('refresh', null); } publishAppEvent(evtName, evt) { @@ -89,6 +88,7 @@ export class PanelCtrl { this.editorTabs = []; this.addEditorTab('General', 'public/app/partials/panelgeneral.html'); this.editModeInitiated = true; + this.events.emit('init-edit-mode', null); } addEditorTab(title, directiveFn, index?) { @@ -118,7 +118,9 @@ export class PanelCtrl { } getExtendedMenu() { - return [{text: 'Panel JSON', click: 'ctrl.editPanelJson(); dismiss();'}]; + var actions = [{text: 'Panel JSON', click: 'ctrl.editPanelJson(); dismiss();'}]; + this.events.emit('init-panel-actions', actions); + return actions; } otherPanelInFullscreenMode() { @@ -126,7 +128,6 @@ export class PanelCtrl { } calculatePanelHeight() { - if (this.fullscreen) { var docHeight = $(window).height(); var editHeight = Math.floor(docHeight * 0.3); @@ -142,8 +143,13 @@ export class PanelCtrl { this.height = this.containerHeight - (PANEL_PADDING + (this.panel.title ? TITLE_HEIGHT : EMPTY_TITLE_HEIGHT)); } - render(arg1?, arg2?) { - this.$scope.$broadcast('render', arg1, arg2); + render(payload?) { + // ignore if other panel is in fullscreen mode + if (this.otherPanelInFullscreenMode()) { + return; + } + + this.events.emit('render', payload); } toggleEditorHelp(index) { diff --git a/public/app/plugins/panel/dashlist/module.ts b/public/app/plugins/panel/dashlist/module.ts index 7d3f9d74bd0..e199e0cfc6e 100644 --- a/public/app/plugins/panel/dashlist/module.ts +++ b/public/app/plugins/panel/dashlist/module.ts @@ -28,18 +28,18 @@ class DashListCtrl extends PanelCtrl { this.panel.tags = [$scope.panel.tag]; delete this.panel.tag; } + + this.events.on('refresh', this.onRefresh.bind(this)); + this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); } - initEditMode() { - super.initEditMode(); + onInitEditMode() { + this.editorTabIndex = 1; this.modes = ['starred', 'search', 'recently viewed']; - this.icon = "fa fa-star"; - this.addEditorTab('Options', () => { - return {templateUrl: 'public/app/plugins/panel/dashlist/editor.html'}; - }); + this.addEditorTab('Options', 'public/app/plugins/panel/dashlist/editor.html'); } - refresh() { + onRefresh() { var params: any = {limit: this.panel.limit}; if (this.panel.mode === 'recently viewed') { diff --git a/public/app/plugins/panel/graph/graph.js b/public/app/plugins/panel/graph/graph.js index 098d86a294f..0f53852cec0 100755 --- a/public/app/plugins/panel/graph/graph.js +++ b/public/app/plugins/panel/graph/graph.js @@ -54,7 +54,7 @@ function (angular, $, moment, _, kbn, GraphTooltip) { }, scope); // Receive render events - scope.$on('render',function(event, renderData) { + ctrl.events.on('render', function(renderData) { data = renderData || data; if (!data) { ctrl.refresh(); @@ -97,10 +97,6 @@ function (angular, $, moment, _, kbn, GraphTooltip) { return true; } - if (ctrl.otherPanelInFullscreenMode()) { - return true; - } - if (!setElementHeight()) { return true; } if(_.isString(data)) { diff --git a/public/app/plugins/panel/graph/legend.js b/public/app/plugins/panel/graph/legend.js index d6d1bdbf2dd..22bbb756b59 100644 --- a/public/app/plugins/panel/graph/legend.js +++ b/public/app/plugins/panel/graph/legend.js @@ -22,7 +22,7 @@ function (angular, _, $) { var seriesList; var i; - scope.$on('render', function() { + ctrl.events.on('render', function() { data = ctrl.seriesList; if (data) { render(); diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index 736e84b8411..29c2dccbac2 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -110,12 +110,11 @@ class GraphCtrl extends MetricsPanelCtrl { this.events.on('data-received', this.onDataReceived.bind(this)); this.events.on('data-error', this.onDataError.bind(this)); this.events.on('data-snapshot-load', this.onDataSnapshotLoad.bind(this)); + this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); + this.events.on('init-panel-actions', this.onInitPanelActions.bind(this)); } - initEditMode() { - super.initEditMode(); - - this.icon = "fa fa-bar-chart"; + onInitEditMode() { this.addEditorTab('Axes & Grid', 'public/app/plugins/panel/graph/axisEditor.html', 2); this.addEditorTab('Display Styles', 'public/app/plugins/panel/graph/styleEditor.html', 3); @@ -129,12 +128,10 @@ class GraphCtrl extends MetricsPanelCtrl { this.unitFormats = kbn.getUnitFormats(); } - getExtendedMenu() { - var menu = super.getExtendedMenu(); - menu.push({text: 'Export CSV (series as rows)', click: 'ctrl.exportCsv()'}); - menu.push({text: 'Export CSV (series as columns)', click: 'ctrl.exportCsvColumns()'}); - menu.push({text: 'Toggle legend', click: 'ctrl.toggleLegend()'}); - return menu; + onInitPanelActions(actions) { + actions.push({text: 'Export CSV (series as rows)', click: 'ctrl.exportCsv()'}); + actions.push({text: 'Export CSV (series as columns)', click: 'ctrl.exportCsvColumns()'}); + actions.push({text: 'Toggle legend', click: 'ctrl.toggleLegend()'}); } setUnitFormat(axis, subItem) { diff --git a/public/app/plugins/panel/graph/specs/graph_specs.ts b/public/app/plugins/panel/graph/specs/graph_specs.ts index dae90224ff8..28b38101004 100644 --- a/public/app/plugins/panel/graph/specs/graph_specs.ts +++ b/public/app/plugins/panel/graph/specs/graph_specs.ts @@ -8,6 +8,7 @@ import $ from 'jquery'; import helpers from 'test/specs/helpers'; import TimeSeries from 'app/core/time_series2'; import moment from 'moment'; +import {Emitter} from 'app/core/core'; describe('grafanaGraph', function() { @@ -24,31 +25,33 @@ describe('grafanaGraph', function() { })); beforeEach(angularMocks.inject(function($rootScope, $compile) { - var ctrl: any = {}; + var ctrl: any = { + events: new Emitter(), + height: 200, + panel: { + legend: {}, + grid: { }, + y_formats: [], + seriesOverrides: [], + tooltip: { + shared: true + } + }, + renderingCompleted: sinon.spy(), + hiddenSeries: {}, + dashboard: {timezone: 'browser'}, + range: { + from: moment([2015, 1, 1, 10]), + to: moment([2015, 1, 1, 22]), + }, + }; + var scope = $rootScope.$new(); scope.ctrl = ctrl; - var element = angular.element("
"); - ctrl.height = '200px'; - ctrl.panel = { - legend: {}, - grid: { }, - y_formats: [], - seriesOverrides: [], - tooltip: { - shared: true - } - }; $rootScope.onAppEvent = sinon.spy(); - ctrl.otherPanelInFullscreenMode = sinon.spy(); - ctrl.renderingCompleted = sinon.spy(); - ctrl.hiddenSeries = {}; - ctrl.dashboard = { timezone: 'browser' }; - ctrl.range = { - from: moment([2015, 1, 1, 10]), - to: moment([2015, 1, 1, 22]), - }; + ctx.data = []; ctx.data.push(new TimeSeries({ datapoints: [[1,1],[2,2]], @@ -61,11 +64,12 @@ describe('grafanaGraph', function() { setupFunc(ctrl, ctx.data); + var element = angular.element("
"); $compile(element)(scope); scope.$digest(); - $.plot = ctx.plotSpy = sinon.spy(); - scope.$emit('render', ctx.data); + $.plot = ctx.plotSpy = sinon.spy(); + ctrl.events.emit('render', ctx.data); ctx.plotData = ctx.plotSpy.getCall(0).args[1]; ctx.plotOptions = ctx.plotSpy.getCall(0).args[2]; })); diff --git a/public/app/plugins/panel/singlestat/module.ts b/public/app/plugins/panel/singlestat/module.ts index b196ef7e9f1..bbae79d4e65 100644 --- a/public/app/plugins/panel/singlestat/module.ts +++ b/public/app/plugins/panel/singlestat/module.ts @@ -235,14 +235,7 @@ class SingleStatCtrl extends MetricsPanelCtrl { var templateSrv = this.templateSrv; var data, linkInfo; var $panelContainer = elem.find('.panel-container'); - // change elem to singlestat panel elem = elem.find('.singlestat-panel'); - hookupDrilldownLinkTooltip(); - - scope.$on('render', function() { - render(); - ctrl.renderingCompleted(); - }); function setElementHeight() { elem.css('height', ctrl.height + 'px'); @@ -417,6 +410,13 @@ class SingleStatCtrl extends MetricsPanelCtrl { drilldownTooltip.place_tt(e.pageX+20, e.pageY-15); }); } + + hookupDrilldownLinkTooltip(); + + this.events.on('render', function() { + render(); + ctrl.renderingCompleted(); + }); } } diff --git a/public/app/plugins/panel/table/module.ts b/public/app/plugins/panel/table/module.ts index bcce647b8ca..d5ac802a62b 100644 --- a/public/app/plugins/panel/table/module.ts +++ b/public/app/plugins/panel/table/module.ts @@ -61,17 +61,16 @@ class TablePanelCtrl extends MetricsPanelCtrl { this.events.on('data-received', this.onDataReceived.bind(this)); this.events.on('data-error', this.onDataError.bind(this)); this.events.on('data-snapshot-load', this.onDataSnapshotLoad.bind(this)); + this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); + this.events.on('init-panel-actions', this.onInitPanelActions.bind(this)); } - initEditMode() { - super.initEditMode(); + onInitEditMode() { this.addEditorTab('Options', tablePanelEditor, 2); } - getExtendedMenu() { - var menu = super.getExtendedMenu(); - menu.push({text: 'Export CSV', click: 'ctrl.exportCsv()'}); - return menu; + onInitPanelActions(actions) { + actions.push({text: 'Export CSV', click: 'ctrl.exportCsv()'}); } issueQueries(datasource) { @@ -211,7 +210,7 @@ class TablePanelCtrl extends MetricsPanelCtrl { elem.off('click', '.table-panel-page-link'); }); - scope.$on('render', function(event, renderData) { + ctrl.events.on('render', function(renderData) { data = renderData || data; if (data) { renderPanel(); diff --git a/public/app/plugins/panel/text/module.ts b/public/app/plugins/panel/text/module.ts index f30b7c619bb..42657788c0a 100644 --- a/public/app/plugins/panel/text/module.ts +++ b/public/app/plugins/panel/text/module.ts @@ -20,18 +20,18 @@ export class TextPanelCtrl extends PanelCtrl { super($scope, $injector); _.defaults(this.panel, panelDefaults); + + this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); + this.events.on('refresh', this.onRender.bind(this)); + this.events.on('render', this.onRender.bind(this)); } - initEditMode() { - super.initEditMode(); - this.icon = 'fa fa-text-width'; + onInitEditMode() { this.addEditorTab('Options', 'public/app/plugins/panel/text/editor.html'); this.editorTabIndex = 1; } - render() { - super.render(); - + onRender() { if (this.panel.mode === 'markdown') { this.renderMarkdown(this.panel.content); } else if (this.panel.mode === 'html') { diff --git a/public/test/core/utils/emitter_specs.ts b/public/test/core/utils/emitter_specs.ts new file mode 100644 index 00000000000..6ab5f80fa0e --- /dev/null +++ b/public/test/core/utils/emitter_specs.ts @@ -0,0 +1,30 @@ +import {describe, beforeEach, it, sinon, expect} from 'test/lib/common' + +import {Emitter} from 'app/core/core'; + +describe("Emitter", () => { + + describe('given 2 subscribers', () => { + + it('should notfiy subscribers', () => { + var events = new Emitter(); + var sub1Called = false; + var sub2Called = false; + + events.on('test', () => { + sub1Called = true; + }); + events.on('test', () => { + sub2Called = true; + }); + + events.emit('test', null); + + expect(sub1Called).to.be(true); + expect(sub2Called).to.be(true); + }); + }); + +}); + +