From 13efc529ecbd4b68dcab6c76ed1b5c48be801afe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 2 Jan 2018 14:52:30 +0100 Subject: [PATCH 01/54] poc: began react panel experiments --- .../dashboard/dashgrid/DashboardPanel.tsx | 78 ++++++++++++++++--- .../app/features/plugins/built_in_plugins.ts | 2 + public/app/plugins/panel/text2/README.md | 5 ++ .../panel/text2/img/icn-text-panel.svg | 26 +++++++ public/app/plugins/panel/text2/module.tsx | 13 ++++ public/app/plugins/panel/text2/plugin.json | 17 ++++ 6 files changed, 129 insertions(+), 12 deletions(-) create mode 100644 public/app/plugins/panel/text2/README.md create mode 100644 public/app/plugins/panel/text2/img/icn-text-panel.svg create mode 100644 public/app/plugins/panel/text2/module.tsx create mode 100644 public/app/plugins/panel/text2/plugin.json diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx index 27fe64d4660..562b79a859e 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -1,9 +1,12 @@ import React from 'react'; -import {PanelModel} from '../panel_model'; -import {PanelContainer} from './PanelContainer'; -import {AttachedPanel} from './PanelLoader'; -import {DashboardRow} from './DashboardRow'; -import {AddPanelPanel} from './AddPanelPanel'; +import config from 'app/core/config'; +import classNames from 'classnames'; +import { PanelModel } from '../panel_model'; +import { PanelContainer } from './PanelContainer'; +import { AttachedPanel } from './PanelLoader'; +import { DashboardRow } from './DashboardRow'; +import { AddPanelPanel } from './AddPanelPanel'; +import { importPluginModule } from 'app/features/plugins/plugin_loader'; export interface DashboardPanelProps { panel: PanelModel; @@ -13,10 +16,26 @@ export interface DashboardPanelProps { export class DashboardPanel extends React.Component { element: any; attachedPanel: AttachedPanel; + pluginInfo: any; + pluginExports: any; + specialPanels = {}; constructor(props) { super(props); this.state = {}; + + this.specialPanels['row'] = this.renderRow.bind(this); + this.specialPanels['add-panel'] = this.renderAddPanel.bind(this); + + if (!this.isSpecial()) { + this.pluginInfo = config.panels[this.props.panel.type]; + + // load panel plugin + importPluginModule(this.pluginInfo.module).then(pluginExports => { + this.pluginExports = pluginExports; + this.forceUpdate(); + }); + } } componentDidMount() { @@ -36,19 +55,54 @@ export class DashboardPanel extends React.Component { } } + isSpecial() { + return this.specialPanels[this.props.panel.type]; + } + + renderRow() { + return ; + } + + renderAddPanel() { + return ; + } + render() { - // special handling for rows - if (this.props.panel.type === 'row') { - return ; + if (this.isSpecial()) { + return this.specialPanels[this.props.panel.type](); } - if (this.props.panel.type === 'add-panel') { - return ; + let isFullscreen = false; + let isLoading = false; + let panelHeaderClass = classNames({ 'panel-header': true, 'grid-drag-handle': !isFullscreen }); + let PanelComponent = null; + + if (this.pluginExports && this.pluginExports.PanelComponent) { + PanelComponent = this.pluginExports.PanelComponent; } return ( -
this.element = element} className="panel-height-helper" /> +
+
+ + + + + + {isLoading && ( + + + + )} +
{this.props.panel.title}
+
+ +
{PanelComponent && }
+
); + + // return ( + //
this.element = element} className="panel-height-helper" /> + // ); } } - diff --git a/public/app/features/plugins/built_in_plugins.ts b/public/app/features/plugins/built_in_plugins.ts index c86efc4f695..a4657f84aa1 100644 --- a/public/app/features/plugins/built_in_plugins.ts +++ b/public/app/features/plugins/built_in_plugins.ts @@ -10,6 +10,7 @@ import * as postgresPlugin from 'app/plugins/datasource/postgres/module'; import * as prometheusPlugin from 'app/plugins/datasource/prometheus/module'; import * as textPanel from 'app/plugins/panel/text/module'; +import * as text2Panel from 'app/plugins/panel/text2/module'; import * as graphPanel from 'app/plugins/panel/graph/module'; import * as dashListPanel from 'app/plugins/panel/dashlist/module'; import * as pluginsListPanel from 'app/plugins/panel/pluginlist/module'; @@ -37,6 +38,7 @@ const builtInPlugins = { 'app/plugins/app/testdata/datasource/module': testDataDSPlugin, 'app/plugins/panel/text/module': textPanel, + 'app/plugins/panel/text2/module': text2Panel, 'app/plugins/panel/graph/module': graphPanel, 'app/plugins/panel/dashlist/module': dashListPanel, 'app/plugins/panel/pluginlist/module': pluginsListPanel, diff --git a/public/app/plugins/panel/text2/README.md b/public/app/plugins/panel/text2/README.md new file mode 100644 index 00000000000..667ab51784a --- /dev/null +++ b/public/app/plugins/panel/text2/README.md @@ -0,0 +1,5 @@ +# 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 new file mode 100644 index 00000000000..a9d0a1d2c4a --- /dev/null +++ b/public/app/plugins/panel/text2/img/icn-text-panel.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/public/app/plugins/panel/text2/module.tsx b/public/app/plugins/panel/text2/module.tsx new file mode 100644 index 00000000000..7f5e363891c --- /dev/null +++ b/public/app/plugins/panel/text2/module.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +export class ReactTestPanel extends React.Component { + constructor(props) { + super(props); + } + + render() { + return

Panel content

; + } +} + +export { ReactTestPanel as PanelComponent }; diff --git a/public/app/plugins/panel/text2/plugin.json b/public/app/plugins/panel/text2/plugin.json new file mode 100644 index 00000000000..95d821bfd50 --- /dev/null +++ b/public/app/plugins/panel/text2/plugin.json @@ -0,0 +1,17 @@ +{ + "type": "panel", + "name": "Text2", + "id": "text2", + + "info": { + "author": { + "name": "Grafana Project", + "url": "https://grafana.com" + }, + "logos": { + "small": "img/icn-text-panel.svg", + "large": "img/icn-text-panel.svg" + } + } +} + From 3eb5f232094e20caf03d428d1f7031c8b6e15459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 3 Jan 2018 13:03:26 +0100 Subject: [PATCH 02/54] poc: began react panel experiments, step2 --- .../dashboard/dashgrid/DashboardPanel.tsx | 77 +++++++++++++++---- public/app/features/panel/panel_header.ts | 15 ---- public/sass/pages/_dashboard.scss | 22 +++--- 3 files changed, 71 insertions(+), 43 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx index 562b79a859e..7a0a9bb2e86 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -1,6 +1,7 @@ import React from 'react'; import config from 'app/core/config'; import classNames from 'classnames'; +import appEvents from 'app/core/app_events'; import { PanelModel } from '../panel_model'; import { PanelContainer } from './PanelContainer'; import { AttachedPanel } from './PanelLoader'; @@ -72,9 +73,6 @@ export class DashboardPanel extends React.Component { return this.specialPanels[this.props.panel.type](); } - let isFullscreen = false; - let isLoading = false; - let panelHeaderClass = classNames({ 'panel-header': true, 'grid-drag-handle': !isFullscreen }); let PanelComponent = null; if (this.pluginExports && this.pluginExports.PanelComponent) { @@ -83,20 +81,7 @@ export class DashboardPanel extends React.Component { return (
-
- - - - - - {isLoading && ( - - - - )} -
{this.props.panel.title}
-
- +
{PanelComponent && }
); @@ -106,3 +91,61 @@ export class DashboardPanel extends React.Component { // ); } } + +interface PanelHeaderProps { + panel: any; +} + +export class PanelHeader extends React.Component { + onEditPanel = () => { + appEvents.emit('panel-change-view', { + fullscreen: true, + edit: true, + panelId: this.props.panel.id, + }); + }; + + render() { + let isFullscreen = false; + let isLoading = false; + let panelHeaderClass = classNames({ 'panel-header': true, 'grid-drag-handle': !isFullscreen }); + + return ( +
+ + + + + + {isLoading && ( + + + + )} + +
+ + + {this.props.panel.title} + + + + + + 4m + + +
+
+ ); + } +} diff --git a/public/app/features/panel/panel_header.ts b/public/app/features/panel/panel_header.ts index ca6ed68b648..cb9f2d5c563 100644 --- a/public/app/features/panel/panel_header.ts +++ b/public/app/features/panel/panel_header.ts @@ -10,21 +10,6 @@ var template = ` {{ctrl.timeInfo}} diff --git a/public/sass/pages/_dashboard.scss b/public/sass/pages/_dashboard.scss index af9a02caa2a..6d4e94a5175 100644 --- a/public/sass/pages/_dashboard.scss +++ b/public/sass/pages/_dashboard.scss @@ -40,6 +40,14 @@ div.flot-text { background-color: transparent; border: none; } + + &:hover { + .panel-menu-toggle { + visibility: visible; + transition: opacity 0.1s ease-in 0.2s; + opacity: 1; + } + } } .panel-content { @@ -159,7 +167,7 @@ div.flot-text { display: block; @include panel-corner-color(lighten($panel-bg, 4%)); .fa:before { - content: "\f129"; + content: '\f129'; } } @@ -170,7 +178,7 @@ div.flot-text { left: -5px; } .fa:before { - content: "\f08e"; + content: '\f08e'; } } @@ -179,19 +187,11 @@ div.flot-text { color: $text-color; @include panel-corner-color($popover-error-bg); .fa:before { - content: "\f12a"; + content: '\f12a'; } } } -.panel-hover-highlight { - .panel-menu-toggle { - visibility: visible; - transition: opacity 0.1s ease-in 0.2s; - opacity: 1; - } -} - .panel-time-info { font-weight: bold; float: right; From 456b4d2a66add0f15313dbbe6c8cdfb666ad9c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 3 Jan 2018 13:33:54 +0100 Subject: [PATCH 03/54] poc: began react panel experiments, step2 --- public/app/core/directives/dash_class.js | 40 ------------------- public/app/core/directives/dash_class.ts | 34 ++++++++++++++++ .../dashboard/dashgrid/AddPanelPanel.tsx | 7 ++-- .../dashboard/dashgrid/DashboardGrid.tsx | 10 +++-- .../dashboard/dashgrid/DashboardPanel.tsx | 36 ++++------------- .../dashboard/dashgrid/DashboardRow.tsx | 20 +++------- .../dashboard/specs/DashboardRow.jest.tsx | 14 ++----- 7 files changed, 60 insertions(+), 101 deletions(-) delete mode 100644 public/app/core/directives/dash_class.js create mode 100644 public/app/core/directives/dash_class.ts diff --git a/public/app/core/directives/dash_class.js b/public/app/core/directives/dash_class.js deleted file mode 100644 index 9df53bdbd48..00000000000 --- a/public/app/core/directives/dash_class.js +++ /dev/null @@ -1,40 +0,0 @@ -define([ - 'lodash', - 'jquery', - '../core_module', -], -function (_, $, coreModule) { - 'use strict'; - - coreModule.default.directive('dashClass', function() { - return { - link: function($scope, elem) { - - $scope.onAppEvent('panel-fullscreen-enter', function() { - elem.toggleClass('panel-in-fullscreen', true); - }); - - $scope.onAppEvent('panel-fullscreen-exit', function() { - elem.toggleClass('panel-in-fullscreen', false); - }); - - $scope.$watch('ctrl.playlistSrv.isPlaying', function(newValue) { - elem.toggleClass('playlist-active', newValue === true); - }); - - $scope.$watch('ctrl.dashboardViewState.state.editview', function(newValue) { - if (newValue) { - elem.toggleClass('dashboard-page--settings-opening', _.isString(newValue)); - setTimeout(function() { - elem.toggleClass('dashboard-page--settings-open', _.isString(newValue)); - }, 10); - } else { - elem.removeClass('dashboard-page--settings-opening'); - elem.removeClass('dashboard-page--settings-open'); - } - }); - } - }; - }); - -}); diff --git a/public/app/core/directives/dash_class.ts b/public/app/core/directives/dash_class.ts new file mode 100644 index 00000000000..f0723f4fec7 --- /dev/null +++ b/public/app/core/directives/dash_class.ts @@ -0,0 +1,34 @@ +import _ from 'lodash'; +import coreModule from '../core_module'; + +coreModule.directive('dashClass', function($timeout) { + return { + link: function($scope, elem) { + $scope.ctrl.dashboard.events.on('view-mode-changed', function(panel) { + $timeout(() => { + elem.toggleClass('panel-in-fullscreen', panel.fullscreen === true); + }); + }); + + $scope.onAppEvent('panel-fullscreen-exit', function() { + elem.toggleClass('panel-in-fullscreen', false); + }); + + $scope.$watch('ctrl.playlistSrv.isPlaying', function(newValue) { + elem.toggleClass('playlist-active', newValue === true); + }); + + $scope.$watch('ctrl.dashboardViewState.state.editview', function(newValue) { + if (newValue) { + elem.toggleClass('dashboard-page--settings-opening', _.isString(newValue)); + setTimeout(function() { + elem.toggleClass('dashboard-page--settings-open', _.isString(newValue)); + }, 10); + } else { + elem.removeClass('dashboard-page--settings-opening'); + elem.removeClass('dashboard-page--settings-open'); + } + }); + }, + }; +}); diff --git a/public/app/features/dashboard/dashgrid/AddPanelPanel.tsx b/public/app/features/dashboard/dashgrid/AddPanelPanel.tsx index 1f143f3d7f7..483ccb52bda 100644 --- a/public/app/features/dashboard/dashgrid/AddPanelPanel.tsx +++ b/public/app/features/dashboard/dashgrid/AddPanelPanel.tsx @@ -3,14 +3,14 @@ import _ from 'lodash'; import config from 'app/core/config'; import { PanelModel } from '../panel_model'; -import { PanelContainer } from './PanelContainer'; +import { DashboardModel } from '../dashboard_model'; import ScrollBar from 'app/core/components/ScrollBar/ScrollBar'; import store from 'app/core/store'; import { LS_PANEL_COPY_KEY } from 'app/core/constants'; export interface AddPanelPanelProps { panel: PanelModel; - getPanelContainer: () => PanelContainer; + dashboard: DashboardModel; } export interface AddPanelPanelState { @@ -55,8 +55,7 @@ export class AddPanelPanel extends React.Component { - const panelContainer = this.props.getPanelContainer(); - const dashboard = panelContainer.getDashboard(); + const dashboard = this.props.dashboard; const { gridPos } = this.props.panel; var newPanel: any = { diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx index 3f65c33c90d..0bb75c54963 100644 --- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx @@ -50,7 +50,8 @@ function GridWrapper({ onResize={onResize} onResizeStop={onResizeStop} onDragStop={onDragStop} - onLayoutChange={onLayoutChange}> + onLayoutChange={onLayoutChange} + > {children} ); @@ -177,8 +178,8 @@ export class DashboardGrid extends React.Component { const panelClasses = classNames({ panel: true, 'panel--fullscreen': panel.fullscreen }); panelElements.push(
- -
, + +
); } @@ -196,7 +197,8 @@ export class DashboardGrid extends React.Component { onWidthChange={this.onWidthChange} onDragStop={this.onDragStop} onResize={this.onResize} - onResizeStop={this.onResizeStop}> + onResizeStop={this.onResizeStop} + > {this.renderPanels()} ); diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx index 7a0a9bb2e86..eecf532922d 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -3,7 +3,7 @@ import config from 'app/core/config'; import classNames from 'classnames'; import appEvents from 'app/core/app_events'; import { PanelModel } from '../panel_model'; -import { PanelContainer } from './PanelContainer'; +import { DashboardModel } from '../dashboard_model'; import { AttachedPanel } from './PanelLoader'; import { DashboardRow } from './DashboardRow'; import { AddPanelPanel } from './AddPanelPanel'; @@ -11,7 +11,7 @@ import { importPluginModule } from 'app/features/plugins/plugin_loader'; export interface DashboardPanelProps { panel: PanelModel; - getPanelContainer: () => PanelContainer; + dashboard: DashboardModel; } export class DashboardPanel extends React.Component { @@ -39,33 +39,16 @@ export class DashboardPanel extends React.Component { } } - componentDidMount() { - if (!this.element) { - return; - } - - const panelContainer = this.props.getPanelContainer(); - const dashboard = panelContainer.getDashboard(); - const loader = panelContainer.getPanelLoader(); - this.attachedPanel = loader.load(this.element, this.props.panel, dashboard); - } - - componentWillUnmount() { - if (this.attachedPanel) { - this.attachedPanel.destroy(); - } - } - isSpecial() { return this.specialPanels[this.props.panel.type]; } renderRow() { - return ; + return ; } renderAddPanel() { - return ; + return ; } render() { @@ -81,7 +64,7 @@ export class DashboardPanel extends React.Component { return (
- +
{PanelComponent && }
); @@ -93,16 +76,13 @@ export class DashboardPanel extends React.Component { } interface PanelHeaderProps { - panel: any; + panel: PanelModel; + dashboard: DashboardModel; } export class PanelHeader extends React.Component { onEditPanel = () => { - appEvents.emit('panel-change-view', { - fullscreen: true, - edit: true, - panelId: this.props.panel.id, - }); + this.props.dashboard.setViewMode(this.props.panel, true, true); }; render() { diff --git a/public/app/features/dashboard/dashgrid/DashboardRow.tsx b/public/app/features/dashboard/dashgrid/DashboardRow.tsx index fad7c120f65..17dd1681c66 100644 --- a/public/app/features/dashboard/dashgrid/DashboardRow.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardRow.tsx @@ -1,19 +1,16 @@ import React from 'react'; import classNames from 'classnames'; import { PanelModel } from '../panel_model'; -import { PanelContainer } from './PanelContainer'; +import { DashboardModel } from '../dashboard_model'; import templateSrv from 'app/features/templating/template_srv'; import appEvents from 'app/core/app_events'; export interface DashboardRowProps { panel: PanelModel; - getPanelContainer: () => PanelContainer; + dashboard: DashboardModel; } export class DashboardRow extends React.Component { - dashboard: any; - panelContainer: any; - constructor(props) { super(props); @@ -21,16 +18,13 @@ export class DashboardRow extends React.Component { collapsed: this.props.panel.collapsed, }; - this.panelContainer = this.props.getPanelContainer(); - this.dashboard = this.panelContainer.getDashboard(); - this.toggle = this.toggle.bind(this); this.openSettings = this.openSettings.bind(this); this.delete = this.delete.bind(this); } toggle() { - this.dashboard.toggleRow(this.props.panel); + this.props.dashboard.toggleRow(this.props.panel); this.setState(prevState => { return { collapsed: !prevState.collapsed }; @@ -55,14 +49,10 @@ export class DashboardRow extends React.Component { altActionText: 'Delete row only', icon: 'fa-trash', onConfirm: () => { - const panelContainer = this.props.getPanelContainer(); - const dashboard = panelContainer.getDashboard(); - dashboard.removeRow(this.props.panel, true); + this.props.dashboard.removeRow(this.props.panel, true); }, onAltAction: () => { - const panelContainer = this.props.getPanelContainer(); - const dashboard = panelContainer.getDashboard(); - dashboard.removeRow(this.props.panel, false); + this.props.dashboard.removeRow(this.props.panel, false); }, }); } diff --git a/public/app/features/dashboard/specs/DashboardRow.jest.tsx b/public/app/features/dashboard/specs/DashboardRow.jest.tsx index 2d44f2e0e74..3270abc9de9 100644 --- a/public/app/features/dashboard/specs/DashboardRow.jest.tsx +++ b/public/app/features/dashboard/specs/DashboardRow.jest.tsx @@ -4,18 +4,13 @@ import { DashboardRow } from '../dashgrid/DashboardRow'; import { PanelModel } from '../panel_model'; describe('DashboardRow', () => { - let wrapper, panel, getPanelContainer, dashboardMock; + let wrapper, panel, dashboardMock; beforeEach(() => { - dashboardMock = {toggleRow: jest.fn()}; + dashboardMock = { toggleRow: jest.fn() }; - getPanelContainer = jest.fn().mockReturnValue({ - getDashboard: jest.fn().mockReturnValue(dashboardMock), - getPanelLoader: jest.fn() - }); - - panel = new PanelModel({collapsed: false}); - wrapper = shallow(); + panel = new PanelModel({ collapsed: false }); + wrapper = shallow(); }); it('Should not have collapsed class when collaped is false', () => { @@ -29,5 +24,4 @@ describe('DashboardRow', () => { expect(wrapper.find('.dashboard-row--collapsed')).toHaveLength(1); expect(dashboardMock.toggleRow.mock.calls).toHaveLength(1); }); - }); From 636cca83320ebce7b82b2bb65b97f67581f9986e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 2 Feb 2018 16:01:01 +0100 Subject: [PATCH 04/54] poc: merge sync --- public/app/features/dashboard/dashgrid/AddPanelPanel.tsx | 4 +--- public/app/features/dashboard/dashgrid/DashboardPanel.tsx | 1 - public/app/features/dashboard/dashgrid/DashboardRow.tsx | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/AddPanelPanel.tsx b/public/app/features/dashboard/dashgrid/AddPanelPanel.tsx index eb6a4de00ae..beae2650d3e 100644 --- a/public/app/features/dashboard/dashgrid/AddPanelPanel.tsx +++ b/public/app/features/dashboard/dashgrid/AddPanelPanel.tsx @@ -86,9 +86,7 @@ export class AddPanelPanel extends React.Component { } update() { - this.dashboard.processRepeats(); + this.props.dashboard.processRepeats(); this.forceUpdate(); } From 35403c18753e9c6364a90b1e84d81a0e28725282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 19 Jun 2018 08:42:41 +0200 Subject: [PATCH 05/54] wip: react panel makeover mini progress --- public/app/core/directives/dash_class.ts | 4 +- .../dashboard/dashgrid/DashboardGrid.tsx | 1 + .../dashboard/dashgrid/DashboardPanel.tsx | 77 ++++++++++++++++- public/app/features/dashboard/panel_model.ts | 6 ++ .../dashboard/specs/AddPanelPanel.jest.tsx | 7 +- .../app/features/dashboard/view_state_srv.ts | 84 +++++-------------- 6 files changed, 102 insertions(+), 77 deletions(-) diff --git a/public/app/core/directives/dash_class.ts b/public/app/core/directives/dash_class.ts index f0723f4fec7..1dab57da0d4 100644 --- a/public/app/core/directives/dash_class.ts +++ b/public/app/core/directives/dash_class.ts @@ -10,9 +10,7 @@ coreModule.directive('dashClass', function($timeout) { }); }); - $scope.onAppEvent('panel-fullscreen-exit', function() { - elem.toggleClass('panel-in-fullscreen', false); - }); + elem.toggleClass('panel-in-fullscreen', $scope.ctrl.dashboard.meta.fullscreen === true); $scope.$watch('ctrl.playlistSrv.isPlaying', function(newValue) { elem.toggleClass('playlist-active', newValue === true); diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx index 0bb75c54963..9f2e449b49e 100644 --- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx @@ -175,6 +175,7 @@ export class DashboardGrid extends React.Component { const panelElements = []; for (let panel of this.dashboard.panels) { + console.log('panel.fullscreen', panel.fullscreen); const panelClasses = classNames({ panel: true, 'panel--fullscreen': panel.fullscreen }); panelElements.push(
diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx index 2b864dc47d0..fe97ac6039a 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import $ from 'jquery'; import config from 'app/core/config'; import classNames from 'classnames'; import { PanelModel } from '../panel_model'; @@ -7,6 +8,11 @@ import { AttachedPanel } from './PanelLoader'; import { DashboardRow } from './DashboardRow'; import { AddPanelPanel } from './AddPanelPanel'; import { importPluginModule } from 'app/features/plugins/plugin_loader'; +import { store } from 'app/stores/store'; +import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN } from 'app/core/constants'; + +const TITLE_HEIGHT = 27; +const PANEL_BORDER = 2; export interface DashboardPanelProps { panel: PanelModel; @@ -61,13 +67,40 @@ export class DashboardPanel extends React.Component { PanelComponent = this.pluginExports.PanelComponent; } + let panelContentStyle = { + height: this.getPanelHeight(), + }; + return ( -
- -
{PanelComponent && }
+
+
+ +
+ {PanelComponent && } +
+
+
+ {this.props.panel.isEditing && } +
); } + + getPanelHeight() { + const panel = this.props.panel; + let height = 0; + + if (panel.fullscreen) { + var docHeight = $(window).height(); + var editHeight = Math.floor(docHeight * 0.4); + var fullscreenHeight = Math.floor(docHeight * 0.8); + height = panel.isEditing ? editHeight : fullscreenHeight; + } else { + height = panel.gridPos.h * GRID_CELL_HEIGHT + (panel.gridPos.h - 1) * GRID_CELL_VMARGIN; + } + + return height - PANEL_BORDER + TITLE_HEIGHT; + } } interface PanelHeaderProps { @@ -77,7 +110,11 @@ interface PanelHeaderProps { export class PanelHeader extends React.Component { onEditPanel = () => { - this.props.dashboard.setViewMode(this.props.panel, true, true); + store.view.updateQuery({ + panelId: this.props.panel.id, + edit: true, + fullscreen: true, + }); }; render() { @@ -124,3 +161,35 @@ export class PanelHeader extends React.Component { ); } } + +interface PanelEditorProps { + panel: PanelModel; + dashboard: DashboardModel; +} + +export class PanelEditor extends React.Component { + render() { + return ( +
+
+

{this.props.panel.type}

+ + + + +
+ +
testing
+
+ ); + } +} diff --git a/public/app/features/dashboard/panel_model.ts b/public/app/features/dashboard/panel_model.ts index daca8e60f8e..2cf46bfdf53 100644 --- a/public/app/features/dashboard/panel_model.ts +++ b/public/app/features/dashboard/panel_model.ts @@ -13,6 +13,7 @@ const notPersistedProperties: { [str: string]: boolean } = { events: true, fullscreen: true, isEditing: true, + editModeInitiated: true, }; export class PanelModel { @@ -36,6 +37,7 @@ export class PanelModel { fullscreen: boolean; isEditing: boolean; events: Emitter; + editModeInitiated: boolean; constructor(model) { this.events = new Emitter(); @@ -91,6 +93,10 @@ export class PanelModel { this.events.emit('panel-size-changed'); } + initEditMode() { + this.events.emit('panel-init-edit-mode'); + } + destroy() { this.events.removeAllListeners(); } diff --git a/public/app/features/dashboard/specs/AddPanelPanel.jest.tsx b/public/app/features/dashboard/specs/AddPanelPanel.jest.tsx index 872d9296d12..7e952d72d69 100644 --- a/public/app/features/dashboard/specs/AddPanelPanel.jest.tsx +++ b/public/app/features/dashboard/specs/AddPanelPanel.jest.tsx @@ -77,13 +77,8 @@ describe('AddPanelPanel', () => { dashboardMock = { toggleRow: jest.fn() }; - getPanelContainer = jest.fn().mockReturnValue({ - getDashboard: jest.fn().mockReturnValue(dashboardMock), - getPanelLoader: jest.fn(), - }); - panel = new PanelModel({ collapsed: false }); - wrapper = shallow(); + wrapper = shallow(); }); it('should fetch all panels sorted with core plugins first', () => { diff --git a/public/app/features/dashboard/view_state_srv.ts b/public/app/features/dashboard/view_state_srv.ts index 1ed2d61df71..3b99c06ad50 100644 --- a/public/app/features/dashboard/view_state_srv.ts +++ b/public/app/features/dashboard/view_state_srv.ts @@ -33,10 +33,6 @@ export class DashboardViewState { self.update(payload); }); - $scope.onAppEvent('panel-initialized', function(evt, payload) { - self.registerPanel(payload.scope); - }); - // this marks changes to location during this digest cycle as not to add history item // don't want url changes like adding orgId to add browser history $location.replace(); @@ -124,102 +120,62 @@ export class DashboardViewState { } syncState() { - if (this.panelScopes.length === 0) { - return; - } - if (this.dashboard.meta.fullscreen) { - var panelScope = this.getPanelScope(this.state.panelId); - if (!panelScope) { + var panel = this.dashboard.getPanelById(this.state.panelId); + + if (!panel) { return; } if (this.fullscreenPanel) { // if already fullscreen - if (this.fullscreenPanel === panelScope && this.editStateChanged === false) { + if (this.fullscreenPanel === panel && this.editStateChanged === false) { return; } else { this.leaveFullscreen(false); } } - if (!panelScope.ctrl.editModeInitiated) { - panelScope.ctrl.initEditMode(); - } - - if (!panelScope.ctrl.fullscreen) { - this.enterFullscreen(panelScope); + if (!panel.fullscreen) { + this.enterFullscreen(panel); } } else if (this.fullscreenPanel) { this.leaveFullscreen(true); } } - getPanelScope(id) { - return _.find(this.panelScopes, function(panelScope) { - return panelScope.ctrl.panel.id === id; - }); - } - leaveFullscreen(render) { - var self = this; - var ctrl = self.fullscreenPanel.ctrl; + var panel = this.fullscreenPanel; - ctrl.editMode = false; - ctrl.fullscreen = false; - - this.dashboard.setViewMode(ctrl.panel, false, false); - this.$scope.appEvent('panel-fullscreen-exit', { panelId: ctrl.panel.id }); + this.dashboard.setViewMode(panel, false, false); this.$scope.appEvent('dash-scroll', { restore: true }); if (!render) { return false; } - this.$timeout(function() { - if (self.oldTimeRange !== ctrl.range) { - self.$rootScope.$broadcast('refresh'); + this.$timeout(() => { + if (this.oldTimeRange !== this.dashboard.time) { + this.$rootScope.$broadcast('refresh'); } else { - self.$rootScope.$broadcast('render'); + this.$rootScope.$broadcast('render'); } - delete self.fullscreenPanel; + delete this.fullscreenPanel; }); + return true; } - enterFullscreen(panelScope) { - var ctrl = panelScope.ctrl; + enterFullscreen(panel) { + const isEditing = this.state.edit && this.dashboard.meta.canEdit; - ctrl.editMode = this.state.edit && this.dashboard.meta.canEdit; - ctrl.fullscreen = true; - - this.oldTimeRange = ctrl.range; - this.fullscreenPanel = panelScope; + this.oldTimeRange = this.dashboard.time; + this.fullscreenPanel = panel; // Firefox doesn't return scrollTop position properly if 'dash-scroll' is emitted after setViewMode() this.$scope.appEvent('dash-scroll', { animate: false, pos: 0 }); - this.dashboard.setViewMode(ctrl.panel, true, ctrl.editMode); - this.$scope.appEvent('panel-fullscreen-enter', { panelId: ctrl.panel.id }); - } - - registerPanel(panelScope) { - var self = this; - self.panelScopes.push(panelScope); - - if (!self.dashboard.meta.soloMode) { - if (self.state.panelId === panelScope.ctrl.panel.id) { - if (self.state.edit) { - panelScope.ctrl.editPanel(); - } else { - panelScope.ctrl.viewPanel(); - } - } - } - - var unbind = panelScope.$on('$destroy', function() { - self.panelScopes = _.without(self.panelScopes, panelScope); - unbind(); - }); + console.log('viewstatesrv.setViewMode'); + this.dashboard.setViewMode(panel, true, isEditing); } } From aa5c9f199aa382af6301ad3c2da303fc26926f6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 19 Jun 2018 14:51:57 +0200 Subject: [PATCH 06/54] react panel minor progress --- .../dashboard/dashgrid/DashboardGrid.tsx | 3 +- .../dashboard/dashgrid/DashboardPanel.tsx | 166 ++++-------------- .../dashboard/dashgrid/PanelChrome.tsx | 66 +++++++ .../dashboard/dashgrid/PanelEditor.tsx | 35 ++++ .../dashboard/dashgrid/PanelHeader.tsx | 64 +++++++ .../dashboard/specs/AddPanelPanel.jest.tsx | 2 +- 6 files changed, 200 insertions(+), 136 deletions(-) create mode 100644 public/app/features/dashboard/dashgrid/PanelChrome.tsx create mode 100644 public/app/features/dashboard/dashgrid/PanelEditor.tsx create mode 100644 public/app/features/dashboard/dashgrid/PanelHeader.tsx diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx index 9f2e449b49e..322b8d972d3 100644 --- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx @@ -175,11 +175,10 @@ export class DashboardGrid extends React.Component { const panelElements = []; for (let panel of this.dashboard.panels) { - console.log('panel.fullscreen', panel.fullscreen); const panelClasses = classNames({ panel: true, 'panel--fullscreen': panel.fullscreen }); panelElements.push(
- +
); } diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx index fe97ac6039a..b18d78c5cc4 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -1,22 +1,18 @@ import React from 'react'; -import $ from 'jquery'; import config from 'app/core/config'; -import classNames from 'classnames'; import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; import { AttachedPanel } from './PanelLoader'; import { DashboardRow } from './DashboardRow'; +import { PanelContainer } from './PanelContainer'; import { AddPanelPanel } from './AddPanelPanel'; import { importPluginModule } from 'app/features/plugins/plugin_loader'; -import { store } from 'app/stores/store'; -import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN } from 'app/core/constants'; - -const TITLE_HEIGHT = 27; -const PANEL_BORDER = 2; +import { PanelChrome } from './PanelChrome'; export interface DashboardPanelProps { panel: PanelModel; dashboard: DashboardModel; + panelContainer: PanelContainer; } export class DashboardPanel extends React.Component { @@ -56,140 +52,44 @@ export class DashboardPanel extends React.Component { return ; } + componentDidUpdate() { + // skip loading angular component if we have no element + // or we have already loaded it + if (!this.element || this.attachedPanel) { + return; + } + + const loader = this.props.panelContainer.getPanelLoader(); + this.attachedPanel = loader.load(this.element, this.props.panel, this.props.dashboard); + } + + componentWillUnmount() { + if (this.attachedPanel) { + this.attachedPanel.destroy(); + } + } + render() { if (this.isSpecial()) { return this.specialPanels[this.props.panel.type](); } - let PanelComponent = null; - - if (this.pluginExports && this.pluginExports.PanelComponent) { - PanelComponent = this.pluginExports.PanelComponent; + if (!this.pluginExports) { + console.log('render null'); + return null; } - let panelContentStyle = { - height: this.getPanelHeight(), - }; - - return ( -
-
- -
- {PanelComponent && } -
-
-
- {this.props.panel.isEditing && } -
-
- ); - } - - getPanelHeight() { - const panel = this.props.panel; - let height = 0; - - if (panel.fullscreen) { - var docHeight = $(window).height(); - var editHeight = Math.floor(docHeight * 0.4); - var fullscreenHeight = Math.floor(docHeight * 0.8); - height = panel.isEditing ? editHeight : fullscreenHeight; - } else { - height = panel.gridPos.h * GRID_CELL_HEIGHT + (panel.gridPos.h - 1) * GRID_CELL_VMARGIN; + if (this.pluginExports.PanelComponent) { + return ( + + ); } - return height - PANEL_BORDER + TITLE_HEIGHT; - } -} - -interface PanelHeaderProps { - panel: PanelModel; - dashboard: DashboardModel; -} - -export class PanelHeader extends React.Component { - onEditPanel = () => { - store.view.updateQuery({ - panelId: this.props.panel.id, - edit: true, - fullscreen: true, - }); - }; - - render() { - let isFullscreen = false; - let isLoading = false; - let panelHeaderClass = classNames({ 'panel-header': true, 'grid-drag-handle': !isFullscreen }); - - return ( -
- - - - - - {isLoading && ( - - - - )} - -
- - - {this.props.panel.title} - - - - - - 4m - - -
-
- ); - } -} - -interface PanelEditorProps { - panel: PanelModel; - dashboard: DashboardModel; -} - -export class PanelEditor extends React.Component { - render() { - return ( -
-
-

{this.props.panel.type}

- - - - -
- -
testing
-
- ); + // legacy angular rendering + return
(this.element = element)} className="panel-height-helper" />; } } diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx new file mode 100644 index 00000000000..25e0875fbef --- /dev/null +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import $ from 'jquery'; +import { PanelModel } from '../panel_model'; +import { DashboardModel } from '../dashboard_model'; +import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN } from 'app/core/constants'; +import { PanelHeader } from './PanelHeader'; +import { PanelEditor } from './PanelEditor'; + +const TITLE_HEIGHT = 27; +const PANEL_BORDER = 2; + +export interface PanelChromeProps { + panel: PanelModel; + dashboard: DashboardModel; + component: any; +} + +export class PanelChrome extends React.Component { + constructor(props) { + super(props); + + this.props.panel.events.on('panel-size-changed', this.triggerForceUpdate.bind(this)); + } + + triggerForceUpdate() { + this.forceUpdate(); + } + + render() { + let panelContentStyle = { + height: this.getPanelHeight(), + }; + + let PanelComponent = this.props.component; + + return ( +
+
+ +
+ {} +
+
+
+ {this.props.panel.isEditing && } +
+
+ ); + } + + getPanelHeight() { + const panel = this.props.panel; + let height = 0; + + if (panel.fullscreen) { + var docHeight = $(window).height(); + var editHeight = Math.floor(docHeight * 0.4); + var fullscreenHeight = Math.floor(docHeight * 0.8); + height = panel.isEditing ? editHeight : fullscreenHeight; + } else { + height = panel.gridPos.h * GRID_CELL_HEIGHT + (panel.gridPos.h - 1) * GRID_CELL_VMARGIN; + } + + return height - PANEL_BORDER + TITLE_HEIGHT; + } +} diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx new file mode 100644 index 00000000000..ec0d63f9182 --- /dev/null +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { PanelModel } from '../panel_model'; +import { DashboardModel } from '../dashboard_model'; + +interface PanelEditorProps { + panel: PanelModel; + dashboard: DashboardModel; +} + +export class PanelEditor extends React.Component { + render() { + return ( +
+
+

{this.props.panel.type}

+ + + + +
+ +
testing
+
+ ); + } +} diff --git a/public/app/features/dashboard/dashgrid/PanelHeader.tsx b/public/app/features/dashboard/dashgrid/PanelHeader.tsx new file mode 100644 index 00000000000..cb806fefa43 --- /dev/null +++ b/public/app/features/dashboard/dashgrid/PanelHeader.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import classNames from 'classnames'; +import { PanelModel } from '../panel_model'; +import { DashboardModel } from '../dashboard_model'; +import { store } from 'app/stores/store'; + +interface PanelHeaderProps { + panel: PanelModel; + dashboard: DashboardModel; +} + +export class PanelHeader extends React.Component { + onEditPanel = () => { + store.view.updateQuery({ + panelId: this.props.panel.id, + edit: true, + fullscreen: true, + }); + }; + + render() { + let isFullscreen = false; + let isLoading = false; + let panelHeaderClass = classNames({ 'panel-header': true, 'grid-drag-handle': !isFullscreen }); + + return ( +
+ + + + + + {isLoading && ( + + + + )} + +
+ + + {this.props.panel.title} + + + + + + 4m + + +
+
+ ); + } +} diff --git a/public/app/features/dashboard/specs/AddPanelPanel.jest.tsx b/public/app/features/dashboard/specs/AddPanelPanel.jest.tsx index 7e952d72d69..9bf99b5720c 100644 --- a/public/app/features/dashboard/specs/AddPanelPanel.jest.tsx +++ b/public/app/features/dashboard/specs/AddPanelPanel.jest.tsx @@ -14,7 +14,7 @@ jest.mock('app/core/store', () => ({ })); describe('AddPanelPanel', () => { - let wrapper, dashboardMock, getPanelContainer, panel; + let wrapper, dashboardMock, panel; beforeEach(() => { config.panels = [ From db52ea66bd61e7a51699550816cb4ea61dea3f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 19 Jun 2018 16:57:55 +0200 Subject: [PATCH 07/54] react panels minor progress --- public/app/core/directives/dash_class.ts | 4 +--- .../dashboard/dashgrid/DashboardGrid.tsx | 10 ++++++---- .../dashboard/dashgrid/DashboardPanel.tsx | 1 - .../dashboard/dashgrid/PanelChrome.tsx | 3 ++- .../dashboard/dashgrid/PanelEditor.tsx | 2 -- public/app/features/dashboard/panel_model.ts | 3 --- .../app/features/dashboard/view_state_srv.ts | 1 - public/app/features/panel/panel_ctrl.ts | 19 ++++++++++--------- public/app/features/panel/panel_directive.ts | 4 ++-- public/app/plugins/panel/graph/graph.ts | 2 ++ public/app/plugins/panel/text2/module.tsx | 2 +- public/sass/components/_dashboard_grid.scss | 1 - 12 files changed, 24 insertions(+), 28 deletions(-) diff --git a/public/app/core/directives/dash_class.ts b/public/app/core/directives/dash_class.ts index 1dab57da0d4..c164acf7bfc 100644 --- a/public/app/core/directives/dash_class.ts +++ b/public/app/core/directives/dash_class.ts @@ -5,9 +5,7 @@ coreModule.directive('dashClass', function($timeout) { return { link: function($scope, elem) { $scope.ctrl.dashboard.events.on('view-mode-changed', function(panel) { - $timeout(() => { - elem.toggleClass('panel-in-fullscreen', panel.fullscreen === true); - }); + elem.toggleClass('panel-in-fullscreen', panel.fullscreen === true); }); elem.toggleClass('panel-in-fullscreen', $scope.ctrl.dashboard.meta.fullscreen === true); diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx index 322b8d972d3..653ed046e8e 100644 --- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx @@ -85,7 +85,7 @@ export class DashboardGrid extends React.Component { this.dashboard.on('panel-added', this.triggerForceUpdate.bind(this)); this.dashboard.on('panel-removed', this.triggerForceUpdate.bind(this)); this.dashboard.on('repeats-processed', this.triggerForceUpdate.bind(this)); - this.dashboard.on('view-mode-changed', this.triggerForceUpdate.bind(this)); + this.dashboard.on('view-mode-changed', this.onViewModeChanged.bind(this)); this.dashboard.on('row-collapsed', this.triggerForceUpdate.bind(this)); this.dashboard.on('row-expanded', this.triggerForceUpdate.bind(this)); } @@ -142,6 +142,10 @@ export class DashboardGrid extends React.Component { } } + onViewModeChanged(payload) { + this.setState({ animated: payload.fullscreen }); + } + updateGridPos(item, layout) { this.panelMap[item.i].updateGridPos(item); @@ -165,9 +169,7 @@ export class DashboardGrid extends React.Component { componentDidMount() { setTimeout(() => { - this.setState(() => { - return { animated: true }; - }); + this.setState({ animated: true }); }); } diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx index b18d78c5cc4..2aa1b620ea6 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -75,7 +75,6 @@ export class DashboardPanel extends React.Component { } if (!this.pluginExports) { - console.log('render null'); return null; } diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index 25e0875fbef..30263edfb6c 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -23,7 +23,7 @@ export class PanelChrome extends React.Component { } triggerForceUpdate() { - this.forceUpdate(); + // this.forceUpdate(); } render() { @@ -32,6 +32,7 @@ export class PanelChrome extends React.Component { }; let PanelComponent = this.props.component; + console.log('PanelChrome render'); return (
diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index ec0d63f9182..646a0b65ecd 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -12,8 +12,6 @@ export class PanelEditor extends React.Component { return (
-

{this.props.panel.type}

-
  • Queries diff --git a/public/app/features/dashboard/panel_model.ts b/public/app/features/dashboard/panel_model.ts index 2cf46bfdf53..0bb2d2755d7 100644 --- a/public/app/features/dashboard/panel_model.ts +++ b/public/app/features/dashboard/panel_model.ts @@ -13,7 +13,6 @@ const notPersistedProperties: { [str: string]: boolean } = { events: true, fullscreen: true, isEditing: true, - editModeInitiated: true, }; export class PanelModel { @@ -37,7 +36,6 @@ export class PanelModel { fullscreen: boolean; isEditing: boolean; events: Emitter; - editModeInitiated: boolean; constructor(model) { this.events = new Emitter(); @@ -84,7 +82,6 @@ export class PanelModel { this.gridPos.h = newPos.h; if (sizeChanged) { - console.log('PanelModel sizeChanged event and render events fired'); this.events.emit('panel-size-changed'); } } diff --git a/public/app/features/dashboard/view_state_srv.ts b/public/app/features/dashboard/view_state_srv.ts index 3b99c06ad50..73ec8fc0638 100644 --- a/public/app/features/dashboard/view_state_srv.ts +++ b/public/app/features/dashboard/view_state_srv.ts @@ -174,7 +174,6 @@ export class DashboardViewState { // Firefox doesn't return scrollTop position properly if 'dash-scroll' is emitted after setViewMode() this.$scope.appEvent('dash-scroll', { animate: false, pos: 0 }); - console.log('viewstatesrv.setViewMode'); this.dashboard.setViewMode(panel, true, isEditing); } } diff --git a/public/app/features/panel/panel_ctrl.ts b/public/app/features/panel/panel_ctrl.ts index 6402227164f..8f79a789e76 100644 --- a/public/app/features/panel/panel_ctrl.ts +++ b/public/app/features/panel/panel_ctrl.ts @@ -24,10 +24,8 @@ export class PanelCtrl { $injector: any; $location: any; $timeout: any; - fullscreen: boolean; inspector: any; editModeInitiated: boolean; - editMode: any; height: any; containerHeight: any; events: Emitter; @@ -130,6 +128,7 @@ export class PanelCtrl { return { templateUrl: directiveFn }; }; } + if (index) { this.editorTabs.splice(index, 0, editorTab); } else { @@ -190,7 +189,7 @@ export class PanelCtrl { getExtendedMenu() { let menu = []; - if (!this.fullscreen && this.dashboard.meta.canEdit) { + if (!this.panel.fullscreen && this.dashboard.meta.canEdit) { menu.push({ text: 'Duplicate', click: 'ctrl.duplicate()', @@ -220,15 +219,15 @@ export class PanelCtrl { } otherPanelInFullscreenMode() { - return this.dashboard.meta.fullscreen && !this.fullscreen; + return this.dashboard.meta.fullscreen && !this.panel.fullscreen; } calculatePanelHeight() { - if (this.fullscreen) { + if (this.panel.fullscreen) { var docHeight = $(window).height(); var editHeight = Math.floor(docHeight * 0.4); var fullscreenHeight = Math.floor(docHeight * 0.8); - this.containerHeight = this.editMode ? editHeight : fullscreenHeight; + this.containerHeight = this.panel.isEditing ? editHeight : fullscreenHeight; } else { this.containerHeight = this.panel.gridPos.h * GRID_CELL_HEIGHT + (this.panel.gridPos.h - 1) * GRID_CELL_VMARGIN; } @@ -237,6 +236,11 @@ export class PanelCtrl { this.containerHeight = $(window).height(); } + // hacky solution + if (this.panel.isEditing && !this.editModeInitiated) { + this.initEditMode(); + } + this.height = this.containerHeight - (PANEL_BORDER + TITLE_HEIGHT); } @@ -247,9 +251,6 @@ export class PanelCtrl { duplicate() { this.dashboard.duplicatePanel(this.panel); - this.$timeout(() => { - this.$scope.$root.$broadcast('render'); - }); } removePanel() { diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index e549ca262d3..685e527c944 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/public/app/features/panel/panel_directive.ts @@ -7,7 +7,7 @@ var module = angular.module('grafana.directives'); var panelTemplate = `
    -
    +
    @@ -25,7 +25,7 @@ var panelTemplate = `
    -
    +

    diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 9e4fb42952e..33b8c8535fa 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -197,6 +197,8 @@ function graphDirective(timeSrv, popoverSrv, contextSrv) { // Function for rendering panel function render_panel() { panelWidth = elem.width(); + console.log('panelWidth', panelWidth); + if (shouldAbortRender()) { return; } diff --git a/public/app/plugins/panel/text2/module.tsx b/public/app/plugins/panel/text2/module.tsx index 7f5e363891c..8987b738f0b 100644 --- a/public/app/plugins/panel/text2/module.tsx +++ b/public/app/plugins/panel/text2/module.tsx @@ -6,7 +6,7 @@ export class ReactTestPanel extends React.Component { } render() { - return

    Panel content

    ; + return

    I am a react panel, haha!

    ; } } diff --git a/public/sass/components/_dashboard_grid.scss b/public/sass/components/_dashboard_grid.scss index f1908ca8786..26326013dab 100644 --- a/public/sass/components/_dashboard_grid.scss +++ b/public/sass/components/_dashboard_grid.scss @@ -20,7 +20,6 @@ } // Disable grid interaction indicators in fullscreen panels - .panel-header:hover { background-color: inherit; } From 13bc9f6fb2932559074fbabcb755879ed8c03064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 19 Jun 2018 17:30:10 +0200 Subject: [PATCH 08/54] updated --- public/app/features/dashboard/dashgrid/PanelChrome.tsx | 2 +- public/app/plugins/panel/graph/graph.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index 30263edfb6c..a0a1dff7c16 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -23,7 +23,7 @@ export class PanelChrome extends React.Component { } triggerForceUpdate() { - // this.forceUpdate(); + this.forceUpdate(); } render() { diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 33b8c8535fa..09fea9d6a37 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -197,7 +197,6 @@ function graphDirective(timeSrv, popoverSrv, contextSrv) { // Function for rendering panel function render_panel() { panelWidth = elem.width(); - console.log('panelWidth', panelWidth); if (shouldAbortRender()) { return; From 230606146d6db5afaad28ab591188c2cd1a248ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 19 Jun 2018 21:25:57 +0200 Subject: [PATCH 09/54] wip: react panel minor progrss --- public/app/core/components/grafana_app.ts | 5 +++ public/app/core/services/angular_loader.ts | 42 +++++++++++++++++++ public/app/features/dashboard/all.ts | 1 - .../app/features/dashboard/dashboard_ctrl.ts | 10 +---- .../dashboard/dashgrid/DashboardGrid.tsx | 34 +++++++-------- .../dashgrid/DashboardGridDirective.ts | 4 +- .../dashboard/dashgrid/DashboardPanel.tsx | 18 ++++---- .../dashboard/dashgrid/PanelChrome.tsx | 7 +--- .../dashboard/dashgrid/PanelContainer.ts | 7 ---- .../dashboard/dashgrid/PanelEditor.tsx | 36 +++++++++++++++- .../dashboard/dashgrid/PanelLoader.ts | 31 -------------- .../app/features/plugins/plugin_component.ts | 1 + public/app/partials/dashboard.html | 3 +- public/sass/components/_tabbed_view.scss | 9 ++++ 14 files changed, 121 insertions(+), 87 deletions(-) create mode 100644 public/app/core/services/angular_loader.ts delete mode 100644 public/app/features/dashboard/dashgrid/PanelContainer.ts delete mode 100644 public/app/features/dashboard/dashgrid/PanelLoader.ts diff --git a/public/app/core/components/grafana_app.ts b/public/app/core/components/grafana_app.ts index fd2e32db3a7..a888e2973e9 100644 --- a/public/app/core/components/grafana_app.ts +++ b/public/app/core/components/grafana_app.ts @@ -10,6 +10,7 @@ import { createStore } from 'app/stores/store'; import colors from 'app/core/utils/colors'; import { BackendSrv } from 'app/core/services/backend_srv'; import { DatasourceSrv } from 'app/features/plugins/datasource_srv'; +import { AngularLoader, setAngularLoader } from 'app/core/services/angular_loader'; export class GrafanaCtrl { /** @ngInject */ @@ -22,8 +23,12 @@ export class GrafanaCtrl { contextSrv, bridgeSrv, backendSrv: BackendSrv, + angularLoader: AngularLoader, datasourceSrv: DatasourceSrv ) { + // make angular loader service available to react components + setAngularLoader(angularLoader); + // create store with env services createStore({ backendSrv, datasourceSrv }); $scope.init = function() { diff --git a/public/app/core/services/angular_loader.ts b/public/app/core/services/angular_loader.ts new file mode 100644 index 00000000000..36c49be6240 --- /dev/null +++ b/public/app/core/services/angular_loader.ts @@ -0,0 +1,42 @@ +import angular from 'angular'; +import coreModule from 'app/core/core_module'; +import _ from 'lodash'; + +export interface AngularComponent { + destroy(); +} + +export class AngularLoader { + /** @ngInject */ + constructor(private $compile, private $rootScope) {} + + load(elem, scopeProps, template): AngularComponent { + var scope = this.$rootScope.$new(); + + _.assign(scope, scopeProps); + + const compiledElem = this.$compile(template)(scope); + const rootNode = angular.element(elem); + rootNode.append(compiledElem); + + return { + destroy: () => { + scope.$destroy(); + compiledElem.remove(); + }, + }; + } +} + +coreModule.service('angularLoader', AngularLoader); + +let angularLoaderInstance: AngularLoader; + +export function setAngularLoader(pl: AngularLoader) { + angularLoaderInstance = pl; +} + +// away to access it from react +export function getAngularLoader(): AngularLoader { + return angularLoaderInstance; +} diff --git a/public/app/features/dashboard/all.ts b/public/app/features/dashboard/all.ts index a8f491f3ddd..6898b51d095 100644 --- a/public/app/features/dashboard/all.ts +++ b/public/app/features/dashboard/all.ts @@ -22,7 +22,6 @@ import './export_data/export_data_modal'; import './ad_hoc_filters'; import './repeat_option/repeat_option'; import './dashgrid/DashboardGridDirective'; -import './dashgrid/PanelLoader'; import './dashgrid/RowOptions'; import './folder_picker/folder_picker'; import './move_to_folder_modal/move_to_folder'; diff --git a/public/app/features/dashboard/dashboard_ctrl.ts b/public/app/features/dashboard/dashboard_ctrl.ts index 94d0b18f157..a7d1ff23ea4 100644 --- a/public/app/features/dashboard/dashboard_ctrl.ts +++ b/public/app/features/dashboard/dashboard_ctrl.ts @@ -1,11 +1,10 @@ import config from 'app/core/config'; import coreModule from 'app/core/core_module'; -import { PanelContainer } from './dashgrid/PanelContainer'; import { DashboardModel } from './dashboard_model'; import { PanelModel } from './panel_model'; -export class DashboardCtrl implements PanelContainer { +export class DashboardCtrl { dashboard: DashboardModel; dashboardViewState: any; loadedFallbackDashboard: boolean; @@ -22,8 +21,7 @@ export class DashboardCtrl implements PanelContainer { private dashboardSrv, private unsavedChangesSrv, private dashboardViewStateSrv, - public playlistSrv, - private panelLoader + public playlistSrv ) { // temp hack due to way dashboards are loaded // can't use controllerAs on route yet @@ -119,10 +117,6 @@ export class DashboardCtrl implements PanelContainer { return this.dashboard; } - getPanelLoader() { - return this.panelLoader; - } - timezoneChanged() { this.$rootScope.$broadcast('refresh'); } diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx index 653ed046e8e..9ee6cdbe1f8 100644 --- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx @@ -3,7 +3,6 @@ import ReactGridLayout from 'react-grid-layout'; import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, GRID_COLUMN_COUNT } from 'app/core/constants'; import { DashboardPanel } from './DashboardPanel'; import { DashboardModel } from '../dashboard_model'; -import { PanelContainer } from './PanelContainer'; import { PanelModel } from '../panel_model'; import classNames from 'classnames'; import sizeMe from 'react-sizeme'; @@ -60,18 +59,15 @@ function GridWrapper({ const SizedReactLayoutGrid = sizeMe({ monitorWidth: true })(GridWrapper); export interface DashboardGridProps { - getPanelContainer: () => PanelContainer; + dashboard: DashboardModel; } export class DashboardGrid extends React.Component { gridToPanelMap: any; - panelContainer: PanelContainer; - dashboard: DashboardModel; panelMap: { [id: string]: PanelModel }; constructor(props) { super(props); - this.panelContainer = this.props.getPanelContainer(); this.onLayoutChange = this.onLayoutChange.bind(this); this.onResize = this.onResize.bind(this); this.onResizeStop = this.onResizeStop.bind(this); @@ -81,20 +77,20 @@ export class DashboardGrid extends React.Component { this.state = { animated: false }; // subscribe to dashboard events - this.dashboard = this.panelContainer.getDashboard(); - this.dashboard.on('panel-added', this.triggerForceUpdate.bind(this)); - this.dashboard.on('panel-removed', this.triggerForceUpdate.bind(this)); - this.dashboard.on('repeats-processed', this.triggerForceUpdate.bind(this)); - this.dashboard.on('view-mode-changed', this.onViewModeChanged.bind(this)); - this.dashboard.on('row-collapsed', this.triggerForceUpdate.bind(this)); - this.dashboard.on('row-expanded', this.triggerForceUpdate.bind(this)); + let dashboard = this.props.dashboard; + dashboard.on('panel-added', this.triggerForceUpdate.bind(this)); + dashboard.on('panel-removed', this.triggerForceUpdate.bind(this)); + dashboard.on('repeats-processed', this.triggerForceUpdate.bind(this)); + dashboard.on('view-mode-changed', this.onViewModeChanged.bind(this)); + dashboard.on('row-collapsed', this.triggerForceUpdate.bind(this)); + dashboard.on('row-expanded', this.triggerForceUpdate.bind(this)); } buildLayout() { const layout = []; this.panelMap = {}; - for (let panel of this.dashboard.panels) { + for (let panel of this.props.dashboard.panels) { let stringId = panel.id.toString(); this.panelMap[stringId] = panel; @@ -129,7 +125,7 @@ export class DashboardGrid extends React.Component { this.panelMap[newPos.i].updateGridPos(newPos); } - this.dashboard.sortPanelsByGridPos(); + this.props.dashboard.sortPanelsByGridPos(); } triggerForceUpdate() { @@ -137,7 +133,7 @@ export class DashboardGrid extends React.Component { } onWidthChange() { - for (const panel of this.dashboard.panels) { + for (const panel of this.props.dashboard.panels) { panel.resizeDone(); } } @@ -176,11 +172,11 @@ export class DashboardGrid extends React.Component { renderPanels() { const panelElements = []; - for (let panel of this.dashboard.panels) { + for (let panel of this.props.dashboard.panels) { const panelClasses = classNames({ panel: true, 'panel--fullscreen': panel.fullscreen }); panelElements.push(
    - +
    ); } @@ -193,8 +189,8 @@ export class DashboardGrid extends React.Component { { element: any; - attachedPanel: AttachedPanel; + angularPanel: AngularComponent; pluginInfo: any; pluginExports: any; specialPanels = {}; @@ -55,17 +53,19 @@ export class DashboardPanel extends React.Component { componentDidUpdate() { // skip loading angular component if we have no element // or we have already loaded it - if (!this.element || this.attachedPanel) { + if (!this.element || this.angularPanel) { return; } - const loader = this.props.panelContainer.getPanelLoader(); - this.attachedPanel = loader.load(this.element, this.props.panel, this.props.dashboard); + let loader = getAngularLoader(); + var template = ''; + let scopeProps = { panel: this.props.panel, dashboard: this.props.dashboard }; + this.angularPanel = loader.load(this.element, scopeProps, template); } componentWillUnmount() { - if (this.attachedPanel) { - this.attachedPanel.destroy(); + if (this.angularPanel) { + this.angularPanel.destroy(); } } diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index a0a1dff7c16..bf5f8044a37 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -32,7 +32,6 @@ export class PanelChrome extends React.Component { }; let PanelComponent = this.props.component; - console.log('PanelChrome render'); return (
    @@ -42,9 +41,7 @@ export class PanelChrome extends React.Component { {}

    -
    - {this.props.panel.isEditing && } -
    + {this.props.panel.isEditing && }
    ); } @@ -55,7 +52,7 @@ export class PanelChrome extends React.Component { if (panel.fullscreen) { var docHeight = $(window).height(); - var editHeight = Math.floor(docHeight * 0.4); + var editHeight = Math.floor(docHeight * 0.3); var fullscreenHeight = Math.floor(docHeight * 0.8); height = panel.isEditing ? editHeight : fullscreenHeight; } else { diff --git a/public/app/features/dashboard/dashgrid/PanelContainer.ts b/public/app/features/dashboard/dashgrid/PanelContainer.ts deleted file mode 100644 index 87f3235a176..00000000000 --- a/public/app/features/dashboard/dashgrid/PanelContainer.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { DashboardModel } from '../dashboard_model'; -import { PanelLoader } from './PanelLoader'; - -export interface PanelContainer { - getPanelLoader(): PanelLoader; - getDashboard(): DashboardModel; -} diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index 646a0b65ecd..fc7967d087c 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; +import { getAngularLoader, AngularComponent } from 'app/core/services/angular_loader'; interface PanelEditorProps { panel: PanelModel; @@ -8,9 +9,38 @@ interface PanelEditorProps { } export class PanelEditor extends React.Component { + queryElement: any; + queryComp: AngularComponent; + + constructor(props) { + super(props); + } + + componentDidMount() { + if (!this.queryElement) { + return; + } + + let loader = getAngularLoader(); + var template = ''; + let scopeProps = { + ctrl: { + panel: this.props.panel, + dashboard: this.props.dashboard, + panelCtrl: { + panel: this.props.panel, + dashboard: this.props.dashboard, + }, + }, + target: {}, + }; + + this.queryComp = loader.load(this.queryElement, scopeProps, template); + } + render() { return ( -
    +
    • @@ -26,7 +56,9 @@ export class PanelEditor extends React.Component {
    -
    testing
    +
    +
    (this.queryElement = element)} className="panel-height-helper" /> +
    ); } diff --git a/public/app/features/dashboard/dashgrid/PanelLoader.ts b/public/app/features/dashboard/dashgrid/PanelLoader.ts deleted file mode 100644 index beda30bdff7..00000000000 --- a/public/app/features/dashboard/dashgrid/PanelLoader.ts +++ /dev/null @@ -1,31 +0,0 @@ -import angular from 'angular'; -import coreModule from 'app/core/core_module'; - -export interface AttachedPanel { - destroy(); -} - -export class PanelLoader { - /** @ngInject */ - constructor(private $compile, private $rootScope) {} - - load(elem, panel, dashboard): AttachedPanel { - var template = ''; - var panelScope = this.$rootScope.$new(); - panelScope.panel = panel; - panelScope.dashboard = dashboard; - - const compiledElem = this.$compile(template)(panelScope); - const rootNode = angular.element(elem); - rootNode.append(compiledElem); - - return { - destroy: () => { - panelScope.$destroy(); - compiledElem.remove(); - }, - }; - } -} - -coreModule.service('panelLoader', PanelLoader); diff --git a/public/app/features/plugins/plugin_component.ts b/public/app/features/plugins/plugin_component.ts index 1936e57f558..2ca3dd6bd0b 100644 --- a/public/app/features/plugins/plugin_component.ts +++ b/public/app/features/plugins/plugin_component.ts @@ -110,6 +110,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $ let datasource = scope.target.datasource || scope.ctrl.panel.datasource; return datasourceSrv.get(datasource).then(ds => { scope.datasource = ds; + console.log('scope', scope); return importPluginModule(ds.meta.module).then(dsModule => { return { diff --git a/public/app/partials/dashboard.html b/public/app/partials/dashboard.html index 9506587c515..9e7d4fa1c6c 100644 --- a/public/app/partials/dashboard.html +++ b/public/app/partials/dashboard.html @@ -11,8 +11,7 @@ - - +
    diff --git a/public/sass/components/_tabbed_view.scss b/public/sass/components/_tabbed_view.scss index bf95d453504..a5d38d292a1 100644 --- a/public/sass/components/_tabbed_view.scss +++ b/public/sass/components/_tabbed_view.scss @@ -10,6 +10,15 @@ background: none; } } + + &.tabbed-view--panel-edit-new { + padding: 10px 0 0 0; + + .tabbed-view-header { + padding: 0px; + background: none; + } + } } .tabbed-view-header { From 1099daec38a8ed360e97ee7d018b90a5984cfef1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 20 Jun 2018 12:05:03 +0200 Subject: [PATCH 10/54] wip: react panels, query editor loading from react PanelEditor view --- public/app/features/dashboard/panel_model.ts | 5 +++++ public/app/features/panel/metrics_panel_ctrl.ts | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/public/app/features/dashboard/panel_model.ts b/public/app/features/dashboard/panel_model.ts index 0bb2d2755d7..2de409eda57 100644 --- a/public/app/features/dashboard/panel_model.ts +++ b/public/app/features/dashboard/panel_model.ts @@ -31,6 +31,7 @@ export class PanelModel { collapsed?: boolean; panels?: any; soloMode?: boolean; + targets: any[]; // non persisted fullscreen: boolean; @@ -48,6 +49,10 @@ export class PanelModel { if (!this.gridPos) { this.gridPos = { x: 0, y: 0, h: 3, w: 6 }; } + + if (!this.targets) { + this.targets = [{}]; + } } getSaveModel() { diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts index 75c0de3bc6e..ba97ce79c1f 100644 --- a/public/app/features/panel/metrics_panel_ctrl.ts +++ b/public/app/features/panel/metrics_panel_ctrl.ts @@ -45,10 +45,6 @@ class MetricsPanelCtrl extends PanelCtrl { this.scope = $scope; this.panel.datasource = this.panel.datasource || null; - 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)); this.events.on('panel-teardown', this.onPanelTearDown.bind(this)); From cec70c1ed8e564737ace0e87ff5423cf1ca6f8d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 26 Jun 2018 16:32:01 +0200 Subject: [PATCH 11/54] feat: panels v2, metrics-tab loading --- .../app/features/dashboard/dashgrid/PanelEditor.tsx | 3 +-- public/app/features/dashboard/panel_model.ts | 12 +++++------- public/app/features/panel/metrics_tab.ts | 3 +++ yarn.lock | 12 ++++++------ 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index fc7967d087c..fa34dde1ab8 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -22,7 +22,7 @@ export class PanelEditor extends React.Component { } let loader = getAngularLoader(); - var template = ''; + var template = ''; let scopeProps = { ctrl: { panel: this.props.panel, @@ -32,7 +32,6 @@ export class PanelEditor extends React.Component { dashboard: this.props.dashboard, }, }, - target: {}, }; this.queryComp = loader.load(this.queryElement, scopeProps, template); diff --git a/public/app/features/dashboard/panel_model.ts b/public/app/features/dashboard/panel_model.ts index 2de409eda57..6ee49886a06 100644 --- a/public/app/features/dashboard/panel_model.ts +++ b/public/app/features/dashboard/panel_model.ts @@ -32,6 +32,7 @@ export class PanelModel { panels?: any; soloMode?: boolean; targets: any[]; + datasource: string; // non persisted fullscreen: boolean; @@ -46,13 +47,10 @@ export class PanelModel { this[property] = model[property]; } - if (!this.gridPos) { - this.gridPos = { x: 0, y: 0, h: 3, w: 6 }; - } - - if (!this.targets) { - this.targets = [{}]; - } + // defaults + this.gridPos = this.gridPos || { x: 0, y: 0, h: 3, w: 6 }; + this.datasource = this.datasource || null; + this.targets = this.targets || [{}]; } getSaveModel() { diff --git a/public/app/features/panel/metrics_tab.ts b/public/app/features/panel/metrics_tab.ts index 4da40f214a1..b8b313ae198 100644 --- a/public/app/features/panel/metrics_tab.ts +++ b/public/app/features/panel/metrics_tab.ts @@ -1,5 +1,6 @@ import { DashboardModel } from '../dashboard/dashboard_model'; import Remarkable from 'remarkable'; +import coreModule from 'app/core/core_module'; export class MetricsTabCtrl { dsName: string; @@ -120,3 +121,5 @@ export function metricsTabDirective() { controller: MetricsTabCtrl, }; } + +coreModule.directive('metricsTab', metricsTabDirective); diff --git a/yarn.lock b/yarn.lock index 6cc48a7c79d..6772d7c14a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1114,7 +1114,7 @@ babel-plugin-istanbul@^4.1.5, babel-plugin-istanbul@^4.1.6: istanbul-lib-instrument "^1.10.1" test-exclude "^4.2.1" -babel-plugin-jest-hoist@^22.4.4: +babel-plugin-jest-hoist@^22.4.3, babel-plugin-jest-hoist@^22.4.4: version "22.4.4" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.4.4.tgz#b9851906eab34c7bf6f8c895a2b08bea1a844c0b" @@ -6498,7 +6498,7 @@ jest-docblock@^22.4.0, jest-docblock@^22.4.3: dependencies: detect-newline "^2.1.0" -jest-environment-jsdom@^22.4.1: +jest-environment-jsdom@^22.4.1, jest-environment-jsdom@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-22.4.3.tgz#d67daa4155e33516aecdd35afd82d4abf0fa8a1e" dependencies: @@ -6506,7 +6506,7 @@ jest-environment-jsdom@^22.4.1: jest-util "^22.4.3" jsdom "^11.5.1" -jest-environment-node@^22.4.1: +jest-environment-node@^22.4.1, jest-environment-node@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-22.4.3.tgz#54c4eaa374c83dd52a9da8759be14ebe1d0b9129" dependencies: @@ -6533,7 +6533,7 @@ jest-haste-map@^22.4.2: micromatch "^2.3.11" sane "^2.0.0" -jest-jasmine2@^22.4.4: +jest-jasmine2@^22.4.3, jest-jasmine2@^22.4.4: version "22.4.4" resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-22.4.4.tgz#c55f92c961a141f693f869f5f081a79a10d24e23" dependencies: @@ -6587,7 +6587,7 @@ jest-resolve-dependencies@^22.1.0: dependencies: jest-regex-util "^22.4.3" -jest-resolve@^22.4.2: +jest-resolve@^22.4.2, jest-resolve@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-22.4.3.tgz#0ce9d438c8438229aa9b916968ec6b05c1abb4ea" dependencies: @@ -6671,7 +6671,7 @@ jest-validate@^21.1.0: leven "^2.1.0" pretty-format "^21.2.1" -jest-validate@^22.4.4: +jest-validate@^22.4.3, jest-validate@^22.4.4: version "22.4.4" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-22.4.4.tgz#1dd0b616ef46c995de61810d85f57119dbbcec4d" dependencies: From 2f5bcd37ec685e3a143c35d655ca39c83d66447b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 26 Jun 2018 16:45:42 +0200 Subject: [PATCH 12/54] react panels wip --- public/app/features/dashboard/dashgrid/PanelEditor.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index fa34dde1ab8..45733559b68 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -11,9 +11,15 @@ interface PanelEditorProps { export class PanelEditor extends React.Component { queryElement: any; queryComp: AngularComponent; + tabs: any[]; constructor(props) { super(props); + + this.tabs = [ + { id: 'queries', text: 'Queries', icon: 'fa fa-database' }, + { id: 'viz', text: 'Visualization', icon: 'fa fa-line-chart' }, + ]; } componentDidMount() { @@ -37,6 +43,8 @@ export class PanelEditor extends React.Component { this.queryComp = loader.load(this.queryElement, scopeProps, template); } + onChangeTab = tabName => {}; + render() { return (
    From 682c792dfb173c64cf8f904b4ca1efef3d4e0eb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 26 Jun 2018 12:07:41 -0700 Subject: [PATCH 13/54] wip: react panels editor mode, tabs working --- .../dashboard/dashgrid/PanelEditor.tsx | 92 +++++++++++-------- .../dashboard/dashgrid/PanelHeader.tsx | 13 ++- .../dashboard/dashgrid/QueriesTab.tsx | 49 ++++++++++ public/app/stores/ViewStore/ViewStore.ts | 6 +- 4 files changed, 115 insertions(+), 45 deletions(-) create mode 100644 public/app/features/dashboard/dashgrid/QueriesTab.tsx diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index 45733559b68..c1350729ddd 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -1,61 +1,54 @@ import React from 'react'; import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; -import { getAngularLoader, AngularComponent } from 'app/core/services/angular_loader'; +import { store } from 'app/stores/store'; +import { observer } from 'mobx-react'; +import { QueriesTab } from './QueriesTab'; +import classNames from 'classnames'; interface PanelEditorProps { panel: PanelModel; dashboard: DashboardModel; } -export class PanelEditor extends React.Component { - queryElement: any; - queryComp: AngularComponent; - tabs: any[]; +interface PanelEditorTab { + id: string; + text: string; + icon: string; +} +@observer +export class PanelEditor extends React.Component { constructor(props) { super(props); + } - this.tabs = [ + renderQueriesTab() { + return ; + } + + renderVizTab() { + return

    Visualizations

    ; + } + + onChangeTab = (tab: PanelEditorTab) => { + store.view.updateQuery({ tab: tab.id }, false); + }; + + render() { + const activeTab: string = store.view.query.get('tab') || 'queries'; + const tabs: PanelEditorTab[] = [ { id: 'queries', text: 'Queries', icon: 'fa fa-database' }, { id: 'viz', text: 'Visualization', icon: 'fa fa-line-chart' }, ]; - } - componentDidMount() { - if (!this.queryElement) { - return; - } - - let loader = getAngularLoader(); - var template = ''; - let scopeProps = { - ctrl: { - panel: this.props.panel, - dashboard: this.props.dashboard, - panelCtrl: { - panel: this.props.panel, - dashboard: this.props.dashboard, - }, - }, - }; - - this.queryComp = loader.load(this.queryElement, scopeProps, template); - } - - onChangeTab = tabName => {}; - - render() { return (
    -
    (this.queryElement = element)} className="panel-height-helper" /> + {activeTab === 'queries' && this.renderQueriesTab()} + {activeTab === 'viz' && this.renderVizTab()}
    ); } } + +interface TabItemParams { + tab: PanelEditorTab; + activeTab: string; + onClick: (tab: PanelEditorTab) => void; +} + +function TabItem({ tab, activeTab, onClick }: TabItemParams) { + const tabClasses = classNames({ + 'gf-tabs-link': true, + active: activeTab === tab.id, + }); + + return ( +
  • + onClick(tab)}> + + {tab.text} + +
  • + ); +} diff --git a/public/app/features/dashboard/dashgrid/PanelHeader.tsx b/public/app/features/dashboard/dashgrid/PanelHeader.tsx index cb806fefa43..97d41e15a0c 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader.tsx @@ -11,11 +11,14 @@ interface PanelHeaderProps { export class PanelHeader extends React.Component { onEditPanel = () => { - store.view.updateQuery({ - panelId: this.props.panel.id, - edit: true, - fullscreen: true, - }); + store.view.updateQuery( + { + panelId: this.props.panel.id, + edit: true, + fullscreen: true, + }, + false + ); }; render() { diff --git a/public/app/features/dashboard/dashgrid/QueriesTab.tsx b/public/app/features/dashboard/dashgrid/QueriesTab.tsx new file mode 100644 index 00000000000..4cfc65c983b --- /dev/null +++ b/public/app/features/dashboard/dashgrid/QueriesTab.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { PanelModel } from '../panel_model'; +import { DashboardModel } from '../dashboard_model'; +import { getAngularLoader, AngularComponent } from 'app/core/services/angular_loader'; + +interface Props { + panel: PanelModel; + dashboard: DashboardModel; +} + +export class QueriesTab extends React.Component { + element: any; + component: AngularComponent; + + constructor(props) { + super(props); + } + + componentDidMount() { + if (!this.element) { + return; + } + + let loader = getAngularLoader(); + var template = ''; + let scopeProps = { + ctrl: { + panel: this.props.panel, + dashboard: this.props.dashboard, + panelCtrl: { + panel: this.props.panel, + dashboard: this.props.dashboard, + }, + }, + }; + + this.component = loader.load(this.element, scopeProps, template); + } + + componentWillUnmount() { + if (this.component) { + this.component.destroy(); + } + } + + render() { + return
    (this.element = element)} className="panel-height-helper" />; + } +} diff --git a/public/app/stores/ViewStore/ViewStore.ts b/public/app/stores/ViewStore/ViewStore.ts index ba966a194d8..83cb01d4bd4 100644 --- a/public/app/stores/ViewStore/ViewStore.ts +++ b/public/app/stores/ViewStore/ViewStore.ts @@ -23,8 +23,10 @@ export const ViewStore = types })) .actions(self => { // querystring only - function updateQuery(query: any) { - self.query.clear(); + function updateQuery(query: any, clear = true) { + if (clear) { + self.query.clear(); + } for (let key of Object.keys(query)) { if (query[key]) { self.query.set(key, query[key]); From 70c808130f2b95e4a49989aaf266a4cd42fa9676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 28 Jun 2018 04:31:55 -0700 Subject: [PATCH 14/54] react panels wip --- .../dashboard/dashgrid/PanelEditor.tsx | 13 +++++++---- public/app/features/panel/DataPanel.tsx | 23 +++++++++++++++++++ .../app/features/plugins/plugin_component.ts | 1 - 3 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 public/app/features/panel/DataPanel.tsx diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index c1350729ddd..ebcde802b7f 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -19,8 +19,15 @@ interface PanelEditorTab { @observer export class PanelEditor extends React.Component { + tabs: PanelEditorTab[]; + constructor(props) { super(props); + + this.tabs = [ + { id: 'queries', text: 'Queries', icon: 'fa fa-database' }, + { id: 'viz', text: 'Visualization', icon: 'fa fa-line-chart' }, + ]; } renderQueriesTab() { @@ -37,16 +44,12 @@ export class PanelEditor extends React.Component { render() { const activeTab: string = store.view.query.get('tab') || 'queries'; - const tabs: PanelEditorTab[] = [ - { id: 'queries', text: 'Queries', icon: 'fa fa-database' }, - { id: 'viz', text: 'Visualization', icon: 'fa fa-line-chart' }, - ]; return (
      - {tabs.map(tab => { + {this.tabs.map(tab => { return ; })}
    diff --git a/public/app/features/panel/DataPanel.tsx b/public/app/features/panel/DataPanel.tsx new file mode 100644 index 00000000000..5ea490e497d --- /dev/null +++ b/public/app/features/panel/DataPanel.tsx @@ -0,0 +1,23 @@ +import React, { Component, ComponentClass } from 'react'; +import _ from 'lodash'; + +export interface Props { + type: string; + queries: Query[]; +} + +interface State { + isLoading: boolean; + timeSeries: TimeSeriesServerResponse[]; +} + +export interface OriginalProps { + data: TimeSeriesServerResponse[]; + isLoading: boolean; +} + +const DataPanel = (ComposedComponent: ComponentClass) => { + class Wrapper extends Component {} + + return Wrapper; +}; diff --git a/public/app/features/plugins/plugin_component.ts b/public/app/features/plugins/plugin_component.ts index 2ca3dd6bd0b..1936e57f558 100644 --- a/public/app/features/plugins/plugin_component.ts +++ b/public/app/features/plugins/plugin_component.ts @@ -110,7 +110,6 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $ let datasource = scope.target.datasource || scope.ctrl.panel.datasource; return datasourceSrv.get(datasource).then(ds => { scope.datasource = ds; - console.log('scope', scope); return importPluginModule(ds.meta.module).then(dsModule => { return { From ab9e1b35cdf48ccb4d303d4960b7fca9aaea85c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 1 Jul 2018 17:34:42 +0200 Subject: [PATCH 15/54] wip: minor progress on DataPanel --- .../dashboard/dashgrid/PanelChrome.tsx | 4 +- public/app/features/panel/DataPanel.tsx | 78 +++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 public/app/features/panel/DataPanel.tsx diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index bf5f8044a37..3fd9df799af 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -9,13 +9,13 @@ import { PanelEditor } from './PanelEditor'; const TITLE_HEIGHT = 27; const PANEL_BORDER = 2; -export interface PanelChromeProps { +export interface Props { panel: PanelModel; dashboard: DashboardModel; component: any; } -export class PanelChrome extends React.Component { +export class PanelChrome extends React.Component { constructor(props) { super(props); diff --git a/public/app/features/panel/DataPanel.tsx b/public/app/features/panel/DataPanel.tsx new file mode 100644 index 00000000000..46418516a26 --- /dev/null +++ b/public/app/features/panel/DataPanel.tsx @@ -0,0 +1,78 @@ +import React, { Component, ComponentClass } from 'react'; + +export interface OuterProps { + type: string; + queries: any[]; + isVisible: boolean; +} + +export interface AddedProps { + data: any[]; +} + +interface State { + isLoading: boolean; + data: any[]; +} + +const DataPanel = (ComposedComponent: ComponentClass) => { + class Wrapper extends Component { + public static defaultProps = { + isVisible: true, + }; + + constructor(props: OuterProps) { + super(props); + + this.state = { + isLoading: false, + data: [], + }; + } + + public componentDidMount() { + this.issueQueries(); + } + + public issueQueries = () => { + const { queries, isVisible } = this.props; + + if (!isVisible) { + return; + } + + if (!queries.length) { + this.setState({ data: [{ message: 'no queries' }] }); + return; + } + + this.setState({ isLoading: true }); + }; + + public render() { + const { data, isLoading } = this.state; + + if (!data.length) { + return ( +
    +

    No Data

    +
    + ); + } + + if (isLoading) { + return ( +
    +

    Loading

    +
    + ); + } + + return ; + } + } + + return Wrapper; +}; + +export default DataPanel; From c86fc6fb47f9dead091ef0e2b6f048c6e292e716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 5 Jul 2018 13:10:39 -0700 Subject: [PATCH 16/54] react-panels: minor progress on data flow --- .../dashboard/dashgrid/DashboardPanel.tsx | 25 ++++++----- .../dashgrid}/DataPanel.tsx | 28 +++++++----- .../dashboard/dashgrid/PanelChrome.tsx | 44 ++++++++++++------- public/app/features/plugins/plugin_loader.ts | 13 +++++- public/app/plugins/panel/text2/module.tsx | 14 ++++-- 5 files changed, 84 insertions(+), 40 deletions(-) rename public/app/features/{panel => dashboard/dashgrid}/DataPanel.tsx (67%) diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx index c45d3c15bf0..3d2542d63c8 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -5,24 +5,27 @@ import { DashboardModel } from '../dashboard_model'; import { getAngularLoader, AngularComponent } from 'app/core/services/angular_loader'; import { DashboardRow } from './DashboardRow'; import { AddPanelPanel } from './AddPanelPanel'; -import { importPluginModule } from 'app/features/plugins/plugin_loader'; +import { importPluginModule, PluginExports } from 'app/features/plugins/plugin_loader'; import { PanelChrome } from './PanelChrome'; -export interface DashboardPanelProps { +export interface Props { panel: PanelModel; dashboard: DashboardModel; } -export class DashboardPanel extends React.Component { +export interface State { + pluginExports: PluginExports; +} + +export class DashboardPanel extends React.Component { element: any; angularPanel: AngularComponent; pluginInfo: any; - pluginExports: any; specialPanels = {}; constructor(props) { super(props); - this.state = {}; + this.state = { pluginExports: null }; this.specialPanels['row'] = this.renderRow.bind(this); this.specialPanels['add-panel'] = this.renderAddPanel.bind(this); @@ -32,8 +35,7 @@ export class DashboardPanel extends React.Component { // load panel plugin importPluginModule(this.pluginInfo.module).then(pluginExports => { - this.pluginExports = pluginExports; - this.forceUpdate(); + this.setState({ pluginExports: pluginExports }); }); } } @@ -70,18 +72,21 @@ export class DashboardPanel extends React.Component { } render() { + const { pluginExports } = this.state; + if (this.isSpecial()) { return this.specialPanels[this.props.panel.type](); } - if (!this.pluginExports) { + if (!pluginExports) { return null; } - if (this.pluginExports.PanelComponent) { + if (pluginExports.PanelComponent) { return ( diff --git a/public/app/features/panel/DataPanel.tsx b/public/app/features/dashboard/dashgrid/DataPanel.tsx similarity index 67% rename from public/app/features/panel/DataPanel.tsx rename to public/app/features/dashboard/dashgrid/DataPanel.tsx index a1669f6603d..251bd243b90 100644 --- a/public/app/features/panel/DataPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DataPanel.tsx @@ -1,5 +1,4 @@ import React, { Component, ComponentClass } from 'react'; -import _ from 'lodash'; export interface OuterProps { type: string; @@ -7,16 +6,19 @@ export interface OuterProps { isVisible: boolean; } -export interface AddedProps { +export interface PanelProps extends OuterProps { data: any[]; } +export interface DataPanel extends ComponentClass { +} + interface State { isLoading: boolean; data: any[]; } -const DataPanel = (ComposedComponent: ComponentClass) => { +export const DataPanelWrapper = (ComposedComponent: ComponentClass) => { class Wrapper extends Component { public static defaultProps = { isVisible: true, @@ -32,26 +34,31 @@ const DataPanel = (ComposedComponent: ComponentClass) = } public componentDidMount() { + console.log('data panel mount'); this.issueQueries(); } - public issueQueries = () => { - const { queries, isVisible } = this.props; + public issueQueries = async () => { + const { isVisible } = this.props; if (!isVisible) { return; } - if (!queries.length) { - this.setState({ data: [{ message: 'no queries' }] }); - return; - } - this.setState({ isLoading: true }); + + await new Promise(resolve => { + setTimeout(() => { + + this.setState({ isLoading: false, data: [{value: 10}] }); + + }, 500); + }); }; public render() { const { data, isLoading } = this.state; + console.log('data panel render'); if (!data.length) { return ( @@ -76,4 +83,3 @@ const DataPanel = (ComposedComponent: ComponentClass) = return Wrapper; }; -export default DataPanel; diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index 3fd9df799af..ebe25379c2d 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -1,10 +1,11 @@ -import React from 'react'; +import React, { ComponentClass } from 'react'; import $ from 'jquery'; import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN } from 'app/core/constants'; import { PanelHeader } from './PanelHeader'; import { PanelEditor } from './PanelEditor'; +import { DataPanel, PanelProps, DataPanelWrapper } from './DataPanel'; const TITLE_HEIGHT = 27; const PANEL_BORDER = 2; @@ -12,33 +13,46 @@ const PANEL_BORDER = 2; export interface Props { panel: PanelModel; dashboard: DashboardModel; - component: any; + component: ComponentClass; } -export class PanelChrome extends React.Component { +interface State { + height: number; +} + +export class PanelChrome extends React.Component { + panelComponent: DataPanel; + constructor(props) { super(props); - this.props.panel.events.on('panel-size-changed', this.triggerForceUpdate.bind(this)); - } - - triggerForceUpdate() { - this.forceUpdate(); - } - - render() { - let panelContentStyle = { + this.state = { height: this.getPanelHeight(), }; - let PanelComponent = this.props.component; + this.panelComponent = DataPanelWrapper(this.props.component); + this.props.panel.events.on('panel-size-changed', this.onPanelSizeChanged); + } + + onPanelSizeChanged = () => { + this.setState({ + height: this.getPanelHeight(), + }); + }; + + componentDidMount() { + console.log('panel chrome mounted'); + } + + render() { + let PanelComponent = this.panelComponent; return (
    -
    - {} +
    + {}
    {this.props.panel.isEditing && } diff --git a/public/app/features/plugins/plugin_loader.ts b/public/app/features/plugins/plugin_loader.ts index f999ee7e2ff..ffcd36312fb 100644 --- a/public/app/features/plugins/plugin_loader.ts +++ b/public/app/features/plugins/plugin_loader.ts @@ -138,11 +138,22 @@ const flotDeps = [ 'jquery.flot.stackpercent', 'jquery.flot.events', ]; + for (let flotDep of flotDeps) { exposeToPlugin(flotDep, { fakeDep: 1 }); } -export function importPluginModule(path: string): Promise { +export interface PluginExports { + PanelCtrl?; + any; + PanelComponent?: any; + Datasource?: any; + QueryCtrl?: any; + ConfigCtrl?: any; + AnnotationsQueryCtrl?: any; +} + +export function importPluginModule(path: string): Promise { let builtIn = builtInPlugins[path]; if (builtIn) { return Promise.resolve(builtIn); diff --git a/public/app/plugins/panel/text2/module.tsx b/public/app/plugins/panel/text2/module.tsx index 8987b738f0b..019cf912340 100644 --- a/public/app/plugins/panel/text2/module.tsx +++ b/public/app/plugins/panel/text2/module.tsx @@ -1,12 +1,20 @@ -import React from 'react'; +import React, { PureComponent } from 'react'; +import { PanelProps } from 'app/features/dashboard/dashgrid/DataPanel'; -export class ReactTestPanel extends React.Component { +export class ReactTestPanel extends PureComponent { constructor(props) { super(props); } render() { - return

    I am a react panel, haha!

    ; + const { data } = this.props; + let value = 0; + + if (data.length) { + value = data[0].value; + } + + return

    I am a react value: {value}

    ; } } From dec62d73401ddce4ecda6b3162018220ca87a7f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 6 Jul 2018 04:42:59 -0700 Subject: [PATCH 17/54] another baby step --- .../dashboard/dashgrid/DashboardGrid.tsx | 2 +- .../dashboard/dashgrid/PanelEditor.tsx | 12 ++++- .../features/dashboard/dashgrid/VizPicker.tsx | 46 +++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 public/app/features/dashboard/dashgrid/VizPicker.tsx diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx index 9ee6cdbe1f8..30d97898900 100644 --- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx @@ -139,7 +139,7 @@ export class DashboardGrid extends React.Component { } onViewModeChanged(payload) { - this.setState({ animated: payload.fullscreen }); + this.setState({ animated: !payload.fullscreen }); } updateGridPos(item, layout) { diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index ebcde802b7f..1b92127c1d3 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -5,6 +5,7 @@ import { store } from 'app/stores/store'; import { observer } from 'mobx-react'; import { QueriesTab } from './QueriesTab'; import classNames from 'classnames'; +import { VizPicker } from './VizPicker'; interface PanelEditorProps { panel: PanelModel; @@ -35,7 +36,16 @@ export class PanelEditor extends React.Component { } renderVizTab() { - return

    Visualizations

    ; + return ( +
    +
    + +
    +
    +
    Options
    +
    +
    + ); } onChangeTab = (tab: PanelEditorTab) => { diff --git a/public/app/features/dashboard/dashgrid/VizPicker.tsx b/public/app/features/dashboard/dashgrid/VizPicker.tsx new file mode 100644 index 00000000000..2d879ef94d4 --- /dev/null +++ b/public/app/features/dashboard/dashgrid/VizPicker.tsx @@ -0,0 +1,46 @@ +import React, { PureComponent } from 'react'; +import config from 'app/core/config'; +import _ from 'lodash'; + +interface Props {} + +interface State { + pluginList: any[]; +} + +export class VizPicker extends PureComponent { + constructor(props) { + super(props); + + this.state = { + pluginList: this.getPanelPlugins(''), + }; + } + + getPanelPlugins(filter) { + let panels = _.chain(config.panels) + .filter({ hideFromList: false }) + .map(item => item) + .value(); + + // add sort by sort property + return _.sortBy(panels, 'sort'); + } + + onChangeVizPlugin = plugin => { + console.log('set viz'); + }; + + renderVizPlugin(plugin, index) { + return ( +
    this.onChangeVizPlugin(plugin)} title={plugin.name}> + +
    {plugin.name}
    +
    + ); + } + + render() { + return
    {this.state.pluginList.map(this.renderVizPlugin)}
    ; + } +} From dbe191fd55943eeee1e7b7fe3cb48c7df462e44a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sat, 7 Jul 2018 05:10:03 -0700 Subject: [PATCH 18/54] wip: viz editor started --- .../dashboard/dashgrid/DashboardPanel.tsx | 1 - .../dashboard/dashgrid/PanelEditor.tsx | 5 +- .../app/features/dashboard/dashnav/dashnav.ts | 2 + public/sass/_grafana.scss | 1 + public/sass/components/_viz_editor.scss | 50 +++++++++++++++++++ 5 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 public/sass/components/_viz_editor.scss diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx index 3d2542d63c8..b56e2bef39f 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -85,7 +85,6 @@ export class DashboardPanel extends React.Component { if (pluginExports.PanelComponent) { return ( { renderVizTab() { return (
    -
    +
    +
    Visualization Type
    -
    +
    Options
    diff --git a/public/app/features/dashboard/dashnav/dashnav.ts b/public/app/features/dashboard/dashnav/dashnav.ts index 628f09349d3..73e06877472 100644 --- a/public/app/features/dashboard/dashnav/dashnav.ts +++ b/public/app/features/dashboard/dashnav/dashnav.ts @@ -38,6 +38,8 @@ export class DashNavCtrl { } else if (search.fullscreen) { delete search.fullscreen; delete search.edit; + delete search.tab; + delete search.panelId; } this.$location.search(search); } diff --git a/public/sass/_grafana.scss b/public/sass/_grafana.scss index afc869f8b15..0c758abf9e0 100644 --- a/public/sass/_grafana.scss +++ b/public/sass/_grafana.scss @@ -92,6 +92,7 @@ @import 'components/form_select_box'; @import 'components/user-picker'; @import 'components/description-picker'; +@import 'components/viz_editor'; // PAGES @import 'pages/login'; diff --git a/public/sass/components/_viz_editor.scss b/public/sass/components/_viz_editor.scss new file mode 100644 index 00000000000..0dc368b4ed0 --- /dev/null +++ b/public/sass/components/_viz_editor.scss @@ -0,0 +1,50 @@ +.viz-editor { + display: flex; +} + +.viz-editor-col1 { + width: 150px; + background: $panel-bg; +} + +.viz-editor-col2 { + flex-grow: 1; +} + +.viz-picker { + padding: 3px 8px; + display: flex; + flex-direction: row; + flex-wrap: wrap; + overflow: auto; + height: 100%; +} + +.viz-picker__item { + background: $card-background; + box-shadow: $card-shadow; + + border-radius: 3px; + padding: $spacer/3 $spacer; + width: 31%; + height: 60px; + text-align: center; + margin: $gf-form-margin; + cursor: pointer; + + &.active, + &:hover { + background: $card-background-hover; + } +} + +.viz-picker__item-name { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + font-size: $font-size-sm; +} + +.viz-picker__item-img { + height: calc(100% - 15px); +} From 50f24c98f7e735a75a732a372209a8f2b013484b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 8 Jul 2018 07:39:25 -0700 Subject: [PATCH 19/54] wip: minor progres on react panels edit mode --- public/app/core/config.ts | 13 +++- .../dashboard/dashgrid/PanelChrome.tsx | 4 +- .../dashboard/dashgrid/PanelEditor.tsx | 19 ++++-- .../features/dashboard/dashgrid/VizPicker.tsx | 46 -------------- .../dashboard/dashgrid/VizTypePicker.tsx | 61 +++++++++++++++++++ .../dashboard/specs/AddPanelPanel.jest.tsx | 15 +++++ public/app/features/panel/panel_directive.ts | 2 +- .../app/features/plugins/plugin_component.ts | 2 +- public/sass/components/_panel_add_panel.scss | 4 -- public/sass/components/_tabbed_view.scss | 23 ++----- public/sass/components/_viz_editor.scss | 43 ++++++++++--- public/sass/pages/_dashboard.scss | 12 +++- 12 files changed, 154 insertions(+), 90 deletions(-) delete mode 100644 public/app/features/dashboard/dashgrid/VizPicker.tsx create mode 100644 public/app/features/dashboard/dashgrid/VizTypePicker.tsx diff --git a/public/app/core/config.ts b/public/app/core/config.ts index e065ddb22fb..eb2eee999ab 100644 --- a/public/app/core/config.ts +++ b/public/app/core/config.ts @@ -7,9 +7,20 @@ export interface BuildInfo { env: string; } +export interface PanelPlugin { + id: string; + name: string; + meta: any; + hideFromList: boolean; + module: string; + baseUrl: string; + info: any; + sort: number; +} + export class Settings { datasources: any; - panels: any; + panels: PanelPlugin[]; appSubUrl: string; window_title_prefix: string; buildInfo: BuildInfo; diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index ebe25379c2d..b4584af63f8 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -48,7 +48,7 @@ export class PanelChrome extends React.Component { let PanelComponent = this.panelComponent; return ( -
    +
    @@ -73,6 +73,6 @@ export class PanelChrome extends React.Component { height = panel.gridPos.h * GRID_CELL_HEIGHT + (panel.gridPos.h - 1) * GRID_CELL_VMARGIN; } - return height - PANEL_BORDER + TITLE_HEIGHT; + return height - (PANEL_BORDER + TITLE_HEIGHT); } } diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index 9296293f6de..55babf4b19d 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -1,11 +1,12 @@ import React from 'react'; +import classNames from 'classnames'; import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; import { store } from 'app/stores/store'; import { observer } from 'mobx-react'; import { QueriesTab } from './QueriesTab'; -import classNames from 'classnames'; -import { VizPicker } from './VizPicker'; +import { PanelPlugin } from 'app/core/config'; +import { VizTypePicker } from './VizTypePicker'; interface PanelEditorProps { panel: PanelModel; @@ -39,16 +40,22 @@ export class PanelEditor extends React.Component { return (
    -
    Visualization Type
    - +
    -
    Options
    +
    Options
    ); } + onVizTypeChanged = (plugin: PanelPlugin) => { + this.props.panel.type = plugin.id; + this.forceUpdate(); + + console.log('panel type changed', plugin); + }; + onChangeTab = (tab: PanelEditorTab) => { store.view.updateQuery({ tab: tab.id }, false); }; @@ -57,7 +64,7 @@ export class PanelEditor extends React.Component { const activeTab: string = store.view.query.get('tab') || 'queries'; return ( -
    +
      {this.tabs.map(tab => { diff --git a/public/app/features/dashboard/dashgrid/VizPicker.tsx b/public/app/features/dashboard/dashgrid/VizPicker.tsx deleted file mode 100644 index 2d879ef94d4..00000000000 --- a/public/app/features/dashboard/dashgrid/VizPicker.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React, { PureComponent } from 'react'; -import config from 'app/core/config'; -import _ from 'lodash'; - -interface Props {} - -interface State { - pluginList: any[]; -} - -export class VizPicker extends PureComponent { - constructor(props) { - super(props); - - this.state = { - pluginList: this.getPanelPlugins(''), - }; - } - - getPanelPlugins(filter) { - let panels = _.chain(config.panels) - .filter({ hideFromList: false }) - .map(item => item) - .value(); - - // add sort by sort property - return _.sortBy(panels, 'sort'); - } - - onChangeVizPlugin = plugin => { - console.log('set viz'); - }; - - renderVizPlugin(plugin, index) { - return ( -
      this.onChangeVizPlugin(plugin)} title={plugin.name}> - -
      {plugin.name}
      -
      - ); - } - - render() { - return
      {this.state.pluginList.map(this.renderVizPlugin)}
      ; - } -} diff --git a/public/app/features/dashboard/dashgrid/VizTypePicker.tsx b/public/app/features/dashboard/dashgrid/VizTypePicker.tsx new file mode 100644 index 00000000000..197892090b5 --- /dev/null +++ b/public/app/features/dashboard/dashgrid/VizTypePicker.tsx @@ -0,0 +1,61 @@ +import React, { PureComponent } from 'react'; +import classNames from 'classnames'; +import config, { PanelPlugin } from 'app/core/config'; +import _ from 'lodash'; + +interface Props { + currentType: string; + onTypeChanged: (newType: PanelPlugin) => void; +} + +interface State { + pluginList: PanelPlugin[]; +} + +export class VizTypePicker extends PureComponent { + constructor(props) { + super(props); + + this.state = { + pluginList: this.getPanelPlugins(''), + }; + } + + getPanelPlugins(filter) { + let panels = _.chain(config.panels) + .filter({ hideFromList: false }) + .map(item => item) + .value(); + + // add sort by sort property + return _.sortBy(panels, 'sort'); + } + + renderVizPlugin = (plugin, index) => { + const cssClass = classNames({ + 'viz-picker__item': true, + 'viz-picker__item--selected': plugin.id === this.props.currentType, + }); + + return ( +
      this.props.onTypeChanged(plugin)} title={plugin.name}> + +
      {plugin.name}
      +
      + ); + }; + + render() { + return ( +
      +
      + +
      +
      {this.state.pluginList.map(this.renderVizPlugin)}
      +
      + ); + } +} diff --git a/public/app/features/dashboard/specs/AddPanelPanel.jest.tsx b/public/app/features/dashboard/specs/AddPanelPanel.jest.tsx index 9bf99b5720c..c5f66fed32a 100644 --- a/public/app/features/dashboard/specs/AddPanelPanel.jest.tsx +++ b/public/app/features/dashboard/specs/AddPanelPanel.jest.tsx @@ -23,6 +23,9 @@ describe('AddPanelPanel', () => { hideFromList: false, name: 'Singlestat', sort: 2, + module: '', + baseUrl: '', + meta: {}, info: { logos: { small: '', @@ -34,6 +37,9 @@ describe('AddPanelPanel', () => { hideFromList: true, name: 'Hidden', sort: 100, + meta: {}, + module: '', + baseUrl: '', info: { logos: { small: '', @@ -45,6 +51,9 @@ describe('AddPanelPanel', () => { hideFromList: false, name: 'Graph', sort: 1, + meta: {}, + module: '', + baseUrl: '', info: { logos: { small: '', @@ -56,6 +65,9 @@ describe('AddPanelPanel', () => { hideFromList: false, name: 'Zabbix', sort: 100, + meta: {}, + module: '', + baseUrl: '', info: { logos: { small: '', @@ -67,6 +79,9 @@ describe('AddPanelPanel', () => { hideFromList: false, name: 'Piechart', sort: 100, + meta: {}, + module: '', + baseUrl: '', info: { logos: { small: '', diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index 685e527c944..67cad9c5594 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/public/app/features/panel/panel_directive.ts @@ -26,7 +26,7 @@ var panelTemplate = `
    -
    +

    {{ctrl.pluginName}} diff --git a/public/app/features/plugins/plugin_component.ts b/public/app/features/plugins/plugin_component.ts index 1936e57f558..5ef4019c24d 100644 --- a/public/app/features/plugins/plugin_component.ts +++ b/public/app/features/plugins/plugin_component.ts @@ -95,7 +95,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $ PanelCtrl.templatePromise = getTemplate(PanelCtrl).then(template => { PanelCtrl.templateUrl = null; - PanelCtrl.template = `${template}`; + PanelCtrl.template = `${template}`; return componentInfo; }); diff --git a/public/sass/components/_panel_add_panel.scss b/public/sass/components/_panel_add_panel.scss index 5bfff31a108..263b181262e 100644 --- a/public/sass/components/_panel_add_panel.scss +++ b/public/sass/components/_panel_add_panel.scss @@ -85,10 +85,6 @@ height: calc(100% - 15px); } -.add-panel__item-icon { - padding: 2px; -} - .add-panel__searchbar { width: 100%; margin-bottom: 10px; diff --git a/public/sass/components/_tabbed_view.scss b/public/sass/components/_tabbed_view.scss index a5d38d292a1..d577fa4777b 100644 --- a/public/sass/components/_tabbed_view.scss +++ b/public/sass/components/_tabbed_view.scss @@ -1,28 +1,16 @@ .tabbed-view { - padding: $spacer*3; margin-bottom: $dashboard-padding; + display: flex; + flex-direction: column; + height: 100%; - &.tabbed-view--panel-edit { - padding: 0; - - .tabbed-view-header { - padding: 0px 25px; - background: none; - } - } - - &.tabbed-view--panel-edit-new { + &.tabbed-view--new { padding: 10px 0 0 0; - - .tabbed-view-header { - padding: 0px; - background: none; - } + height: 100%; } } .tabbed-view-header { - background: $page-header-bg; box-shadow: $page-header-shadow; border-bottom: 1px solid $page-header-border-color; @include clearfix(); @@ -58,6 +46,7 @@ .tabbed-view-body { padding: $spacer*2 $spacer; + height: 100%; &--small { min-height: 0px; diff --git a/public/sass/components/_viz_editor.scss b/public/sass/components/_viz_editor.scss index 0dc368b4ed0..b377a7d5c28 100644 --- a/public/sass/components/_viz_editor.scss +++ b/public/sass/components/_viz_editor.scss @@ -1,10 +1,12 @@ .viz-editor { display: flex; + height: 100%; } .viz-editor-col1 { - width: 150px; - background: $panel-bg; + width: 240px; + height: 100%; + margin-right: 40px; } .viz-editor-col2 { @@ -12,11 +14,15 @@ } .viz-picker { + display: flex; + flex-direction: column; +} + +.viz-picker-list { padding: 3px 8px; display: flex; - flex-direction: row; - flex-wrap: wrap; - overflow: auto; + flex-direction: column; + overflow: hidden; height: 100%; } @@ -25,17 +31,29 @@ box-shadow: $card-shadow; border-radius: 3px; - padding: $spacer/3 $spacer; - width: 31%; + padding: $spacer; + width: 100%; height: 60px; text-align: center; margin: $gf-form-margin; cursor: pointer; + display: flex; - &.active, &:hover { background: $card-background-hover; } + + &--selected { + border: 1px solid $orange; + + .viz-picker__item-name { + color: $text-color; + } + + .viz-picker__item-img { + filter: saturate(100%); + } + } } .viz-picker__item-name { @@ -43,8 +61,15 @@ overflow: hidden; white-space: nowrap; font-size: $font-size-sm; + display: flex; + flex-direction: column; + align-self: center; + padding-left: $spacer; + font-size: $font-size-md; + color: $text-muted; } .viz-picker__item-img { - height: calc(100% - 15px); + height: 100%; + filter: saturate(30%); } diff --git a/public/sass/pages/_dashboard.scss b/public/sass/pages/_dashboard.scss index 2e8097e3fb7..1ff4f2078e7 100644 --- a/public/sass/pages/_dashboard.scss +++ b/public/sass/pages/_dashboard.scss @@ -1,7 +1,8 @@ .dashboard-container { padding: $dashboard-padding; width: 100%; - min-height: 100%; + height: 100%; + box-sizing: border-box; } .template-variable { @@ -28,12 +29,17 @@ div.flot-text { height: 100%; } +.panel-editor-container { + display: flex; + flex-direction: column; + height: 100%; +} + .panel-container { background-color: $panel-bg; border: $panel-border; position: relative; border-radius: 3px; - height: 100%; &.panel-transparent { background-color: transparent; @@ -233,5 +239,5 @@ div.flot-text { } .panel-full-edit { - margin: $dashboard-padding (-$dashboard-padding) 0 (-$dashboard-padding); + padding-top: $dashboard-padding; } From 8036c49ffed416a210f1d8859b7e5f619410cbdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 8 Jul 2018 12:29:23 -0700 Subject: [PATCH 20/54] wip: minopr progress on react panel edit infra --- .../features/dashboard/dashgrid/DashboardPanel.tsx | 3 +-- .../app/features/dashboard/dashgrid/PanelEditor.tsx | 2 -- public/sass/components/_tabbed_view.scss | 7 ++++--- public/sass/components/_viz_editor.scss | 12 +++++++----- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx index b56e2bef39f..0591d90cde2 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -53,8 +53,7 @@ export class DashboardPanel extends React.Component { } componentDidUpdate() { - // skip loading angular component if we have no element - // or we have already loaded it + // skip loading angular component if we have no element or we have already loaded it if (!this.element || this.angularPanel) { return; } diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index 55babf4b19d..3ddf7d2f81b 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -52,8 +52,6 @@ export class PanelEditor extends React.Component { onVizTypeChanged = (plugin: PanelPlugin) => { this.props.panel.type = plugin.id; this.forceUpdate(); - - console.log('panel type changed', plugin); }; onChangeTab = (tab: PanelEditorTab) => { diff --git a/public/sass/components/_tabbed_view.scss b/public/sass/components/_tabbed_view.scss index d577fa4777b..80e76b5fbf4 100644 --- a/public/sass/components/_tabbed_view.scss +++ b/public/sass/components/_tabbed_view.scss @@ -1,5 +1,4 @@ .tabbed-view { - margin-bottom: $dashboard-padding; display: flex; flex-direction: column; height: 100%; @@ -45,8 +44,10 @@ } .tabbed-view-body { - padding: $spacer*2 $spacer; - height: 100%; + padding: $spacer*2 $spacer $spacer $spacer; + display: flex; + flex-direction: column; + flex: 1; &--small { min-height: 0px; diff --git a/public/sass/components/_viz_editor.scss b/public/sass/components/_viz_editor.scss index b377a7d5c28..f0c4dceaee0 100644 --- a/public/sass/components/_viz_editor.scss +++ b/public/sass/components/_viz_editor.scss @@ -4,7 +4,7 @@ } .viz-editor-col1 { - width: 240px; + width: 210px; height: 100%; margin-right: 40px; } @@ -16,14 +16,15 @@ .viz-picker { display: flex; flex-direction: column; + height: 100%; } .viz-picker-list { - padding: 3px 8px; + padding-top: $spacer; display: flex; flex-direction: column; overflow: hidden; - height: 100%; + flex-grow: 1; } .viz-picker__item { @@ -35,9 +36,10 @@ width: 100%; height: 60px; text-align: center; - margin: $gf-form-margin; + margin-bottom: 6px; cursor: pointer; display: flex; + flex-shrink: 0; &:hover { background: $card-background-hover; @@ -60,7 +62,7 @@ text-overflow: ellipsis; overflow: hidden; white-space: nowrap; - font-size: $font-size-sm; + font-size: $font-size-h5; display: flex; flex-direction: column; align-self: center; From 51f8d3ca420c93c9ad0ea6c0830ba5b1797a6607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 8 Jul 2018 13:03:22 -0700 Subject: [PATCH 21/54] fix: minor css change --- public/sass/components/_viz_editor.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/public/sass/components/_viz_editor.scss b/public/sass/components/_viz_editor.scss index f0c4dceaee0..7d69813ebd5 100644 --- a/public/sass/components/_viz_editor.scss +++ b/public/sass/components/_viz_editor.scss @@ -40,6 +40,7 @@ cursor: pointer; display: flex; flex-shrink: 0; + border: 1px solid transparent; &:hover { background: $card-background-hover; From 761283231c4c1d3e98342db826985c64c5b042ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 9 Jul 2018 18:17:51 +0200 Subject: [PATCH 22/54] react panels: working on changing type --- public/app/core/config.ts | 12 +- .../dashboard/dashgrid/DashboardGrid.tsx | 1 + .../dashboard/dashgrid/DashboardPanel.tsx | 81 ++++++-- .../dashboard/dashgrid/PanelChrome.tsx | 47 +---- .../dashboard/dashgrid/PanelEditor.tsx | 6 +- .../dashboard/dashgrid/PanelHeader.tsx | 14 +- .../dashboard/dashgrid/VizTypePicker.tsx | 3 +- public/app/features/dashboard/panel_model.ts | 5 + .../app/features/plugins/built_in_plugins.ts | 2 + public/app/features/plugins/plugin_loader.ts | 11 +- public/app/plugins/panel/graph2/README.md | 5 + .../panel/graph2/img/icn-graph-panel.svg | 186 ++++++++++++++++++ public/app/plugins/panel/graph2/module.tsx | 21 ++ public/app/plugins/panel/graph2/plugin.json | 17 ++ public/app/plugins/panel/text2/module.tsx | 2 +- public/app/types/plugins.ts | 20 ++ public/sass/pages/_dashboard.scss | 9 + 17 files changed, 352 insertions(+), 90 deletions(-) create mode 100644 public/app/plugins/panel/graph2/README.md create mode 100644 public/app/plugins/panel/graph2/img/icn-graph-panel.svg create mode 100644 public/app/plugins/panel/graph2/module.tsx create mode 100644 public/app/plugins/panel/graph2/plugin.json create mode 100644 public/app/types/plugins.ts diff --git a/public/app/core/config.ts b/public/app/core/config.ts index eb2eee999ab..249a274ce5d 100644 --- a/public/app/core/config.ts +++ b/public/app/core/config.ts @@ -1,4 +1,5 @@ import _ from 'lodash'; +import { PanelPlugin } from 'app/types/plugins'; export interface BuildInfo { version: string; @@ -7,17 +8,6 @@ export interface BuildInfo { env: string; } -export interface PanelPlugin { - id: string; - name: string; - meta: any; - hideFromList: boolean; - module: string; - baseUrl: string; - info: any; - sort: number; -} - export class Settings { datasources: any; panels: PanelPlugin[]; diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx index 30d97898900..77b55a2130f 100644 --- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx @@ -171,6 +171,7 @@ export class DashboardGrid extends React.Component { renderPanels() { const panelElements = []; + console.log('render panels'); for (let panel of this.props.dashboard.panels) { const panelClasses = classNames({ panel: true, 'panel--fullscreen': panel.fullscreen }); diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx index 0591d90cde2..781e7186a7e 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -5,8 +5,10 @@ import { DashboardModel } from '../dashboard_model'; import { getAngularLoader, AngularComponent } from 'app/core/services/angular_loader'; import { DashboardRow } from './DashboardRow'; import { AddPanelPanel } from './AddPanelPanel'; -import { importPluginModule, PluginExports } from 'app/features/plugins/plugin_loader'; +import { importPluginModule } from 'app/features/plugins/plugin_loader'; +import { PluginExports } from 'app/types/plugins'; import { PanelChrome } from './PanelChrome'; +import { PanelEditor } from './PanelEditor'; export interface Props { panel: PanelModel; @@ -29,15 +31,11 @@ export class DashboardPanel extends React.Component { this.specialPanels['row'] = this.renderRow.bind(this); this.specialPanels['add-panel'] = this.renderAddPanel.bind(this); + this.props.panel.events.on('panel-size-changed', this.triggerForceUpdate.bind(this)); + } - if (!this.isSpecial()) { - this.pluginInfo = config.panels[this.props.panel.type]; - - // load panel plugin - importPluginModule(this.pluginInfo.module).then(pluginExports => { - this.setState({ pluginExports: pluginExports }); - }); - } + triggerForceUpdate() { + this.forceUpdate(); } isSpecial() { @@ -52,8 +50,33 @@ export class DashboardPanel extends React.Component { return ; } + loadPlugin() { + if (this.isSpecial()) { + return; + } + + // handle plugin loading & changing of plugin type + if (!this.pluginInfo || this.pluginInfo.id !== this.props.panel.type) { + this.pluginInfo = config.panels[this.props.panel.type]; + + if (this.pluginInfo.exports) { + this.setState({ pluginExports: this.pluginInfo.exports }); + } else { + importPluginModule(this.pluginInfo.module).then(pluginExports => { + this.setState({ pluginExports: pluginExports }); + }); + } + } + } + + componentDidMount() { + this.loadPlugin(); + } + componentDidUpdate() { - // skip loading angular component if we have no element or we have already loaded it + this.loadPlugin(); + + // handle angular plugin loading if (!this.element || this.angularPanel) { return; } @@ -70,25 +93,43 @@ export class DashboardPanel extends React.Component { } } - render() { + renderReactPanel() { const { pluginExports } = this.state; + const containerClass = this.props.panel.isEditing ? 'panel-editor-container' : 'panel-height-helper'; + const panelWrapperClass = this.props.panel.isEditing ? 'panel-editor-container__panel' : 'panel-height-helper'; + // this might look strange with these classes that change when edit, but + // I want to try to keep markup (parents) for panel the same in edit mode to avoide unmount / new mount of panel + // plugin component + return ( +
    +
    + +
    + {this.props.panel.isEditing && ( +
    + +
    + )} +
    + ); + } + + render() { if (this.isSpecial()) { return this.specialPanels[this.props.panel.type](); } - if (!pluginExports) { + if (!this.state.pluginExports) { return null; } - if (pluginExports.PanelComponent) { - return ( - - ); + if (this.state.pluginExports.PanelComponent) { + return this.renderReactPanel(); } // legacy angular rendering diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index b4584af63f8..eb0b34c3b06 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -2,23 +2,16 @@ import React, { ComponentClass } from 'react'; import $ from 'jquery'; import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; -import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN } from 'app/core/constants'; import { PanelHeader } from './PanelHeader'; -import { PanelEditor } from './PanelEditor'; import { DataPanel, PanelProps, DataPanelWrapper } from './DataPanel'; -const TITLE_HEIGHT = 27; -const PANEL_BORDER = 2; - export interface Props { panel: PanelModel; dashboard: DashboardModel; component: ComponentClass; } -interface State { - height: number; -} +interface State {} export class PanelChrome extends React.Component { panelComponent: DataPanel; @@ -26,20 +19,9 @@ export class PanelChrome extends React.Component { constructor(props) { super(props); - this.state = { - height: this.getPanelHeight(), - }; - this.panelComponent = DataPanelWrapper(this.props.component); - this.props.panel.events.on('panel-size-changed', this.onPanelSizeChanged); } - onPanelSizeChanged = () => { - this.setState({ - height: this.getPanelHeight(), - }); - }; - componentDidMount() { console.log('panel chrome mounted'); } @@ -48,31 +30,10 @@ export class PanelChrome extends React.Component { let PanelComponent = this.panelComponent; return ( -
    -
    - -
    - {} -
    -
    - {this.props.panel.isEditing && } +
    + +
    {}
    ); } - - getPanelHeight() { - const panel = this.props.panel; - let height = 0; - - if (panel.fullscreen) { - var docHeight = $(window).height(); - var editHeight = Math.floor(docHeight * 0.3); - var fullscreenHeight = Math.floor(docHeight * 0.8); - height = panel.isEditing ? editHeight : fullscreenHeight; - } else { - height = panel.gridPos.h * GRID_CELL_HEIGHT + (panel.gridPos.h - 1) * GRID_CELL_VMARGIN; - } - - return height - (PANEL_BORDER + TITLE_HEIGHT); - } } diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index 3ddf7d2f81b..1a8b1190928 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -5,7 +5,7 @@ import { DashboardModel } from '../dashboard_model'; import { store } from 'app/stores/store'; import { observer } from 'mobx-react'; import { QueriesTab } from './QueriesTab'; -import { PanelPlugin } from 'app/core/config'; +import { PanelPlugin } from 'app/types/plugins'; import { VizTypePicker } from './VizTypePicker'; interface PanelEditorProps { @@ -50,8 +50,8 @@ export class PanelEditor extends React.Component { } onVizTypeChanged = (plugin: PanelPlugin) => { - this.props.panel.type = plugin.id; - this.forceUpdate(); + console.log('changing type to ', plugin.id); + this.props.panel.changeType(plugin.id); }; onChangeTab = (tab: PanelEditorTab) => { diff --git a/public/app/features/dashboard/dashgrid/PanelHeader.tsx b/public/app/features/dashboard/dashgrid/PanelHeader.tsx index 97d41e15a0c..c4c169ceb88 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader.tsx @@ -21,6 +21,16 @@ export class PanelHeader extends React.Component { ); }; + onViewPanel = () => { + store.view.updateQuery( + { + panelId: this.props.panel.id, + fullscreen: true, + }, + false + ); + }; + render() { let isFullscreen = false; let isLoading = false; @@ -52,7 +62,9 @@ export class PanelHeader extends React.Component {
  • - asd + + View +
diff --git a/public/app/features/dashboard/dashgrid/VizTypePicker.tsx b/public/app/features/dashboard/dashgrid/VizTypePicker.tsx index 197892090b5..5a77ab6bfcf 100644 --- a/public/app/features/dashboard/dashgrid/VizTypePicker.tsx +++ b/public/app/features/dashboard/dashgrid/VizTypePicker.tsx @@ -1,6 +1,7 @@ import React, { PureComponent } from 'react'; import classNames from 'classnames'; -import config, { PanelPlugin } from 'app/core/config'; +import config from 'app/core/config'; +import { PanelPlugin } from 'app/types/plugins'; import _ from 'lodash'; interface Props { diff --git a/public/app/features/dashboard/panel_model.ts b/public/app/features/dashboard/panel_model.ts index 6ee49886a06..8c9270ad1ab 100644 --- a/public/app/features/dashboard/panel_model.ts +++ b/public/app/features/dashboard/panel_model.ts @@ -97,6 +97,11 @@ export class PanelModel { this.events.emit('panel-init-edit-mode'); } + changeType(newType: string) { + this.type = newType; + this.events.emit('panel-size-changed'); + } + destroy() { this.events.removeAllListeners(); } diff --git a/public/app/features/plugins/built_in_plugins.ts b/public/app/features/plugins/built_in_plugins.ts index 5f1f56b29bd..79f699d2291 100644 --- a/public/app/features/plugins/built_in_plugins.ts +++ b/public/app/features/plugins/built_in_plugins.ts @@ -12,6 +12,7 @@ import * as mssqlPlugin from 'app/plugins/datasource/mssql/module'; 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'; import * as pluginsListPanel from 'app/plugins/panel/pluginlist/module'; @@ -41,6 +42,7 @@ const builtInPlugins = { '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, 'app/plugins/panel/pluginlist/module': pluginsListPanel, diff --git a/public/app/features/plugins/plugin_loader.ts b/public/app/features/plugins/plugin_loader.ts index ffcd36312fb..4564e5d4fd9 100644 --- a/public/app/features/plugins/plugin_loader.ts +++ b/public/app/features/plugins/plugin_loader.ts @@ -18,6 +18,7 @@ import config from 'app/core/config'; import TimeSeries from 'app/core/time_series2'; import TableModel from 'app/core/table_model'; import { coreModule, appEvents, contextSrv } from 'app/core/core'; +import { PluginExports } from 'app/types/plugins'; import * as datemath from 'app/core/utils/datemath'; import * as fileExport from 'app/core/utils/file_export'; import * as flatten from 'app/core/utils/flatten'; @@ -143,16 +144,6 @@ for (let flotDep of flotDeps) { exposeToPlugin(flotDep, { fakeDep: 1 }); } -export interface PluginExports { - PanelCtrl?; - any; - PanelComponent?: any; - Datasource?: any; - QueryCtrl?: any; - ConfigCtrl?: any; - AnnotationsQueryCtrl?: any; -} - export function importPluginModule(path: string): Promise { let builtIn = builtInPlugins[path]; if (builtIn) { diff --git a/public/app/plugins/panel/graph2/README.md b/public/app/plugins/panel/graph2/README.md new file mode 100644 index 00000000000..667ab51784a --- /dev/null +++ b/public/app/plugins/panel/graph2/README.md @@ -0,0 +1,5 @@ +# 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/graph2/img/icn-graph-panel.svg b/public/app/plugins/panel/graph2/img/icn-graph-panel.svg new file mode 100644 index 00000000000..463b3d5770b --- /dev/null +++ b/public/app/plugins/panel/graph2/img/icn-graph-panel.svg @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/app/plugins/panel/graph2/module.tsx b/public/app/plugins/panel/graph2/module.tsx new file mode 100644 index 00000000000..7ae24bd7d40 --- /dev/null +++ b/public/app/plugins/panel/graph2/module.tsx @@ -0,0 +1,21 @@ +import React, { PureComponent } from 'react'; +import { PanelProps } from 'app/features/dashboard/dashgrid/DataPanel'; + +export class ReactTestPanel extends PureComponent { + constructor(props) { + super(props); + } + + render() { + const { data } = this.props; + let value = 0; + + if (data.length) { + value = data[0].value; + } + + return

Graph Panel! {value}

; + } +} + +export { ReactTestPanel as PanelComponent }; diff --git a/public/app/plugins/panel/graph2/plugin.json b/public/app/plugins/panel/graph2/plugin.json new file mode 100644 index 00000000000..bc60d6ad2d7 --- /dev/null +++ b/public/app/plugins/panel/graph2/plugin.json @@ -0,0 +1,17 @@ +{ + "type": "panel", + "name": "React Graph", + "id": "graph2", + + "info": { + "author": { + "name": "Grafana Project", + "url": "https://grafana.com" + }, + "logos": { + "small": "img/icn-graph-panel.svg", + "large": "img/icn-graph-panel.svg" + } + } +} + diff --git a/public/app/plugins/panel/text2/module.tsx b/public/app/plugins/panel/text2/module.tsx index 019cf912340..20b6be4ca72 100644 --- a/public/app/plugins/panel/text2/module.tsx +++ b/public/app/plugins/panel/text2/module.tsx @@ -14,7 +14,7 @@ export class ReactTestPanel extends PureComponent { value = data[0].value; } - return

I am a react value: {value}

; + return

Text Panel {value}

; } } diff --git a/public/app/types/plugins.ts b/public/app/types/plugins.ts new file mode 100644 index 00000000000..9f0208463b9 --- /dev/null +++ b/public/app/types/plugins.ts @@ -0,0 +1,20 @@ +export interface PluginExports { + PanelCtrl?; + PanelComponent?: any; + Datasource?: any; + QueryCtrl?: any; + ConfigCtrl?: any; + AnnotationsQueryCtrl?: any; +} + +export interface PanelPlugin { + id: string; + name: string; + meta: any; + hideFromList: boolean; + module: string; + baseUrl: string; + info: any; + sort: number; + exports?: PluginExports; +} diff --git a/public/sass/pages/_dashboard.scss b/public/sass/pages/_dashboard.scss index 1ff4f2078e7..dad8bfa1c49 100644 --- a/public/sass/pages/_dashboard.scss +++ b/public/sass/pages/_dashboard.scss @@ -35,11 +35,20 @@ div.flot-text { height: 100%; } +.panel-editor-container__panel { + height: 35%; +} + +.panel-editor-container__editor { + height: 65%; +} + .panel-container { background-color: $panel-bg; border: $panel-border; position: relative; border-radius: 3px; + height: 100%; &.panel-transparent { background-color: transparent; From dc3a81200b1cd366bdf237ada152ae3d79bdaf59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 9 Jul 2018 13:24:15 -0700 Subject: [PATCH 23/54] wip: you can now change panel type in edit mode --- .../dashboard/dashgrid/DashboardPanel.tsx | 28 +++++++++++++------ .../dashboard/dashgrid/PanelChrome.tsx | 20 +++++++------ .../dashboard/dashgrid/PanelEditor.tsx | 9 ++---- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx index 781e7186a7e..bec0a6e6dbe 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -6,7 +6,7 @@ import { getAngularLoader, AngularComponent } from 'app/core/services/angular_lo import { DashboardRow } from './DashboardRow'; import { AddPanelPanel } from './AddPanelPanel'; import { importPluginModule } from 'app/features/plugins/plugin_loader'; -import { PluginExports } from 'app/types/plugins'; +import { PluginExports, PanelPlugin } from 'app/types/plugins'; import { PanelChrome } from './PanelChrome'; import { PanelEditor } from './PanelEditor'; @@ -27,15 +27,13 @@ export class DashboardPanel extends React.Component { constructor(props) { super(props); - this.state = { pluginExports: null }; + + this.state = { + pluginExports: null, + }; this.specialPanels['row'] = this.renderRow.bind(this); this.specialPanels['add-panel'] = this.renderAddPanel.bind(this); - this.props.panel.events.on('panel-size-changed', this.triggerForceUpdate.bind(this)); - } - - triggerForceUpdate() { - this.forceUpdate(); } isSpecial() { @@ -50,6 +48,11 @@ export class DashboardPanel extends React.Component { return ; } + onPluginTypeChanged = (plugin: PanelPlugin) => { + this.props.panel.changeType(plugin.id); + this.loadPlugin(); + }; + loadPlugin() { if (this.isSpecial()) { return; @@ -63,6 +66,9 @@ export class DashboardPanel extends React.Component { this.setState({ pluginExports: this.pluginInfo.exports }); } else { importPluginModule(this.pluginInfo.module).then(pluginExports => { + // cache plugin exports (saves a promise async cycle next time) + this.pluginInfo.exports = pluginExports; + // update panel state this.setState({ pluginExports: pluginExports }); }); } @@ -74,6 +80,7 @@ export class DashboardPanel extends React.Component { } componentDidUpdate() { + console.log('componentDidUpdate'); this.loadPlugin(); // handle angular plugin loading @@ -112,7 +119,12 @@ export class DashboardPanel extends React.Component {
{this.props.panel.isEditing && (
- +
)}
diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index eb0b34c3b06..73208c34130 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -1,5 +1,4 @@ import React, { ComponentClass } from 'react'; -import $ from 'jquery'; import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; import { PanelHeader } from './PanelHeader'; @@ -13,21 +12,26 @@ export interface Props { interface State {} +// cache DataPanel wrapper components +const dataPanels: { [s: string]: DataPanel } = {}; + export class PanelChrome extends React.Component { panelComponent: DataPanel; constructor(props) { super(props); - - this.panelComponent = DataPanelWrapper(this.props.component); - } - - componentDidMount() { - console.log('panel chrome mounted'); } render() { - let PanelComponent = this.panelComponent; + const { type } = this.props.panel; + + let PanelComponent = dataPanels[type]; + + if (!PanelComponent) { + PanelComponent = dataPanels[type] = DataPanelWrapper(this.props.component); + } + + console.log('PanelChrome render', PanelComponent); return (
diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index 1a8b1190928..f2b71837822 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -11,6 +11,8 @@ import { VizTypePicker } from './VizTypePicker'; interface PanelEditorProps { panel: PanelModel; dashboard: DashboardModel; + panelType: string; + onTypeChanged: (newType: PanelPlugin) => void; } interface PanelEditorTab { @@ -40,7 +42,7 @@ export class PanelEditor extends React.Component { return (
- +
Options
@@ -49,11 +51,6 @@ export class PanelEditor extends React.Component { ); } - onVizTypeChanged = (plugin: PanelPlugin) => { - console.log('changing type to ', plugin.id); - this.props.panel.changeType(plugin.id); - }; - onChangeTab = (tab: PanelEditorTab) => { store.view.updateQuery({ tab: tab.id }, false); }; From 211e0f2199f67184f7d010c4999f2e6fb6f671a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 11 Jul 2018 12:11:21 -0700 Subject: [PATCH 24/54] wip: another baby step, another million to go --- .../dashboard/dashgrid/DashboardPanel.tsx | 2 +- .../features/dashboard/dashgrid/PanelEditor.tsx | 15 ++++++++++++++- public/app/plugins/panel/text2/module.tsx | 8 +++++++- public/app/types/plugins.ts | 1 + public/sass/components/_viz_editor.scss | 2 +- 5 files changed, 24 insertions(+), 4 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx index bec0a6e6dbe..4b2e81b969c 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -107,7 +107,6 @@ export class DashboardPanel extends React.Component { // this might look strange with these classes that change when edit, but // I want to try to keep markup (parents) for panel the same in edit mode to avoide unmount / new mount of panel - // plugin component return (
@@ -124,6 +123,7 @@ export class DashboardPanel extends React.Component { panelType={this.props.panel.type} dashboard={this.props.dashboard} onTypeChanged={this.onPluginTypeChanged} + pluginExports={pluginExports} />
)} diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index f2b71837822..70b5bf11815 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -5,13 +5,14 @@ import { DashboardModel } from '../dashboard_model'; import { store } from 'app/stores/store'; import { observer } from 'mobx-react'; import { QueriesTab } from './QueriesTab'; -import { PanelPlugin } from 'app/types/plugins'; +import { PanelPlugin, PluginExports } from 'app/types/plugins'; import { VizTypePicker } from './VizTypePicker'; interface PanelEditorProps { panel: PanelModel; dashboard: DashboardModel; panelType: string; + pluginExports: PluginExports; onTypeChanged: (newType: PanelPlugin) => void; } @@ -38,6 +39,17 @@ export class PanelEditor extends React.Component { return ; } + renderPanelOptions() { + const { pluginExports } = this.props; + + if (pluginExports.PanelOptions) { + const PanelOptions = pluginExports.PanelOptions; + return ; + } else { + return

Visualization has no options

; + } + } + renderVizTab() { return (
@@ -46,6 +58,7 @@ export class PanelEditor extends React.Component {
Options
+ {this.renderPanelOptions()}
); diff --git a/public/app/plugins/panel/text2/module.tsx b/public/app/plugins/panel/text2/module.tsx index 20b6be4ca72..703a9897c6f 100644 --- a/public/app/plugins/panel/text2/module.tsx +++ b/public/app/plugins/panel/text2/module.tsx @@ -18,4 +18,10 @@ export class ReactTestPanel extends PureComponent { } } -export { ReactTestPanel as PanelComponent }; +export class TextOptions extends PureComponent { + render() { + return

Text2 Options component

; + } +} + +export { ReactTestPanel as PanelComponent, TextOptions as PanelOptions }; diff --git a/public/app/types/plugins.ts b/public/app/types/plugins.ts index 9f0208463b9..53d7b2c51c9 100644 --- a/public/app/types/plugins.ts +++ b/public/app/types/plugins.ts @@ -5,6 +5,7 @@ export interface PluginExports { QueryCtrl?: any; ConfigCtrl?: any; AnnotationsQueryCtrl?: any; + PanelOptions?: any; } export interface PanelPlugin { diff --git a/public/sass/components/_viz_editor.scss b/public/sass/components/_viz_editor.scss index 7d69813ebd5..3658c869d6d 100644 --- a/public/sass/components/_viz_editor.scss +++ b/public/sass/components/_viz_editor.scss @@ -23,7 +23,7 @@ padding-top: $spacer; display: flex; flex-direction: column; - overflow: hidden; + overflow: auto; flex-grow: 1; } From 4e089229fb4c134a918b42a10414882661f35727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 16 Jul 2018 14:56:40 +0200 Subject: [PATCH 25/54] minor fix for legacy panels --- public/app/features/dashboard/dashgrid/PanelHeader.tsx | 1 + public/app/features/panel/panel_directive.ts | 2 +- public/sass/pages/_dashboard.scss | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/public/app/features/dashboard/dashgrid/PanelHeader.tsx b/public/app/features/dashboard/dashgrid/PanelHeader.tsx index c4c169ceb88..8c3dc6a324e 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader.tsx @@ -26,6 +26,7 @@ export class PanelHeader extends React.Component { { panelId: this.props.panel.id, fullscreen: true, + edit: null, }, false ); diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index 67cad9c5594..f4ed06007a5 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/public/app/features/panel/panel_directive.ts @@ -6,7 +6,7 @@ import baron from 'baron'; var module = angular.module('grafana.directives'); var panelTemplate = ` -
+
diff --git a/public/sass/pages/_dashboard.scss b/public/sass/pages/_dashboard.scss index dad8bfa1c49..ba4fa93b37b 100644 --- a/public/sass/pages/_dashboard.scss +++ b/public/sass/pages/_dashboard.scss @@ -62,6 +62,10 @@ div.flot-text { opacity: 1; } } + + &--is-editing { + height: auto; + } } .panel-content { From 1c9781627523a4005cdf61be684c72f7c81f7364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 24 Aug 2018 18:46:17 +0200 Subject: [PATCH 26/54] wip: trying to align react & angular edit modes --- public/app/core/directives/dash_class.ts | 4 +- .../dashboard/dashgrid/DashboardGrid.tsx | 2 +- .../dashboard/dashgrid/PanelHeader.tsx | 2 +- public/app/features/panel/panel_directive.ts | 82 ++++++++++--------- public/app/stores/ViewStore/ViewStore.ts | 2 + 5 files changed, 51 insertions(+), 41 deletions(-) diff --git a/public/app/core/directives/dash_class.ts b/public/app/core/directives/dash_class.ts index c164acf7bfc..53d5a83a256 100644 --- a/public/app/core/directives/dash_class.ts +++ b/public/app/core/directives/dash_class.ts @@ -5,7 +5,9 @@ coreModule.directive('dashClass', function($timeout) { return { link: function($scope, elem) { $scope.ctrl.dashboard.events.on('view-mode-changed', function(panel) { - elem.toggleClass('panel-in-fullscreen', panel.fullscreen === true); + $timeout(() => { + elem.toggleClass('panel-in-fullscreen', panel.fullscreen === true); + }, 10); }); elem.toggleClass('panel-in-fullscreen', $scope.ctrl.dashboard.meta.fullscreen === true); diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx index 77b55a2130f..3b2615787ea 100644 --- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx @@ -165,7 +165,7 @@ export class DashboardGrid extends React.Component { componentDidMount() { setTimeout(() => { - this.setState({ animated: true }); + this.setState({ animated: false }); }); } diff --git a/public/app/features/dashboard/dashgrid/PanelHeader.tsx b/public/app/features/dashboard/dashgrid/PanelHeader.tsx index 8c3dc6a324e..d8fd5f9aa89 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader.tsx @@ -26,7 +26,7 @@ export class PanelHeader extends React.Component { { panelId: this.props.panel.id, fullscreen: true, - edit: null, + edit: false, }, false ); diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index f4ed06007a5..1d256927a8a 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/public/app/features/panel/panel_directive.ts @@ -6,48 +6,53 @@ import baron from 'baron'; var module = angular.module('grafana.directives'); var panelTemplate = ` -
-
- - - - +
+
+
+
+ + + + - - - + + + - -
+ +
-
- -
-
- -
-
-
-

- {{ctrl.pluginName}} -

- - - - +
+ +
+
-
-
- +
+
+
+

+ {{ctrl.pluginName}} +

+ + + + +
+ +
+
+ +
@@ -86,7 +91,7 @@ module.directive('grafanaPanel', function($rootScope, $document, $timeout) { } function panelHeightUpdated() { - panelContent.css({ height: ctrl.height + 'px' }); + // panelContent.css({ height: ctrl.height + 'px' }); } function resizeScrollableContent() { @@ -135,6 +140,7 @@ module.directive('grafanaPanel', function($rootScope, $document, $timeout) { ctrl.calculatePanelHeight(); panelHeightUpdated(); $timeout(() => { + console.log('panel directive panel size changed, render'); resizeScrollableContent(); ctrl.render(); }); diff --git a/public/app/stores/ViewStore/ViewStore.ts b/public/app/stores/ViewStore/ViewStore.ts index 83cb01d4bd4..6529cc954d9 100644 --- a/public/app/stores/ViewStore/ViewStore.ts +++ b/public/app/stores/ViewStore/ViewStore.ts @@ -30,6 +30,8 @@ export const ViewStore = types for (let key of Object.keys(query)) { if (query[key]) { self.query.set(key, query[key]); + } else { + self.query.delete(key); } } } From 91b343403c28caa03b4a74afb23c9bbf72b88bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sat, 25 Aug 2018 07:21:00 -0700 Subject: [PATCH 27/54] wip: minor fixes --- public/app/core/components/scroll/scroll.ts | 1 + public/app/core/directives/dash_class.ts | 4 +--- public/app/features/dashboard/dashgrid/DashboardGrid.tsx | 2 +- public/sass/components/_dashboard_grid.scss | 5 +++++ 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/public/app/core/components/scroll/scroll.ts b/public/app/core/components/scroll/scroll.ts index 3f9865e6dce..b45f7ca8bd7 100644 --- a/public/app/core/components/scroll/scroll.ts +++ b/public/app/core/components/scroll/scroll.ts @@ -19,6 +19,7 @@ export function geminiScrollbar() { let scrollRoot = elem.parent(); let scroller = elem; + console.log('scroll'); if (attrs.grafanaScrollbar && attrs.grafanaScrollbar === 'scrollonroot') { scrollRoot = scroller; } diff --git a/public/app/core/directives/dash_class.ts b/public/app/core/directives/dash_class.ts index 53d5a83a256..c164acf7bfc 100644 --- a/public/app/core/directives/dash_class.ts +++ b/public/app/core/directives/dash_class.ts @@ -5,9 +5,7 @@ coreModule.directive('dashClass', function($timeout) { return { link: function($scope, elem) { $scope.ctrl.dashboard.events.on('view-mode-changed', function(panel) { - $timeout(() => { - elem.toggleClass('panel-in-fullscreen', panel.fullscreen === true); - }, 10); + elem.toggleClass('panel-in-fullscreen', panel.fullscreen === true); }); elem.toggleClass('panel-in-fullscreen', $scope.ctrl.dashboard.meta.fullscreen === true); diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx index 3b2615787ea..77b55a2130f 100644 --- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx @@ -165,7 +165,7 @@ export class DashboardGrid extends React.Component { componentDidMount() { setTimeout(() => { - this.setState({ animated: false }); + this.setState({ animated: true }); }); } diff --git a/public/sass/components/_dashboard_grid.scss b/public/sass/components/_dashboard_grid.scss index 26326013dab..da1f140d252 100644 --- a/public/sass/components/_dashboard_grid.scss +++ b/public/sass/components/_dashboard_grid.scss @@ -31,6 +31,11 @@ .react-resizable-handle { display: none; } + + // the react-grid has a height transition + .react-grid-layout { + transition-property: none; + } } @include media-breakpoint-down(sm) { From 4424bdd1b1fd4a2ed50cc0ec0fb138f5bdb7d6b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sat, 25 Aug 2018 07:37:37 -0700 Subject: [PATCH 28/54] fix: going from fullscreen fix --- public/app/core/directives/dash_class.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/public/app/core/directives/dash_class.ts b/public/app/core/directives/dash_class.ts index c164acf7bfc..2a8db641f3f 100644 --- a/public/app/core/directives/dash_class.ts +++ b/public/app/core/directives/dash_class.ts @@ -5,7 +5,13 @@ coreModule.directive('dashClass', function($timeout) { return { link: function($scope, elem) { $scope.ctrl.dashboard.events.on('view-mode-changed', function(panel) { - elem.toggleClass('panel-in-fullscreen', panel.fullscreen === true); + if (panel.fullscreen) { + elem.addClass('panel-in-fullscreen'); + } else { + $timeout(() => { + elem.removeClass('panel-in-fullscreen'); + }); + } }); elem.toggleClass('panel-in-fullscreen', $scope.ctrl.dashboard.meta.fullscreen === true); From 6ba8f6c5ab6e4c42eb8b6ce066d14e603a8e2fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sat, 25 Aug 2018 08:49:39 -0700 Subject: [PATCH 29/54] wip: major change for refresh and render events flow --- .../app/features/dashboard/dashboard_model.ts | 28 ++++++++++++++++++ public/app/features/dashboard/panel_model.ts | 19 ++++++++++++ public/app/features/dashboard/time_srv.ts | 3 +- .../dashboard/timepicker/timepicker.ts | 3 +- .../app/features/dashboard/view_state_srv.ts | 29 +++++++++---------- public/app/features/panel/panel_ctrl.ts | 6 ++-- public/app/features/panel/panel_directive.ts | 1 + 7 files changed, 66 insertions(+), 23 deletions(-) diff --git a/public/app/features/dashboard/dashboard_model.ts b/public/app/features/dashboard/dashboard_model.ts index 92392fc80e8..af665cd8dc3 100644 --- a/public/app/features/dashboard/dashboard_model.ts +++ b/public/app/features/dashboard/dashboard_model.ts @@ -200,6 +200,34 @@ export class DashboardModel { this.events.emit('view-mode-changed', panel); } + startRefresh() { + this.events.emit('refresh'); + + for (const panel of this.panels) { + if (!this.otherPanelInFullscreen(panel)) { + panel.refresh(); + } + } + } + + render() { + this.events.emit('render'); + + for (const panel of this.panels) { + panel.render(); + } + } + + panelInitialized(panel: PanelModel) { + if (!this.otherPanelInFullscreen(panel)) { + panel.refresh(); + } + } + + otherPanelInFullscreen(panel: PanelModel) { + return this.meta.fullscreen && !panel.fullscreen; + } + private ensureListExist(data) { if (!data) { data = {}; diff --git a/public/app/features/dashboard/panel_model.ts b/public/app/features/dashboard/panel_model.ts index 8c9270ad1ab..0abfaa06945 100644 --- a/public/app/features/dashboard/panel_model.ts +++ b/public/app/features/dashboard/panel_model.ts @@ -13,6 +13,7 @@ const notPersistedProperties: { [str: string]: boolean } = { events: true, fullscreen: true, isEditing: true, + hasRefreshed: true, }; export class PanelModel { @@ -37,6 +38,7 @@ export class PanelModel { // non persisted fullscreen: boolean; isEditing: boolean; + hasRefreshed: boolean; events: Emitter; constructor(model) { @@ -93,6 +95,23 @@ export class PanelModel { this.events.emit('panel-size-changed'); } + refresh() { + this.hasRefreshed = true; + this.events.emit('refresh'); + } + + render() { + if (!this.hasRefreshed) { + this.refresh(); + } else { + this.events.emit('render'); + } + } + + panelInitialized() { + this.events.emit('panel-initialized'); + } + initEditMode() { this.events.emit('panel-init-edit-mode'); } diff --git a/public/app/features/dashboard/time_srv.ts b/public/app/features/dashboard/time_srv.ts index 7fd5aed7847..85eaaf6a714 100644 --- a/public/app/features/dashboard/time_srv.ts +++ b/public/app/features/dashboard/time_srv.ts @@ -24,7 +24,6 @@ export class TimeSrv { document.addEventListener('visibilitychange', () => { if (this.autoRefreshBlocked && document.visibilityState === 'visible') { this.autoRefreshBlocked = false; - this.refreshDashboard(); } }); @@ -136,7 +135,7 @@ export class TimeSrv { } refreshDashboard() { - this.$rootScope.$broadcast('refresh'); + this.dashboard.startRefresh(); } private startNextRefreshTimer(afterMs) { diff --git a/public/app/features/dashboard/timepicker/timepicker.ts b/public/app/features/dashboard/timepicker/timepicker.ts index 33cfff92e7f..ce8c4130973 100644 --- a/public/app/features/dashboard/timepicker/timepicker.ts +++ b/public/app/features/dashboard/timepicker/timepicker.ts @@ -30,9 +30,10 @@ export class TimePickerCtrl { $rootScope.onAppEvent('shift-time-forward', () => this.move(1), $scope); $rootScope.onAppEvent('shift-time-backward', () => this.move(-1), $scope); - $rootScope.onAppEvent('refresh', this.onRefresh.bind(this), $scope); $rootScope.onAppEvent('closeTimepicker', this.openDropdown.bind(this), $scope); + this.dashboard.on('refresh', this.onRefresh.bind(this), $scope); + // init options this.panel = this.dashboard.timepicker; _.defaults(this.panel, TimePickerCtrl.defaults); diff --git a/public/app/features/dashboard/view_state_srv.ts b/public/app/features/dashboard/view_state_srv.ts index 73ec8fc0638..56b424c4afb 100644 --- a/public/app/features/dashboard/view_state_srv.ts +++ b/public/app/features/dashboard/view_state_srv.ts @@ -1,6 +1,7 @@ import angular from 'angular'; import _ from 'lodash'; import config from 'app/core/config'; +import appEvents from 'app/core/app_events'; import { DashboardModel } from './dashboard_model'; // represents the transient view state @@ -132,7 +133,7 @@ export class DashboardViewState { if (this.fullscreenPanel === panel && this.editStateChanged === false) { return; } else { - this.leaveFullscreen(false); + this.leaveFullscreen(); } } @@ -140,30 +141,26 @@ export class DashboardViewState { this.enterFullscreen(panel); } } else if (this.fullscreenPanel) { - this.leaveFullscreen(true); + this.leaveFullscreen(); } } - leaveFullscreen(render) { - var panel = this.fullscreenPanel; + leaveFullscreen() { + const panel = this.fullscreenPanel; this.dashboard.setViewMode(panel, false, false); - this.$scope.appEvent('dash-scroll', { restore: true }); - if (!render) { - return false; - } + delete this.fullscreenPanel; this.$timeout(() => { - if (this.oldTimeRange !== this.dashboard.time) { - this.$rootScope.$broadcast('refresh'); - } else { - this.$rootScope.$broadcast('render'); - } - delete this.fullscreenPanel; - }); + appEvents.emit('dash-scroll', { restore: true }); - return true; + if (this.oldTimeRange !== this.dashboard.time) { + this.dashboard.startRefresh(); + } else { + this.dashboard.render(); + } + }); } enterFullscreen(panel) { diff --git a/public/app/features/panel/panel_ctrl.ts b/public/app/features/panel/panel_ctrl.ts index 8f79a789e76..686072e3bcd 100644 --- a/public/app/features/panel/panel_ctrl.ts +++ b/public/app/features/panel/panel_ctrl.ts @@ -47,7 +47,6 @@ export class PanelCtrl { this.pluginName = plugin.name; } - $scope.$on('refresh', () => this.refresh()); $scope.$on('component-did-mount', () => this.panelDidMount()); $scope.$on('$destroy', () => { @@ -57,8 +56,7 @@ export class PanelCtrl { } init() { - this.events.emit('panel-initialized'); - this.publishAppEvent('panel-initialized', { scope: this.$scope }); + this.dashboard.panelInitialized(this.panel); } panelDidMount() { @@ -70,7 +68,7 @@ export class PanelCtrl { } refresh() { - this.events.emit('refresh', null); + this.panel.refresh(); } publishAppEvent(evtName, evt) { diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index 1d256927a8a..ea78c61f847 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/public/app/features/panel/panel_directive.ts @@ -151,6 +151,7 @@ module.directive('grafanaPanel', function($rootScope, $document, $timeout) { panelHeightUpdated(); ctrl.events.on('render', () => { + console.log('panel_directive: render', ctrl.panel.id); if (transparentLastState !== ctrl.panel.transparent) { panelContainer.toggleClass('panel-transparent', ctrl.panel.transparent === true); transparentLastState = ctrl.panel.transparent; From fd81f895091241aa27b004f8fa8299aab1c48900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sat, 25 Aug 2018 12:22:50 -0700 Subject: [PATCH 30/54] wip: angular panels now have similar edit mode and panel type selection enabling quick changing between panel react and angular panel types --- .../core/services/dynamic_directive_srv.ts | 30 +++++------- .../app/features/dashboard/dashboard_model.ts | 5 ++ .../dashboard/dashgrid/DashboardGrid.tsx | 3 +- .../dashboard/dashgrid/DashboardPanel.tsx | 15 +++++- .../dashboard/dashgrid/PanelEditor.tsx | 7 ++- public/app/features/dashboard/panel_model.ts | 9 ++-- .../app/features/dashboard/view_state_srv.ts | 6 +-- .../app/features/panel/metrics_panel_ctrl.ts | 3 -- public/app/features/panel/panel_ctrl.ts | 9 +++- public/app/features/panel/panel_directive.ts | 6 +-- public/app/features/panel/panel_editor_tab.ts | 30 +++++++----- public/app/features/panel/viz_tab.ts | 48 +++++++++++++++++++ .../app/features/plugins/plugin_component.ts | 1 + 13 files changed, 121 insertions(+), 51 deletions(-) create mode 100644 public/app/features/panel/viz_tab.ts diff --git a/public/app/core/services/dynamic_directive_srv.ts b/public/app/core/services/dynamic_directive_srv.ts index 086843b6f9a..7757c564da5 100644 --- a/public/app/core/services/dynamic_directive_srv.ts +++ b/public/app/core/services/dynamic_directive_srv.ts @@ -3,7 +3,7 @@ import coreModule from '../core_module'; class DynamicDirectiveSrv { /** @ngInject */ - constructor(private $compile, private $rootScope) {} + constructor(private $compile) {} addDirective(element, name, scope) { var child = angular.element(document.createElement(name)); @@ -14,25 +14,19 @@ class DynamicDirectiveSrv { } link(scope, elem, attrs, options) { - options - .directive(scope) - .then(directiveInfo => { - if (!directiveInfo || !directiveInfo.fn) { - elem.empty(); - return; - } + const directiveInfo = options.directive(scope); + if (!directiveInfo || !directiveInfo.fn) { + elem.empty(); + return; + } - if (!directiveInfo.fn.registered) { - coreModule.directive(attrs.$normalize(directiveInfo.name), directiveInfo.fn); - directiveInfo.fn.registered = true; - } + if (!directiveInfo.fn.registered) { + console.log('register panel tab'); + coreModule.directive(attrs.$normalize(directiveInfo.name), directiveInfo.fn); + directiveInfo.fn.registered = true; + } - this.addDirective(elem, directiveInfo.name, scope); - }) - .catch(err => { - console.log('Plugin load:', err); - this.$rootScope.appEvent('alert-error', ['Plugin error', err.toString()]); - }); + this.addDirective(elem, directiveInfo.name, scope); } create(options) { diff --git a/public/app/features/dashboard/dashboard_model.ts b/public/app/features/dashboard/dashboard_model.ts index af665cd8dc3..d1c52f168be 100644 --- a/public/app/features/dashboard/dashboard_model.ts +++ b/public/app/features/dashboard/dashboard_model.ts @@ -228,6 +228,11 @@ export class DashboardModel { return this.meta.fullscreen && !panel.fullscreen; } + changePanelType(panel: PanelModel, pluginId: string) { + panel.changeType(pluginId); + this.events.emit('panel-type-changed', panel); + } + private ensureListExist(data) { if (!data) { data = {}; diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx index 77b55a2130f..6f9f3a83ef9 100644 --- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx @@ -84,6 +84,7 @@ export class DashboardGrid extends React.Component { dashboard.on('view-mode-changed', this.onViewModeChanged.bind(this)); dashboard.on('row-collapsed', this.triggerForceUpdate.bind(this)); dashboard.on('row-expanded', this.triggerForceUpdate.bind(this)); + dashboard.on('panel-type-changed', this.triggerForceUpdate.bind(this)); } buildLayout() { @@ -177,7 +178,7 @@ export class DashboardGrid extends React.Component { const panelClasses = classNames({ panel: true, 'panel--fullscreen': panel.fullscreen }); panelElements.push(
- +
); } diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx index 4b2e81b969c..6958b43b05d 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -11,6 +11,7 @@ import { PanelChrome } from './PanelChrome'; import { PanelEditor } from './PanelEditor'; export interface Props { + panelType: string; panel: PanelModel; dashboard: DashboardModel; } @@ -53,6 +54,10 @@ export class DashboardPanel extends React.Component { this.loadPlugin(); }; + onAngularPluginTypeChanged = () => { + this.loadPlugin(); + }; + loadPlugin() { if (this.isSpecial()) { return; @@ -63,9 +68,11 @@ export class DashboardPanel extends React.Component { this.pluginInfo = config.panels[this.props.panel.type]; if (this.pluginInfo.exports) { + this.cleanUpAngularPanel(); this.setState({ pluginExports: this.pluginInfo.exports }); } else { importPluginModule(this.pluginInfo.module).then(pluginExports => { + this.cleanUpAngularPanel(); // cache plugin exports (saves a promise async cycle next time) this.pluginInfo.exports = pluginExports; // update panel state @@ -80,7 +87,6 @@ export class DashboardPanel extends React.Component { } componentDidUpdate() { - console.log('componentDidUpdate'); this.loadPlugin(); // handle angular plugin loading @@ -94,12 +100,17 @@ export class DashboardPanel extends React.Component { this.angularPanel = loader.load(this.element, scopeProps, template); } - componentWillUnmount() { + cleanUpAngularPanel() { if (this.angularPanel) { this.angularPanel.destroy(); + this.angularPanel = null; } } + componentWillUnmount() { + this.cleanUpAngularPanel(); + } + renderReactPanel() { const { pluginExports } = this.state; const containerClass = this.props.panel.isEditing ? 'panel-editor-container' : 'panel-height-helper'; diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index 70b5bf11815..a9b7802fb4b 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -31,7 +31,7 @@ export class PanelEditor extends React.Component { this.tabs = [ { id: 'queries', text: 'Queries', icon: 'fa fa-database' }, - { id: 'viz', text: 'Visualization', icon: 'fa fa-line-chart' }, + { id: 'visualization', text: 'Visualization', icon: 'fa fa-line-chart' }, ]; } @@ -87,7 +87,7 @@ export class PanelEditor extends React.Component {
{activeTab === 'queries' && this.renderQueriesTab()} - {activeTab === 'viz' && this.renderVizTab()} + {activeTab === 'visualization' && this.renderVizTab()}
); @@ -109,8 +109,7 @@ function TabItem({ tab, activeTab, onClick }: TabItemParams) { return (
  • onClick(tab)}> - - {tab.text} + {tab.text}
  • ); diff --git a/public/app/features/dashboard/panel_model.ts b/public/app/features/dashboard/panel_model.ts index 0abfaa06945..6192047c84e 100644 --- a/public/app/features/dashboard/panel_model.ts +++ b/public/app/features/dashboard/panel_model.ts @@ -34,6 +34,7 @@ export class PanelModel { soloMode?: boolean; targets: any[]; datasource: string; + thresholds?: any; // non persisted fullscreen: boolean; @@ -116,9 +117,11 @@ export class PanelModel { this.events.emit('panel-init-edit-mode'); } - changeType(newType: string) { - this.type = newType; - this.events.emit('panel-size-changed'); + changeType(pluginId: string) { + this.type = pluginId; + + delete this.thresholds; + delete this.alert; } destroy() { diff --git a/public/app/features/dashboard/view_state_srv.ts b/public/app/features/dashboard/view_state_srv.ts index 56b424c4afb..266fdb16d13 100644 --- a/public/app/features/dashboard/view_state_srv.ts +++ b/public/app/features/dashboard/view_state_srv.ts @@ -16,7 +16,7 @@ export class DashboardViewState { oldTimeRange: any; /** @ngInject */ - constructor($scope, private $location, private $timeout, private $rootScope) { + constructor($scope, private $location, private $timeout) { var self = this; self.state = {}; self.panelScopes = []; @@ -176,10 +176,10 @@ export class DashboardViewState { } /** @ngInject */ -export function dashboardViewStateSrv($location, $timeout, $rootScope) { +export function dashboardViewStateSrv($location, $timeout) { return { create: function($scope) { - return new DashboardViewState($scope, $location, $timeout, $rootScope); + return new DashboardViewState($scope, $location, $timeout); }, }; } diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts index 432ca847a33..9abda3e8b4d 100644 --- a/public/app/features/panel/metrics_panel_ctrl.ts +++ b/public/app/features/panel/metrics_panel_ctrl.ts @@ -8,8 +8,6 @@ import * as rangeUtil from 'app/core/utils/rangeutil'; import * as dateMath from 'app/core/utils/datemath'; import { encodePathComponent } from 'app/core/utils/location_util'; -import { metricsTabDirective } from './metrics_tab'; - class MetricsPanelCtrl extends PanelCtrl { scope: any; datasource: any; @@ -58,7 +56,6 @@ class MetricsPanelCtrl extends PanelCtrl { } private onInitMetricsPanelEditMode() { - this.addEditorTab('Metrics', metricsTabDirective); this.addEditorTab('Time range', 'public/app/features/panel/partials/panelTime.html'); } diff --git a/public/app/features/panel/panel_ctrl.ts b/public/app/features/panel/panel_ctrl.ts index 686072e3bcd..4b57c15dfd5 100644 --- a/public/app/features/panel/panel_ctrl.ts +++ b/public/app/features/panel/panel_ctrl.ts @@ -6,6 +6,8 @@ import { PanelModel } from 'app/features/dashboard/panel_model'; import Remarkable from 'remarkable'; import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, LS_PANEL_COPY_KEY } from 'app/core/constants'; import store from 'app/core/store'; +import { metricsTabDirective } from './metrics_tab'; +import { vizTabDirective } from './viz_tab'; const TITLE_HEIGHT = 27; const PANEL_BORDER = 2; @@ -97,7 +99,10 @@ export class PanelCtrl { initEditMode() { this.editorTabs = []; + this.addEditorTab('Queries', metricsTabDirective, 0, 'fa fa-database'); + this.addEditorTab('Visualization', vizTabDirective, 1, 'fa fa-line-chart'); this.addEditorTab('General', 'public/app/partials/panelgeneral.html'); + this.editModeInitiated = true; this.events.emit('init-edit-mode', null); @@ -118,8 +123,8 @@ export class PanelCtrl { route.updateParams(); } - addEditorTab(title, directiveFn, index?) { - var editorTab = { title, directiveFn }; + addEditorTab(title, directiveFn, index?, icon?) { + var editorTab = { title, directiveFn, icon }; if (_.isString(directiveFn)) { editorTab.directiveFn = function() { diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index ea78c61f847..191396bf97d 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/public/app/features/panel/panel_directive.ts @@ -32,13 +32,11 @@ var panelTemplate = ` 'panel-height-helper': !ctrl.panel.isEditing}">
    -

    - {{ctrl.pluginName}} -

    -
    +
    diff --git a/public/app/features/panel/panel_editor_tab.ts b/public/app/features/panel/panel_editor_tab.ts index 89eaba88299..dd7f5fdce5f 100644 --- a/public/app/features/panel/panel_editor_tab.ts +++ b/public/app/features/panel/panel_editor_tab.ts @@ -1,6 +1,7 @@ import angular from 'angular'; -var directiveModule = angular.module('grafana.directives'); +const directiveModule = angular.module('grafana.directives'); +const directiveCache = {}; /** @ngInject */ function panelEditorTab(dynamicDirectiveSrv) { @@ -11,18 +12,25 @@ function panelEditorTab(dynamicDirectiveSrv) { index: '=', }, directive: scope => { - var pluginId = scope.ctrl.pluginId; - var tabIndex = scope.index; - // create a wrapper for directiveFn - // required for metrics tab directive - // that is the same for many panels but - // given different names in this function - var fn = () => scope.editorTab.directiveFn(); + const pluginId = scope.ctrl.pluginId; + const tabIndex = scope.index; - return Promise.resolve({ + if (directiveCache[pluginId]) { + if (directiveCache[pluginId][tabIndex]) { + return directiveCache[pluginId][tabIndex]; + } + } else { + directiveCache[pluginId] = []; + } + + let result = { + fn: () => scope.editorTab.directiveFn(), name: `panel-editor-tab-${pluginId}${tabIndex}`, - fn: fn, - }); + }; + + directiveCache[pluginId][tabIndex] = result; + + return result; }, }); } diff --git a/public/app/features/panel/viz_tab.ts b/public/app/features/panel/viz_tab.ts new file mode 100644 index 00000000000..db3c7921475 --- /dev/null +++ b/public/app/features/panel/viz_tab.ts @@ -0,0 +1,48 @@ +import coreModule from 'app/core/core_module'; +import { DashboardModel } from '../dashboard/dashboard_model'; +import { VizTypePicker } from '../dashboard/dashgrid/VizTypePicker'; +import { react2AngularDirective } from 'app/core/utils/react2angular'; +import { PanelPlugin } from 'app/types/plugins'; + +export class VizTabCtrl { + panelCtrl: any; + dashboard: DashboardModel; + + /** @ngInject */ + constructor($scope) { + this.panelCtrl = $scope.ctrl; + this.dashboard = this.panelCtrl.dashboard; + + $scope.ctrl = this; + } + + onTypeChanged = (plugin: PanelPlugin) => { + this.dashboard.changePanelType(this.panelCtrl.panel, plugin.id); + }; +} + +let template = ` +
    +
    + +
    +
    +
    Options
    +
    +
    +`; + +/** @ngInject **/ +export function vizTabDirective() { + 'use strict'; + return { + restrict: 'E', + scope: true, + template: template, + controller: VizTabCtrl, + }; +} + +react2AngularDirective('vizTypePicker', VizTypePicker, ['currentType', ['onTypeChanged', { watchDepth: 'reference' }]]); + +coreModule.directive('vizTab', vizTabDirective); diff --git a/public/app/features/plugins/plugin_component.ts b/public/app/features/plugins/plugin_component.ts index 5ef4019c24d..5375d09d2f5 100644 --- a/public/app/features/plugins/plugin_component.ts +++ b/public/app/features/plugins/plugin_component.ts @@ -211,6 +211,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $ scope.$applyAsync(function() { scope.$broadcast('component-did-mount'); scope.$broadcast('refresh'); + console.log('appendAndCompile', scope.panel); }); }); } From 864c4691da82ba1b62854a821d312c5581365565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sat, 25 Aug 2018 12:38:25 -0700 Subject: [PATCH 31/54] fix: minor fix to changing type --- public/app/features/plugins/plugin_component.ts | 1 - public/sass/components/_tabbed_view.scss | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/public/app/features/plugins/plugin_component.ts b/public/app/features/plugins/plugin_component.ts index 5375d09d2f5..aafffc77fa0 100644 --- a/public/app/features/plugins/plugin_component.ts +++ b/public/app/features/plugins/plugin_component.ts @@ -246,7 +246,6 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $ registerPluginComponent(scope, elem, attrs, componentInfo); }) .catch(err => { - $rootScope.appEvent('alert-error', ['Plugin Error', err.message || err]); console.log('Plugin component error', err); }); }, diff --git a/public/sass/components/_tabbed_view.scss b/public/sass/components/_tabbed_view.scss index 80e76b5fbf4..87b43a31142 100644 --- a/public/sass/components/_tabbed_view.scss +++ b/public/sass/components/_tabbed_view.scss @@ -4,7 +4,7 @@ height: 100%; &.tabbed-view--new { - padding: 10px 0 0 0; + padding: 25px 0 0 0; height: 100%; } } From df822a660b129a459d92a9e6f9d30b2224732331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 10 Sep 2018 16:19:28 +0200 Subject: [PATCH 32/54] initial render/refresh timing issues --- public/app/features/panel/metrics_panel_ctrl.ts | 1 + public/app/features/panel/panel_ctrl.ts | 6 ++---- public/app/features/plugins/plugin_component.ts | 3 +-- public/app/plugins/panel/graph/graph.ts | 1 + 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts index e8f3647f09c..ce65d11b3a1 100644 --- a/public/app/features/panel/metrics_panel_ctrl.ts +++ b/public/app/features/panel/metrics_panel_ctrl.ts @@ -60,6 +60,7 @@ class MetricsPanelCtrl extends PanelCtrl { } private onMetricsPanelRefresh() { + console.log('metrics_panel_ctrl:onRefresh'); // 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 96fcbe8067b..0de55e33ccc 100644 --- a/public/app/features/panel/panel_ctrl.ts +++ b/public/app/features/panel/panel_ctrl.ts @@ -57,12 +57,9 @@ export class PanelCtrl { }); } - init() { - this.dashboard.panelInitialized(this.panel); - } - panelDidMount() { this.events.emit('component-did-mount'); + this.dashboard.panelInitialized(this.panel); } renderingCompleted() { @@ -248,6 +245,7 @@ export class PanelCtrl { } render(payload?) { + console.log('panel_ctrl:render'); this.timing.renderStart = new Date().getTime(); this.events.emit('render', payload); } diff --git a/public/app/features/plugins/plugin_component.ts b/public/app/features/plugins/plugin_component.ts index f6df579dc09..47f2fc535e8 100644 --- a/public/app/features/plugins/plugin_component.ts +++ b/public/app/features/plugins/plugin_component.ts @@ -209,9 +209,8 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $ setTimeout(() => { elem.append(child); scope.$applyAsync(() => { + console.log('post appendAndCompile, broadcast refresh', scope.panel); scope.$broadcast('component-did-mount'); - scope.$broadcast('refresh'); - console.log('appendAndCompile', scope.panel); }); }); } diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 33db0e7220a..3a27c20e37c 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -80,6 +80,7 @@ class GraphElement { this.annotations = this.ctrl.annotations || []; this.buildFlotPairs(this.data); const graphHeight = this.elem.height(); + console.log('graphHeight', graphHeight); updateLegendValues(this.data, this.panel, graphHeight); this.ctrl.events.emit('render-legend'); From c82bf7f67fab093ea730044e45b2a8ac4b10eb4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 10 Sep 2018 17:55:06 +0200 Subject: [PATCH 33/54] fix: changing edit / view fullscreen modes now work --- public/app/core/directives/dash_class.ts | 1 + .../dashboard/dashgrid/VizTypePicker.tsx | 19 +++++++++++++------ .../app/features/dashboard/view_state_srv.ts | 16 +++------------- public/sass/components/_scrollbar.scss | 5 +++++ public/sass/components/_viz_editor.scss | 15 +++++++++------ 5 files changed, 31 insertions(+), 25 deletions(-) diff --git a/public/app/core/directives/dash_class.ts b/public/app/core/directives/dash_class.ts index 645f32de645..37124eb7d4b 100644 --- a/public/app/core/directives/dash_class.ts +++ b/public/app/core/directives/dash_class.ts @@ -6,6 +6,7 @@ function dashClass($timeout) { return { link: ($scope, elem) => { $scope.ctrl.dashboard.events.on('view-mode-changed', panel => { + console.log('view-mode-changed', panel.fullscreen); if (panel.fullscreen) { elem.addClass('panel-in-fullscreen'); } else { diff --git a/public/app/features/dashboard/dashgrid/VizTypePicker.tsx b/public/app/features/dashboard/dashgrid/VizTypePicker.tsx index 8f5690f31d5..9402133df34 100644 --- a/public/app/features/dashboard/dashgrid/VizTypePicker.tsx +++ b/public/app/features/dashboard/dashgrid/VizTypePicker.tsx @@ -2,6 +2,7 @@ import React, { PureComponent } from 'react'; import classNames from 'classnames'; import config from 'app/core/config'; import { PanelPlugin } from 'app/types/plugins'; +import CustomScrollbar from 'app/core/components/CustomScrollbar/CustomScrollbar'; import _ from 'lodash'; interface Props { @@ -49,13 +50,19 @@ export class VizTypePicker extends PureComponent { render() { return (
    -
    - +
    +
    + +
    +
    +
    + +
    {this.state.pluginList.map(this.renderVizPlugin)}
    +
    -
    {this.state.pluginList.map(this.renderVizPlugin)}
    ); } diff --git a/public/app/features/dashboard/view_state_srv.ts b/public/app/features/dashboard/view_state_srv.ts index a13bc88161e..8805050831e 100644 --- a/public/app/features/dashboard/view_state_srv.ts +++ b/public/app/features/dashboard/view_state_srv.ts @@ -11,7 +11,6 @@ export class DashboardViewState { panelScopes: any; $scope: any; dashboard: DashboardModel; - editStateChanged: any; fullscreenPanel: any; oldTimeRange: any; @@ -72,9 +71,6 @@ export class DashboardViewState { } } - // remember if editStateChanged - this.editStateChanged = (state.edit || false) !== (this.state.edit || false); - _.extend(this.state, state); this.dashboard.meta.fullscreen = this.state.fullscreen; @@ -128,17 +124,11 @@ export class DashboardViewState { return; } - if (this.fullscreenPanel) { - // if already fullscreen - if (this.fullscreenPanel === panel && this.editStateChanged === false) { - return; - } else { - this.leaveFullscreen(); - } - } - if (!panel.fullscreen) { this.enterFullscreen(panel); + } else { + // already in fullscreen view just update the view mode + this.dashboard.setViewMode(panel, this.state.fullscreen, this.state.edit); } } else if (this.fullscreenPanel) { this.leaveFullscreen(); diff --git a/public/sass/components/_scrollbar.scss b/public/sass/components/_scrollbar.scss index adb9e0c54c0..00bd5f7c94c 100644 --- a/public/sass/components/_scrollbar.scss +++ b/public/sass/components/_scrollbar.scss @@ -307,6 +307,7 @@ .view { display: flex; flex-grow: 1; + flex-direction: column; } .track-vertical { @@ -337,3 +338,7 @@ border-radius: 6px; } } + +.scroll-margin-helper { + margin-right: 12px; +} diff --git a/public/sass/components/_viz_editor.scss b/public/sass/components/_viz_editor.scss index 3658c869d6d..048e513cfbb 100644 --- a/public/sass/components/_viz_editor.scss +++ b/public/sass/components/_viz_editor.scss @@ -19,12 +19,13 @@ height: 100%; } -.viz-picker-list { - padding-top: $spacer; - display: flex; - flex-direction: column; - overflow: auto; +.viz-picker__search { + flex-grow: 0; +} + +.viz-picker__items { flex-grow: 1; + height: calc(100% - 50px); } .viz-picker__item { @@ -41,13 +42,15 @@ display: flex; flex-shrink: 0; border: 1px solid transparent; + @include left-brand-border; &:hover { background: $card-background-hover; } &--selected { - border: 1px solid $orange; + // border: 1px solid $orange; + @include left-brand-border-gradient(); .viz-picker__item-name { color: $text-color; From 3ceab9484dac87d76cfdc4fd7846458b40a0361f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 10 Sep 2018 19:04:56 +0200 Subject: [PATCH 34/54] wip: moving option tabs into viz tab --- public/app/features/panel/panel_ctrl.ts | 22 +++++++++++++++---- public/app/features/panel/panel_editor_tab.ts | 11 +++++----- public/app/features/panel/viz_tab.ts | 6 +++-- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/public/app/features/panel/panel_ctrl.ts b/public/app/features/panel/panel_ctrl.ts index 0de55e33ccc..82a4a116f89 100644 --- a/public/app/features/panel/panel_ctrl.ts +++ b/public/app/features/panel/panel_ctrl.ts @@ -22,6 +22,7 @@ export class PanelCtrl { pluginName: string; pluginId: string; editorTabs: any; + optionTabs: any; $scope: any; $injector: any; $location: any; @@ -96,9 +97,10 @@ export class PanelCtrl { initEditMode() { this.editorTabs = []; - this.addEditorTab('Queries', metricsTabDirective, 0, 'fa fa-database'); - this.addEditorTab('Visualization', vizTabDirective, 1, 'fa fa-line-chart'); - this.addEditorTab('General', 'public/app/partials/panelgeneral.html'); + this.optionTabs = []; + this.addCommonTab('Queries', metricsTabDirective, 0, 'fa fa-database'); + this.addCommonTab('Visualization', vizTabDirective, 1, 'fa fa-line-chart'); + this.addCommonTab('General', 'public/app/partials/panelgeneral.html'); this.editModeInitiated = true; this.events.emit('init-edit-mode', null); @@ -120,7 +122,7 @@ export class PanelCtrl { route.updateParams(); } - addEditorTab(title, directiveFn, index?, icon?) { + addCommonTab(title, directiveFn, index?, icon?) { const editorTab = { title, directiveFn, icon }; if (_.isString(directiveFn)) { @@ -136,6 +138,18 @@ export class PanelCtrl { } } + addEditorTab(title, directiveFn, index?, icon?) { + const editorTab = { title, directiveFn, icon }; + + if (_.isString(directiveFn)) { + editorTab.directiveFn = () => { + return { templateUrl: directiveFn }; + }; + } + + this.optionTabs.push(editorTab); + } + getMenu() { const menu = []; menu.push({ diff --git a/public/app/features/panel/panel_editor_tab.ts b/public/app/features/panel/panel_editor_tab.ts index fe83a892cc7..d16bab3dc55 100644 --- a/public/app/features/panel/panel_editor_tab.ts +++ b/public/app/features/panel/panel_editor_tab.ts @@ -13,11 +13,12 @@ function panelEditorTab(dynamicDirectiveSrv) { }, directive: scope => { const pluginId = scope.ctrl.pluginId; - const tabIndex = scope.index; + const tabName = scope.editorTab.title.toLowerCase(); + console.log('panelEditorTab', pluginId, tabName); if (directiveCache[pluginId]) { - if (directiveCache[pluginId][tabIndex]) { - return directiveCache[pluginId][tabIndex]; + if (directiveCache[pluginId][tabName]) { + return directiveCache[pluginId][tabName]; } } else { directiveCache[pluginId] = []; @@ -25,10 +26,10 @@ function panelEditorTab(dynamicDirectiveSrv) { const result = { fn: () => scope.editorTab.directiveFn(), - name: `panel-editor-tab-${pluginId}${tabIndex}`, + name: `panel-editor-tab-${pluginId}${tabName}`, }; - directiveCache[pluginId][tabIndex] = result; + directiveCache[pluginId][tabName] = result; return result; }, diff --git a/public/app/features/panel/viz_tab.ts b/public/app/features/panel/viz_tab.ts index 972cf9d43cc..d52511b55da 100644 --- a/public/app/features/panel/viz_tab.ts +++ b/public/app/features/panel/viz_tab.ts @@ -27,7 +27,10 @@ const template = `
    -
    Options
    +
    +
    {{tab.title}}
    + +
    `; @@ -37,7 +40,6 @@ export function vizTabDirective() { 'use strict'; return { restrict: 'E', - scope: true, template: template, controller: VizTabCtrl, }; From 3c7e54461f0278c20129a061f0249ee74a015e1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 18 Sep 2018 15:32:06 +0200 Subject: [PATCH 35/54] updated --- .../dashboard/dashgrid/PanelEditor.tsx | 11 ++++--- .../dashboard/dashgrid/PanelHeader.tsx | 33 ++++++++++--------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index a9b7802fb4b..03cc5350fee 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -2,11 +2,11 @@ import React from 'react'; import classNames from 'classnames'; import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; -import { store } from 'app/stores/store'; -import { observer } from 'mobx-react'; +import { store } from 'app/store/configureStore'; import { QueriesTab } from './QueriesTab'; import { PanelPlugin, PluginExports } from 'app/types/plugins'; import { VizTypePicker } from './VizTypePicker'; +import { updateLocation } from 'app/core/actions'; interface PanelEditorProps { panel: PanelModel; @@ -22,7 +22,6 @@ interface PanelEditorTab { icon: string; } -@observer export class PanelEditor extends React.Component { tabs: PanelEditorTab[]; @@ -65,7 +64,11 @@ export class PanelEditor extends React.Component { } onChangeTab = (tab: PanelEditorTab) => { - store.view.updateQuery({ tab: tab.id }, false); + store.dispatch( + updateLocation({ + query: { tab: tab.id }, + }) + ); }; render() { diff --git a/public/app/features/dashboard/dashgrid/PanelHeader.tsx b/public/app/features/dashboard/dashgrid/PanelHeader.tsx index 02d905a1e68..12d5cd37253 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader.tsx @@ -2,7 +2,8 @@ import React from 'react'; import classNames from 'classnames'; import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; -import { store } from 'app/stores/store'; +import { store } from 'app/store/configureStore'; +import { updateLocation } from 'app/core/actions'; interface PanelHeaderProps { panel: PanelModel; @@ -11,24 +12,26 @@ interface PanelHeaderProps { export class PanelHeader extends React.Component { onEditPanel = () => { - store.view.updateQuery( - { - panelId: this.props.panel.id, - edit: true, - fullscreen: true, - }, - false + store.dispatch( + updateLocation({ + query: { + panelId: this.props.panel.id, + edit: true, + fullscreen: true, + }, + }) ); }; onViewPanel = () => { - store.view.updateQuery( - { - panelId: this.props.panel.id, - fullscreen: true, - edit: false, - }, - false + store.dispatch( + updateLocation({ + query: { + panelId: this.props.panel.id, + edit: false, + fullscreen: true, + }, + }) ); }; From 5b522254502e501912c0f2226a12c47389fb46b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 20 Sep 2018 09:01:06 +0200 Subject: [PATCH 36/54] wip: panel options idea2 --- .../app/features/panel/metrics_panel_ctrl.ts | 2 +- public/app/features/panel/panel_ctrl.ts | 3 +- public/app/features/panel/panel_editor_tab.ts | 3 +- .../features/panel/partials/metrics_tab.html | 4 -- public/app/features/panel/viz_tab.ts | 49 ++++++++++++++----- public/app/plugins/panel/graph/module.ts | 4 +- 6 files changed, 42 insertions(+), 23 deletions(-) diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts index ce65d11b3a1..c7468216881 100644 --- a/public/app/features/panel/metrics_panel_ctrl.ts +++ b/public/app/features/panel/metrics_panel_ctrl.ts @@ -56,7 +56,7 @@ class MetricsPanelCtrl extends PanelCtrl { } private onInitMetricsPanelEditMode() { - this.addEditorTab('Time range', 'public/app/features/panel/partials/panelTime.html'); + // this.addCommonTab('Time range', 'public/app/features/panel/partials/panelTime.html'); } private onMetricsPanelRefresh() { diff --git a/public/app/features/panel/panel_ctrl.ts b/public/app/features/panel/panel_ctrl.ts index 82a4a116f89..fbef69bc42a 100644 --- a/public/app/features/panel/panel_ctrl.ts +++ b/public/app/features/panel/panel_ctrl.ts @@ -100,11 +100,12 @@ export class PanelCtrl { this.optionTabs = []; this.addCommonTab('Queries', metricsTabDirective, 0, 'fa fa-database'); this.addCommonTab('Visualization', vizTabDirective, 1, 'fa fa-line-chart'); - this.addCommonTab('General', 'public/app/partials/panelgeneral.html'); this.editModeInitiated = true; this.events.emit('init-edit-mode', null); + // this.addEditorTab('General', 'public/app/partials/panelgeneral.html'); + const urlTab = (this.$injector.get('$routeParams').tab || '').toLowerCase(); if (urlTab) { this.editorTabs.forEach((tab, i) => { diff --git a/public/app/features/panel/panel_editor_tab.ts b/public/app/features/panel/panel_editor_tab.ts index d16bab3dc55..f7e1c48a323 100644 --- a/public/app/features/panel/panel_editor_tab.ts +++ b/public/app/features/panel/panel_editor_tab.ts @@ -13,9 +13,8 @@ function panelEditorTab(dynamicDirectiveSrv) { }, directive: scope => { const pluginId = scope.ctrl.pluginId; - const tabName = scope.editorTab.title.toLowerCase(); + const tabName = scope.editorTab.title.toLowerCase().replace(' ', '-'); - console.log('panelEditorTab', pluginId, tabName); if (directiveCache[pluginId]) { if (directiveCache[pluginId][tabName]) { return directiveCache[pluginId][tabName]; diff --git a/public/app/features/panel/partials/metrics_tab.html b/public/app/features/panel/partials/metrics_tab.html index 0ee1f81b0c3..815a99d6b74 100644 --- a/public/app/features/panel/partials/metrics_tab.html +++ b/public/app/features/panel/partials/metrics_tab.html @@ -1,11 +1,7 @@
    - - -
    - -
    -
    -
    -
    {{tab.title}}
    - -
    -
    -
    -`; +
    +
    +
    + +
    + +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    `; /** @ngInject */ export function vizTabDirective() { @@ -46,5 +70,4 @@ export function vizTabDirective() { } react2AngularDirective('vizTypePicker', VizTypePicker, ['currentType', ['onTypeChanged', { watchDepth: 'reference' }]]); - coreModule.directive('vizTab', vizTabDirective); diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index f0751ddd816..d9d67f11b6c 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -134,12 +134,12 @@ class GraphCtrl extends MetricsPanelCtrl { } onInitEditMode() { + this.addEditorTab('Display', 'public/app/plugins/panel/graph/tab_display.html', 4); this.addEditorTab('Axes', axesEditorComponent, 2); this.addEditorTab('Legend', 'public/app/plugins/panel/graph/tab_legend.html', 3); - this.addEditorTab('Display', 'public/app/plugins/panel/graph/tab_display.html', 4); if (config.alertingEnabled) { - this.addEditorTab('Alert', alertTab, 5); + this.addCommonTab('Alert', alertTab, 5); } this.subTabIndex = 0; From b7ba73819c1d541f0b8ead9ed18bf35530105045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 4 Oct 2018 09:14:47 +0200 Subject: [PATCH 37/54] mini fix --- public/app/features/dashboard/dashgrid/PanelEditor.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index 03cc5350fee..2a6228217ed 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -72,7 +72,8 @@ export class PanelEditor extends React.Component { }; render() { - const activeTab: string = store.view.query.get('tab') || 'queries'; + const { location } = store.getState(); + const activeTab = location.query.tab || 'queries'; return (
    From 2f84101fe768eb2865b2bea16f943636c71beb4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 7 Oct 2018 10:39:47 -0700 Subject: [PATCH 38/54] wip: restoring old angular panel tabs / edit mode --- .../app/features/panel/metrics_panel_ctrl.ts | 4 +++- public/app/features/panel/panel_ctrl.ts | 23 ++----------------- public/app/features/panel/panel_directive.ts | 4 +++- public/app/plugins/panel/graph/module.ts | 2 +- 4 files changed, 9 insertions(+), 24 deletions(-) diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts index f7a3e22a134..67848e460fb 100644 --- a/public/app/features/panel/metrics_panel_ctrl.ts +++ b/public/app/features/panel/metrics_panel_ctrl.ts @@ -7,6 +7,7 @@ import { PanelCtrl } from 'app/features/panel/panel_ctrl'; import * as rangeUtil from 'app/core/utils/rangeutil'; import * as dateMath from 'app/core/utils/datemath'; import { getExploreUrl } from 'app/core/utils/explore'; +import { metricsTabDirective } from './metrics_tab'; class MetricsPanelCtrl extends PanelCtrl { scope: any; @@ -56,7 +57,8 @@ class MetricsPanelCtrl extends PanelCtrl { } private onInitMetricsPanelEditMode() { - // this.addCommonTab('Time range', 'public/app/features/panel/partials/panelTime.html'); + this.addEditorTab('Queries', metricsTabDirective, 1, 'fa fa-database'); + this.addEditorTab('Time range', 'public/app/features/panel/partials/panelTime.html'); } private onMetricsPanelRefresh() { diff --git a/public/app/features/panel/panel_ctrl.ts b/public/app/features/panel/panel_ctrl.ts index fbef69bc42a..51dd38e4358 100644 --- a/public/app/features/panel/panel_ctrl.ts +++ b/public/app/features/panel/panel_ctrl.ts @@ -6,8 +6,6 @@ import { PanelModel } from 'app/features/dashboard/panel_model'; import Remarkable from 'remarkable'; import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, LS_PANEL_COPY_KEY } from 'app/core/constants'; import store from 'app/core/store'; -import { metricsTabDirective } from './metrics_tab'; -import { vizTabDirective } from './viz_tab'; const TITLE_HEIGHT = 27; const PANEL_BORDER = 2; @@ -22,7 +20,6 @@ export class PanelCtrl { pluginName: string; pluginId: string; editorTabs: any; - optionTabs: any; $scope: any; $injector: any; $location: any; @@ -97,15 +94,11 @@ export class PanelCtrl { initEditMode() { this.editorTabs = []; - this.optionTabs = []; - this.addCommonTab('Queries', metricsTabDirective, 0, 'fa fa-database'); - this.addCommonTab('Visualization', vizTabDirective, 1, 'fa fa-line-chart'); + this.addEditorTab('General', 'public/app/partials/panelgeneral.html'); this.editModeInitiated = true; this.events.emit('init-edit-mode', null); - // this.addEditorTab('General', 'public/app/partials/panelgeneral.html'); - const urlTab = (this.$injector.get('$routeParams').tab || '').toLowerCase(); if (urlTab) { this.editorTabs.forEach((tab, i) => { @@ -123,7 +116,7 @@ export class PanelCtrl { route.updateParams(); } - addCommonTab(title, directiveFn, index?, icon?) { + addEditorTab(title, directiveFn, index?, icon?) { const editorTab = { title, directiveFn, icon }; if (_.isString(directiveFn)) { @@ -139,18 +132,6 @@ export class PanelCtrl { } } - addEditorTab(title, directiveFn, index?, icon?) { - const editorTab = { title, directiveFn, icon }; - - if (_.isString(directiveFn)) { - editorTab.directiveFn = () => { - return { templateUrl: directiveFn }; - }; - } - - this.optionTabs.push(editorTab); - } - getMenu() { const menu = []; menu.push({ diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index 9ce4edd3f52..def7c69a69d 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/public/app/features/panel/panel_directive.ts @@ -32,11 +32,13 @@ const panelTemplate = ` 'panel-height-helper': !ctrl.panel.isEditing}">
    +

    + {{ctrl.pluginName}} +

    • - {{::tab.title}}
    • diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index d9d67f11b6c..584b58ae3ce 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -139,7 +139,7 @@ class GraphCtrl extends MetricsPanelCtrl { this.addEditorTab('Legend', 'public/app/plugins/panel/graph/tab_legend.html', 3); if (config.alertingEnabled) { - this.addCommonTab('Alert', alertTab, 5); + this.addEditorTab('Alert', alertTab, 5); } this.subTabIndex = 0; From d2bdf8cff12e33016704e78331e6cb9dd104caac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 9 Oct 2018 17:07:13 +0200 Subject: [PATCH 39/54] wip: fixed issues now things are starting to work as before for angular panels --- public/app/core/constants.ts | 3 +++ public/app/features/panel/metrics_panel_ctrl.ts | 2 +- public/app/features/panel/panel_ctrl.ts | 4 ++-- public/app/features/panel/panel_directive.ts | 6 ------ public/app/features/plugins/plugin_component.ts | 12 ++++++++---- public/app/partials/dashboard.html | 2 +- public/sass/pages/_dashboard.scss | 4 ++++ 7 files changed, 19 insertions(+), 14 deletions(-) diff --git a/public/app/core/constants.ts b/public/app/core/constants.ts index 2642c5e400a..00981156614 100644 --- a/public/app/core/constants.ts +++ b/public/app/core/constants.ts @@ -8,3 +8,6 @@ export const DEFAULT_ROW_HEIGHT = 250; export const MIN_PANEL_HEIGHT = GRID_CELL_HEIGHT * 3; export const LS_PANEL_COPY_KEY = 'panel-copy'; + +export const DASHBOARD_TOOLBAR_HEIGHT = 55; +export const DASHBOARD_TOP_PADDING = 20; diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts index 67848e460fb..1cdf7c6953d 100644 --- a/public/app/features/panel/metrics_panel_ctrl.ts +++ b/public/app/features/panel/metrics_panel_ctrl.ts @@ -57,7 +57,7 @@ class MetricsPanelCtrl extends PanelCtrl { } private onInitMetricsPanelEditMode() { - this.addEditorTab('Queries', metricsTabDirective, 1, 'fa fa-database'); + this.addEditorTab('Metrics', metricsTabDirective, 1, 'fa fa-database'); this.addEditorTab('Time range', 'public/app/features/panel/partials/panelTime.html'); } diff --git a/public/app/features/panel/panel_ctrl.ts b/public/app/features/panel/panel_ctrl.ts index 51dd38e4358..cbdfd632e0b 100644 --- a/public/app/features/panel/panel_ctrl.ts +++ b/public/app/features/panel/panel_ctrl.ts @@ -220,8 +220,8 @@ export class PanelCtrl { calculatePanelHeight() { if (this.panel.fullscreen) { - const docHeight = $(window).height(); - const editHeight = Math.floor(docHeight * 0.4); + const docHeight = $('.react-grid-layout').height(); + const editHeight = Math.floor(docHeight * 0.35); const fullscreenHeight = Math.floor(docHeight * 0.8); this.containerHeight = this.panel.isEditing ? editHeight : fullscreenHeight; } else { diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index def7c69a69d..e4d70ff3cf9 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/public/app/features/panel/panel_directive.ts @@ -90,10 +90,6 @@ module.directive('grafanaPanel', ($rootScope, $document, $timeout) => { ctrl.dashboard.setPanelFocus(0); } - function panelHeightUpdated() { - // panelContent.css({ height: ctrl.height + 'px' }); - } - function resizeScrollableContent() { if (panelScrollbar) { panelScrollbar.update(); @@ -138,7 +134,6 @@ module.directive('grafanaPanel', ($rootScope, $document, $timeout) => { ctrl.events.on('panel-size-changed', () => { ctrl.calculatePanelHeight(); - panelHeightUpdated(); $timeout(() => { console.log('panel directive panel size changed, render'); resizeScrollableContent(); @@ -148,7 +143,6 @@ module.directive('grafanaPanel', ($rootScope, $document, $timeout) => { // set initial height ctrl.calculatePanelHeight(); - panelHeightUpdated(); ctrl.events.on('render', () => { console.log('panel_directive: render', ctrl.panel.id); diff --git a/public/app/features/plugins/plugin_component.ts b/public/app/features/plugins/plugin_component.ts index 47f2fc535e8..4b421ca446a 100644 --- a/public/app/features/plugins/plugin_component.ts +++ b/public/app/features/plugins/plugin_component.ts @@ -8,7 +8,7 @@ import { importPluginModule } from './plugin_loader'; import { UnknownPanelCtrl } from 'app/plugins/panel/unknown/module'; /** @ngInject */ -function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $templateCache) { +function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $templateCache, $timeout) { function getTemplate(component) { if (component.template) { return $q.when(component.template); @@ -207,10 +207,14 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $ // let a binding digest cycle complete before adding to dom setTimeout(() => { - elem.append(child); scope.$applyAsync(() => { - console.log('post appendAndCompile, broadcast refresh', scope.panel); - scope.$broadcast('component-did-mount'); + elem.append(child); + setTimeout(() => { + scope.$applyAsync(() => { + console.log('post appendAndCompile, broadcast refresh', scope.panel); + scope.$broadcast('component-did-mount'); + }); + }); }); }); } diff --git a/public/app/partials/dashboard.html b/public/app/partials/dashboard.html index 9e7d4fa1c6c..32acdc435f2 100644 --- a/public/app/partials/dashboard.html +++ b/public/app/partials/dashboard.html @@ -7,7 +7,7 @@ class="dashboard-settings"> -
      +
      diff --git a/public/sass/pages/_dashboard.scss b/public/sass/pages/_dashboard.scss index 13801df8cd1..d9ab29cc91c 100644 --- a/public/sass/pages/_dashboard.scss +++ b/public/sass/pages/_dashboard.scss @@ -3,6 +3,10 @@ width: 100%; height: 100%; box-sizing: border-box; + + &--has-submenu { + height: calc(100% - 50px); + } } .template-variable { From 565edc1ed36e14746ce72fa4bd53fb6ed0c7f734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 9 Oct 2018 17:47:43 +0200 Subject: [PATCH 40/54] added setting top hide plugins in alpha state --- conf/defaults.ini | 3 + pkg/api/api.go | 106 +++++++++--------- pkg/api/frontendsettings.go | 10 +- pkg/api/http_server.go | 2 +- pkg/api/index.go | 12 +- pkg/api/login.go | 4 +- pkg/api/user.go | 6 +- pkg/setting/setting.go | 5 + .../{text2 => graph2}/img/icn-text-panel.svg | 0 public/app/plugins/panel/graph2/module.tsx | 12 +- public/app/plugins/panel/graph2/plugin.json | 4 +- .../{graph2 => text2}/img/icn-graph-panel.svg | 0 public/app/plugins/panel/text2/module.tsx | 12 +- public/app/plugins/panel/text2/plugin.json | 8 +- 14 files changed, 99 insertions(+), 85 deletions(-) rename public/app/plugins/panel/{text2 => graph2}/img/icn-text-panel.svg (100%) rename public/app/plugins/panel/{graph2 => text2}/img/icn-graph-panel.svg (100%) diff --git a/conf/defaults.ini b/conf/defaults.ini index eb8debc0094..750f06f2f6a 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -554,3 +554,6 @@ container_name = # Options to configure external image rendering server like https://github.com/grafana/grafana-image-renderer server_url = callback_url = + +[panels] +enable_alpha = false diff --git a/pkg/api/api.go b/pkg/api/api.go index 39b332aeb9f..9e350960a84 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -22,66 +22,66 @@ func (hs *HTTPServer) registerRoutes() { r := hs.RouteRegister // not logged in views - r.Get("/", reqSignedIn, Index) + r.Get("/", reqSignedIn, hs.Index) r.Get("/logout", Logout) r.Post("/login", quota("session"), bind(dtos.LoginCommand{}), Wrap(LoginPost)) r.Get("/login/:name", quota("session"), OAuthLogin) - r.Get("/login", LoginView) - r.Get("/invite/:code", Index) + r.Get("/login", hs.LoginView) + r.Get("/invite/:code", hs.Index) // authed views - r.Get("/profile/", reqSignedIn, Index) - r.Get("/profile/password", reqSignedIn, Index) - r.Get("/profile/switch-org/:id", reqSignedIn, ChangeActiveOrgAndRedirectToHome) - r.Get("/org/", reqSignedIn, Index) - r.Get("/org/new", reqSignedIn, Index) - r.Get("/datasources/", reqSignedIn, Index) - r.Get("/datasources/new", reqSignedIn, Index) - r.Get("/datasources/edit/*", reqSignedIn, Index) - r.Get("/org/users", reqSignedIn, Index) - r.Get("/org/users/new", reqSignedIn, Index) - r.Get("/org/users/invite", reqSignedIn, Index) - r.Get("/org/teams", reqSignedIn, Index) - r.Get("/org/teams/*", reqSignedIn, Index) - r.Get("/org/apikeys/", reqSignedIn, Index) - r.Get("/dashboard/import/", reqSignedIn, Index) - r.Get("/configuration", reqGrafanaAdmin, Index) - r.Get("/admin", reqGrafanaAdmin, Index) - r.Get("/admin/settings", reqGrafanaAdmin, Index) - r.Get("/admin/users", reqGrafanaAdmin, Index) - r.Get("/admin/users/create", reqGrafanaAdmin, Index) - r.Get("/admin/users/edit/:id", reqGrafanaAdmin, Index) - r.Get("/admin/orgs", reqGrafanaAdmin, Index) - r.Get("/admin/orgs/edit/:id", reqGrafanaAdmin, Index) - r.Get("/admin/stats", reqGrafanaAdmin, Index) + r.Get("/profile/", reqSignedIn, hs.Index) + r.Get("/profile/password", reqSignedIn, hs.Index) + r.Get("/profile/switch-org/:id", reqSignedIn, hs.ChangeActiveOrgAndRedirectToHome) + r.Get("/org/", reqSignedIn, hs.Index) + r.Get("/org/new", reqSignedIn, hs.Index) + r.Get("/datasources/", reqSignedIn, hs.Index) + r.Get("/datasources/new", reqSignedIn, hs.Index) + r.Get("/datasources/edit/*", reqSignedIn, hs.Index) + r.Get("/org/users", reqSignedIn, hs.Index) + r.Get("/org/users/new", reqSignedIn, hs.Index) + r.Get("/org/users/invite", reqSignedIn, hs.Index) + r.Get("/org/teams", reqSignedIn, hs.Index) + r.Get("/org/teams/*", reqSignedIn, hs.Index) + r.Get("/org/apikeys/", reqSignedIn, hs.Index) + r.Get("/dashboard/import/", reqSignedIn, hs.Index) + r.Get("/configuration", reqGrafanaAdmin, hs.Index) + r.Get("/admin", reqGrafanaAdmin, hs.Index) + r.Get("/admin/settings", reqGrafanaAdmin, hs.Index) + r.Get("/admin/users", reqGrafanaAdmin, hs.Index) + r.Get("/admin/users/create", reqGrafanaAdmin, hs.Index) + r.Get("/admin/users/edit/:id", reqGrafanaAdmin, hs.Index) + r.Get("/admin/orgs", reqGrafanaAdmin, hs.Index) + r.Get("/admin/orgs/edit/:id", reqGrafanaAdmin, hs.Index) + r.Get("/admin/stats", reqGrafanaAdmin, hs.Index) - r.Get("/styleguide", reqSignedIn, Index) + r.Get("/styleguide", reqSignedIn, hs.Index) - r.Get("/plugins", reqSignedIn, Index) - r.Get("/plugins/:id/edit", reqSignedIn, Index) - r.Get("/plugins/:id/page/:page", reqSignedIn, Index) + r.Get("/plugins", reqSignedIn, hs.Index) + r.Get("/plugins/:id/edit", reqSignedIn, hs.Index) + r.Get("/plugins/:id/page/:page", reqSignedIn, hs.Index) - r.Get("/d/:uid/:slug", reqSignedIn, Index) - r.Get("/d/:uid", reqSignedIn, Index) - r.Get("/dashboard/db/:slug", reqSignedIn, redirectFromLegacyDashboardURL, Index) - r.Get("/dashboard/script/*", reqSignedIn, Index) - r.Get("/dashboard-solo/snapshot/*", Index) - r.Get("/d-solo/:uid/:slug", reqSignedIn, Index) - r.Get("/dashboard-solo/db/:slug", reqSignedIn, redirectFromLegacyDashboardSoloURL, Index) - r.Get("/dashboard-solo/script/*", reqSignedIn, Index) - r.Get("/import/dashboard", reqSignedIn, Index) - r.Get("/dashboards/", reqSignedIn, Index) - r.Get("/dashboards/*", reqSignedIn, Index) + r.Get("/d/:uid/:slug", reqSignedIn, hs.Index) + r.Get("/d/:uid", reqSignedIn, hs.Index) + r.Get("/dashboard/db/:slug", reqSignedIn, redirectFromLegacyDashboardURL, hs.Index) + r.Get("/dashboard/script/*", reqSignedIn, hs.Index) + r.Get("/dashboard-solo/snapshot/*", hs.Index) + r.Get("/d-solo/:uid/:slug", reqSignedIn, hs.Index) + r.Get("/dashboard-solo/db/:slug", reqSignedIn, redirectFromLegacyDashboardSoloURL, hs.Index) + r.Get("/dashboard-solo/script/*", reqSignedIn, hs.Index) + r.Get("/import/dashboard", reqSignedIn, hs.Index) + r.Get("/dashboards/", reqSignedIn, hs.Index) + r.Get("/dashboards/*", reqSignedIn, hs.Index) - r.Get("/explore", reqEditorRole, Index) + r.Get("/explore", reqEditorRole, hs.Index) - r.Get("/playlists/", reqSignedIn, Index) - r.Get("/playlists/*", reqSignedIn, Index) - r.Get("/alerting/", reqSignedIn, Index) - r.Get("/alerting/*", reqSignedIn, Index) + r.Get("/playlists/", reqSignedIn, hs.Index) + r.Get("/playlists/*", reqSignedIn, hs.Index) + r.Get("/alerting/", reqSignedIn, hs.Index) + r.Get("/alerting/*", reqSignedIn, hs.Index) // sign up - r.Get("/signup", Index) + r.Get("/signup", hs.Index) r.Get("/api/user/signup/options", Wrap(GetSignUpOptions)) r.Post("/api/user/signup", quota("user"), bind(dtos.SignUpForm{}), Wrap(SignUp)) r.Post("/api/user/signup/step2", bind(dtos.SignUpStep2Form{}), Wrap(SignUpStep2)) @@ -91,15 +91,15 @@ func (hs *HTTPServer) registerRoutes() { r.Post("/api/user/invite/complete", bind(dtos.CompleteInviteForm{}), Wrap(CompleteInvite)) // reset password - r.Get("/user/password/send-reset-email", Index) - r.Get("/user/password/reset", Index) + r.Get("/user/password/send-reset-email", hs.Index) + r.Get("/user/password/reset", hs.Index) r.Post("/api/user/password/send-reset-email", bind(dtos.SendResetPasswordEmailForm{}), Wrap(SendResetPasswordEmail)) r.Post("/api/user/password/reset", bind(dtos.ResetUserPasswordForm{}), Wrap(ResetPassword)) // dashboard snapshots - r.Get("/dashboard/snapshot/*", Index) - r.Get("/dashboard/snapshots/", reqSignedIn, Index) + r.Get("/dashboard/snapshot/*", hs.Index) + r.Get("/dashboard/snapshots/", reqSignedIn, hs.Index) // api for dashboard snapshots r.Post("/api/snapshots/", bind(m.CreateDashboardSnapshotCommand{}), CreateDashboardSnapshot) @@ -251,7 +251,7 @@ func (hs *HTTPServer) registerRoutes() { pluginRoute.Post("/:pluginId/settings", bind(m.UpdatePluginSettingCmd{}), Wrap(UpdatePluginSetting)) }, reqOrgAdmin) - apiRoute.Get("/frontend/settings/", GetFrontendSettings) + apiRoute.Get("/frontend/settings/", hs.GetFrontendSettings) apiRoute.Any("/datasources/proxy/:id/*", reqSignedIn, hs.ProxyDataSourceRequest) apiRoute.Any("/datasources/proxy/:id", reqSignedIn, hs.ProxyDataSourceRequest) diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index a58be38781e..4ef1f66200c 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -11,7 +11,7 @@ import ( "github.com/grafana/grafana/pkg/util" ) -func getFrontendSettingsMap(c *m.ReqContext) (map[string]interface{}, error) { +func (hs *HTTPServer) getFrontendSettingsMap(c *m.ReqContext) (map[string]interface{}, error) { orgDataSources := make([]*m.DataSource, 0) if c.OrgId != 0 { @@ -120,6 +120,10 @@ func getFrontendSettingsMap(c *m.ReqContext) (map[string]interface{}, error) { panels := map[string]interface{}{} for _, panel := range enabledPlugins.Panels { + if panel.State == "alpha" && !hs.Cfg.EnableAlphaPanels { + continue + } + panels[panel.Id] = map[string]interface{}{ "module": panel.Module, "baseUrl": panel.BaseUrl, @@ -183,8 +187,8 @@ func getPanelSort(id string) int { return sort } -func GetFrontendSettings(c *m.ReqContext) { - settings, err := getFrontendSettingsMap(c) +func (hs *HTTPServer) GetFrontendSettings(c *m.ReqContext) { + settings, err := hs.getFrontendSettingsMap(c) if err != nil { c.JsonApiErr(400, "Failed to get frontend settings", err) return diff --git a/pkg/api/http_server.go b/pkg/api/http_server.go index 432d6a18369..65d1d359f95 100644 --- a/pkg/api/http_server.go +++ b/pkg/api/http_server.go @@ -184,7 +184,7 @@ func (hs *HTTPServer) applyRoutes() { // then custom app proxy routes hs.initAppPluginRoutes(hs.macaron) // lastly not found route - hs.macaron.NotFound(NotFoundHandler) + hs.macaron.NotFound(hs.NotFoundHandler) } func (hs *HTTPServer) addMiddlewaresAndStaticRoutes() { diff --git a/pkg/api/index.go b/pkg/api/index.go index 1b73acd8829..61fb7ec3a5b 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -17,8 +17,8 @@ const ( darkName = "dark" ) -func setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, error) { - settings, err := getFrontendSettingsMap(c) +func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, error) { + settings, err := hs.getFrontendSettingsMap(c) if err != nil { return nil, err } @@ -353,8 +353,8 @@ func setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, error) { return &data, nil } -func Index(c *m.ReqContext) { - data, err := setIndexViewData(c) +func (hs *HTTPServer) Index(c *m.ReqContext) { + data, err := hs.setIndexViewData(c) if err != nil { c.Handle(500, "Failed to get settings", err) return @@ -362,13 +362,13 @@ func Index(c *m.ReqContext) { c.HTML(200, "index", data) } -func NotFoundHandler(c *m.ReqContext) { +func (hs *HTTPServer) NotFoundHandler(c *m.ReqContext) { if c.IsApiRequest() { c.JsonApiErr(404, "Not found", nil) return } - data, err := setIndexViewData(c) + data, err := hs.setIndexViewData(c) if err != nil { c.Handle(500, "Failed to get settings", err) return diff --git a/pkg/api/login.go b/pkg/api/login.go index 632d04e37f1..1083f89adfd 100644 --- a/pkg/api/login.go +++ b/pkg/api/login.go @@ -17,8 +17,8 @@ const ( ViewIndex = "index" ) -func LoginView(c *m.ReqContext) { - viewData, err := setIndexViewData(c) +func (hs *HTTPServer) LoginView(c *m.ReqContext) { + viewData, err := hs.setIndexViewData(c) if err != nil { c.Handle(500, "Failed to get settings", err) return diff --git a/pkg/api/user.go b/pkg/api/user.go index 4b916202e65..7116ad83f3f 100644 --- a/pkg/api/user.go +++ b/pkg/api/user.go @@ -177,17 +177,17 @@ func UserSetUsingOrg(c *m.ReqContext) Response { } // GET /profile/switch-org/:id -func ChangeActiveOrgAndRedirectToHome(c *m.ReqContext) { +func (hs *HTTPServer) ChangeActiveOrgAndRedirectToHome(c *m.ReqContext) { orgID := c.ParamsInt64(":id") if !validateUsingOrg(c.UserId, orgID) { - NotFoundHandler(c) + hs.NotFoundHandler(c) } cmd := m.SetUsingOrgCommand{UserId: c.UserId, OrgId: orgID} if err := bus.Dispatch(&cmd); err != nil { - NotFoundHandler(c) + hs.NotFoundHandler(c) } c.Redirect(setting.AppSubUrl + "/") diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 27df73a9eed..12d452468d1 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -209,6 +209,8 @@ type Cfg struct { TempDataLifetime time.Duration MetricsEndpointEnabled bool + + EnableAlphaPanels bool } type CommandLineArgs struct { @@ -688,6 +690,9 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error { explore := iniFile.Section("explore") ExploreEnabled = explore.Key("enabled").MustBool(false) + panels := iniFile.Section("panels") + cfg.EnableAlphaPanels = panels.Key("enable_alpha").MustBool(false) + cfg.readSessionConfig() cfg.readSmtpSettings() cfg.readQuotaSettings() diff --git a/public/app/plugins/panel/text2/img/icn-text-panel.svg b/public/app/plugins/panel/graph2/img/icn-text-panel.svg similarity index 100% rename from public/app/plugins/panel/text2/img/icn-text-panel.svg rename to public/app/plugins/panel/graph2/img/icn-text-panel.svg diff --git a/public/app/plugins/panel/graph2/module.tsx b/public/app/plugins/panel/graph2/module.tsx index 7ae24bd7d40..a0df192f9d8 100644 --- a/public/app/plugins/panel/graph2/module.tsx +++ b/public/app/plugins/panel/graph2/module.tsx @@ -1,7 +1,7 @@ import React, { PureComponent } from 'react'; import { PanelProps } from 'app/features/dashboard/dashgrid/DataPanel'; -export class ReactTestPanel extends PureComponent { +export class Graph2 extends PureComponent { constructor(props) { super(props); } @@ -14,8 +14,14 @@ export class ReactTestPanel extends PureComponent { value = data[0].value; } - return

      Graph Panel! {value}

      ; + return

      Text Panel {value}

      ; } } -export { ReactTestPanel as PanelComponent }; +export class TextOptions extends PureComponent { + render() { + return

      Text2 Options component

      ; + } +} + +export { Graph2 as PanelComponent, TextOptions as PanelOptions }; diff --git a/public/app/plugins/panel/graph2/plugin.json b/public/app/plugins/panel/graph2/plugin.json index bc60d6ad2d7..b519a57fae4 100644 --- a/public/app/plugins/panel/graph2/plugin.json +++ b/public/app/plugins/panel/graph2/plugin.json @@ -9,8 +9,8 @@ "url": "https://grafana.com" }, "logos": { - "small": "img/icn-graph-panel.svg", - "large": "img/icn-graph-panel.svg" + "small": "img/icn-text-panel.svg", + "large": "img/icn-text-panel.svg" } } } diff --git a/public/app/plugins/panel/graph2/img/icn-graph-panel.svg b/public/app/plugins/panel/text2/img/icn-graph-panel.svg similarity index 100% rename from public/app/plugins/panel/graph2/img/icn-graph-panel.svg rename to public/app/plugins/panel/text2/img/icn-graph-panel.svg diff --git a/public/app/plugins/panel/text2/module.tsx b/public/app/plugins/panel/text2/module.tsx index 703a9897c6f..df00b85176a 100644 --- a/public/app/plugins/panel/text2/module.tsx +++ b/public/app/plugins/panel/text2/module.tsx @@ -1,7 +1,7 @@ import React, { PureComponent } from 'react'; import { PanelProps } from 'app/features/dashboard/dashgrid/DataPanel'; -export class ReactTestPanel extends PureComponent { +export class Text2 extends PureComponent { constructor(props) { super(props); } @@ -14,14 +14,8 @@ export class ReactTestPanel extends PureComponent { value = data[0].value; } - return

      Text Panel {value}

      ; + return

      Graph Panel! {value}

      ; } } -export class TextOptions extends PureComponent { - render() { - return

      Text2 Options component

      ; - } -} - -export { ReactTestPanel as PanelComponent, TextOptions as PanelOptions }; +export { Text2 as PanelComponent }; diff --git a/public/app/plugins/panel/text2/plugin.json b/public/app/plugins/panel/text2/plugin.json index 95d821bfd50..b1133f65e36 100644 --- a/public/app/plugins/panel/text2/plugin.json +++ b/public/app/plugins/panel/text2/plugin.json @@ -1,16 +1,18 @@ { "type": "panel", - "name": "Text2", + "name": "Text v2", "id": "text2", + "state": "alpha", + "info": { "author": { "name": "Grafana Project", "url": "https://grafana.com" }, "logos": { - "small": "img/icn-text-panel.svg", - "large": "img/icn-text-panel.svg" + "small": "img/icn-graph-panel.svg", + "large": "img/icn-graph-panel.svg" } } } From ee1d4ce0e2dc465c2213f8242cfb26dfb5ebe9e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 10 Oct 2018 09:26:17 +0200 Subject: [PATCH 41/54] fix tab switching --- public/app/core/reducers/location.ts | 11 +++++++++-- .../app/features/dashboard/dashgrid/PanelEditor.tsx | 1 + public/app/types/location.ts | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/public/app/core/reducers/location.ts b/public/app/core/reducers/location.ts index 2089cfe9f59..7c7dffd04b9 100644 --- a/public/app/core/reducers/location.ts +++ b/public/app/core/reducers/location.ts @@ -1,6 +1,7 @@ import { Action } from 'app/core/actions/location'; import { LocationState } from 'app/types'; import { renderUrl } from 'app/core/utils/url'; +import _ from 'lodash'; export const initialState: LocationState = { url: '', @@ -12,11 +13,17 @@ export const initialState: LocationState = { export const locationReducer = (state = initialState, action: Action): LocationState => { switch (action.type) { case 'UPDATE_LOCATION': { - const { path, query, routeParams } = action.payload; + const { path, routeParams } = action.payload; + let query = action.payload.query || state.query; + + if (action.payload.partial) { + query = _.defaults(query, state.query); + } + return { url: renderUrl(path || state.path, query), path: path || state.path, - query: query || state.query, + query: query, routeParams: routeParams || state.routeParams, }; } diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index 2a6228217ed..26ac8b7d2c1 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -67,6 +67,7 @@ export class PanelEditor extends React.Component { store.dispatch( updateLocation({ query: { tab: tab.id }, + partial: true, }) ); }; diff --git a/public/app/types/location.ts b/public/app/types/location.ts index 4a7f51523a7..7dcf57f7e02 100644 --- a/public/app/types/location.ts +++ b/public/app/types/location.ts @@ -2,6 +2,7 @@ export interface LocationUpdate { path?: string; query?: UrlQueryMap; routeParams?: UrlQueryMap; + partial?: boolean; } export interface LocationState { From 0662b5f962eb43a92ea7bba2e7eaeb4da6eabebe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 14 Oct 2018 11:13:04 +0200 Subject: [PATCH 42/54] fixing unit tests --- .../app/features/dashboard/dashgrid/QueriesTab.tsx | 14 ++++++++++---- public/app/features/dashboard/panel_model.ts | 2 -- .../features/dashboard/specs/viewstate_srv.test.ts | 1 + 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/QueriesTab.tsx b/public/app/features/dashboard/dashgrid/QueriesTab.tsx index 179d074d547..21159df00aa 100644 --- a/public/app/features/dashboard/dashgrid/QueriesTab.tsx +++ b/public/app/features/dashboard/dashgrid/QueriesTab.tsx @@ -21,15 +21,21 @@ export class QueriesTab extends React.Component { return; } + const { panel, dashboard } = this.props; + + // make sure the panel has datasource & queries properties + panel.datasource = panel.datasource || null; + panel.targets = panel.targets || [{}]; + const loader = getAngularLoader(); const template = ''; const scopeProps = { ctrl: { - panel: this.props.panel, - dashboard: this.props.dashboard, + panel: panel, + dashboard: dashboard, panelCtrl: { - panel: this.props.panel, - dashboard: this.props.dashboard, + panel: panel, + dashboard: dashboard, }, }, }; diff --git a/public/app/features/dashboard/panel_model.ts b/public/app/features/dashboard/panel_model.ts index a3053ae48f8..415238d4e7b 100644 --- a/public/app/features/dashboard/panel_model.ts +++ b/public/app/features/dashboard/panel_model.ts @@ -52,8 +52,6 @@ export class PanelModel { // defaults this.gridPos = this.gridPos || { x: 0, y: 0, h: 3, w: 6 }; - this.datasource = this.datasource || null; - this.targets = this.targets || [{}]; } getSaveModel() { diff --git a/public/app/features/dashboard/specs/viewstate_srv.test.ts b/public/app/features/dashboard/specs/viewstate_srv.test.ts index c2d9a28ea51..f9963afbf85 100644 --- a/public/app/features/dashboard/specs/viewstate_srv.test.ts +++ b/public/app/features/dashboard/specs/viewstate_srv.test.ts @@ -11,6 +11,7 @@ describe('when updating view state', () => { }; const $scope = { + appEvent: jest.fn(), onAppEvent: jest.fn(() => {}), dashboard: new DashboardModel({ panels: [{ id: 1 }], From 2e1d45a8756d99c8c5eaaee2f28bb9a22c0b4a42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 14 Oct 2018 12:41:09 +0200 Subject: [PATCH 43/54] changed DataPanel from HOC to use render props --- public/app/core/components/grafana_app.ts | 2 +- public/app/core/services/angular_loader.ts | 42 ----- .../dashboard/dashgrid/DashboardPanel.tsx | 2 +- .../features/dashboard/dashgrid/DataPanel.tsx | 151 ++++++++++-------- .../dashboard/dashgrid/PanelChrome.tsx | 24 +-- .../dashboard/dashgrid/QueriesTab.tsx | 2 +- public/app/plugins/panel/graph2/module.tsx | 10 +- 7 files changed, 106 insertions(+), 127 deletions(-) delete mode 100644 public/app/core/services/angular_loader.ts diff --git a/public/app/core/components/grafana_app.ts b/public/app/core/components/grafana_app.ts index b513bed3ef0..a244a5393d1 100644 --- a/public/app/core/components/grafana_app.ts +++ b/public/app/core/components/grafana_app.ts @@ -9,7 +9,7 @@ import Drop from 'tether-drop'; import colors from 'app/core/utils/colors'; import { BackendSrv, setBackendSrv } from 'app/core/services/backend_srv'; import { DatasourceSrv } from 'app/features/plugins/datasource_srv'; -import { AngularLoader, setAngularLoader } from 'app/core/services/angular_loader'; +import { AngularLoader, setAngularLoader } from 'app/core/services/AngularLoader'; import { configureStore } from 'app/store/configureStore'; export class GrafanaCtrl { diff --git a/public/app/core/services/angular_loader.ts b/public/app/core/services/angular_loader.ts deleted file mode 100644 index e3a7dec4351..00000000000 --- a/public/app/core/services/angular_loader.ts +++ /dev/null @@ -1,42 +0,0 @@ -import angular from 'angular'; -import coreModule from 'app/core/core_module'; -import _ from 'lodash'; - -export interface AngularComponent { - destroy(); -} - -export class AngularLoader { - /** @ngInject */ - constructor(private $compile, private $rootScope) {} - - load(elem, scopeProps, template): AngularComponent { - const scope = this.$rootScope.$new(); - - _.assign(scope, scopeProps); - - const compiledElem = this.$compile(template)(scope); - const rootNode = angular.element(elem); - rootNode.append(compiledElem); - - return { - destroy: () => { - scope.$destroy(); - compiledElem.remove(); - }, - }; - } -} - -coreModule.service('angularLoader', AngularLoader); - -let angularLoaderInstance: AngularLoader; - -export function setAngularLoader(pl: AngularLoader) { - angularLoaderInstance = pl; -} - -// away to access it from react -export function getAngularLoader(): AngularLoader { - return angularLoaderInstance; -} diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx index 039473a7a30..7dd8a06996d 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -2,7 +2,7 @@ import React from 'react'; import config from 'app/core/config'; import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; -import { getAngularLoader, AngularComponent } from 'app/core/services/angular_loader'; +import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader'; import { DashboardRow } from './DashboardRow'; import { AddPanelPanel } from './AddPanelPanel'; import { importPluginModule } from 'app/features/plugins/plugin_loader'; diff --git a/public/app/features/dashboard/dashgrid/DataPanel.tsx b/public/app/features/dashboard/dashgrid/DataPanel.tsx index bce4c69bb69..d19d6b10e2e 100644 --- a/public/app/features/dashboard/dashgrid/DataPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DataPanel.tsx @@ -1,81 +1,92 @@ -import React, { Component, ComponentClass } from 'react'; +// Library +import React, { Component } from 'react'; -export interface OuterProps { - type: string; +interface RenderProps { + loading: LoadingState; + data: any; +} + +export interface Props { + datasource: string | null; queries: any[]; - isVisible: boolean; + children: (r: RenderProps) => JSX.Element; } -export interface PanelProps extends OuterProps { - data: any[]; +export interface State { + isFirstLoad: boolean; + loading: LoadingState; + data: any; } -export interface DataPanel extends ComponentClass {} - -interface State { - isLoading: boolean; - data: any[]; +export enum LoadingState { + NotStarted = 'NotStarted', + Loading = 'Loading', + Done = 'Done', + Error = 'Error', } -export const DataPanelWrapper = (ComposedComponent: ComponentClass) => { - class Wrapper extends Component { - static defaultProps = { - isVisible: true, +export interface PanelProps extends RenderProps {} + +export class DataPanel extends Component { + constructor(props: Props) { + super(props); + + this.state = { + loading: LoadingState.NotStarted, + data: [], + isFirstLoad: true, }; - - constructor(props: OuterProps) { - super(props); - - this.state = { - isLoading: false, - data: [], - }; - } - - componentDidMount() { - console.log('data panel mount'); - this.issueQueries(); - } - - issueQueries = async () => { - const { isVisible } = this.props; - - if (!isVisible) { - return; - } - - this.setState({ isLoading: true }); - - await new Promise(resolve => { - setTimeout(() => { - this.setState({ isLoading: false, data: [{ value: 10 }] }); - }, 500); - }); - }; - - render() { - const { data, isLoading } = this.state; - console.log('data panel render'); - - if (!data.length) { - return ( -
      -

      No Data

      -
      - ); - } - - if (isLoading) { - return ( -
      -

      Loading

      -
      - ); - } - - return ; - } } - return Wrapper; -}; + componentDidMount() { + console.log('DataPanel mount'); + this.issueQueries(); + } + + issueQueries = async () => { + this.setState({ loading: LoadingState.Loading }); + + await new Promise(resolve => { + setTimeout(() => { + this.setState({ loading: LoadingState.Done, data: [{ value: 10 }], isFirstLoad: false }); + }, 500); + }); + }; + + render() { + const { data, loading, isFirstLoad } = this.state; + console.log('data panel render'); + + if (isFirstLoad && loading === LoadingState.Loading) { + return ( +
      +

      Loading

      +
      + ); + } + + return ( + <> + {this.loadingSpinner} + {this.props.children({ + data, + loading, + })} + + ); + } + + private get loadingSpinner(): JSX.Element { + const { loading } = this.state; + + if (loading === LoadingState.Loading) { + return ( +
      + +
      + ); + } + + return null; + } +} diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index 73208c34130..a3e4b4eedc8 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -2,7 +2,7 @@ import React, { ComponentClass } from 'react'; import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; import { PanelHeader } from './PanelHeader'; -import { DataPanel, PanelProps, DataPanelWrapper } from './DataPanel'; +import { DataPanel, PanelProps } from './DataPanel'; export interface Props { panel: PanelModel; @@ -12,9 +12,6 @@ export interface Props { interface State {} -// cache DataPanel wrapper components -const dataPanels: { [s: string]: DataPanel } = {}; - export class PanelChrome extends React.Component { panelComponent: DataPanel; @@ -23,20 +20,25 @@ export class PanelChrome extends React.Component { } render() { - const { type } = this.props.panel; + const { datasource, targets } = this.props.panel; + const PanelComponent = this.props.component; - let PanelComponent = dataPanels[type]; - - if (!PanelComponent) { - PanelComponent = dataPanels[type] = DataPanelWrapper(this.props.component); - } + // if (!PanelComponent) { + // PanelComponent = dataPanels[type] = DataPanelWrapper(this.props.component); + // } console.log('PanelChrome render', PanelComponent); return (
      -
      {}
      +
      + + {({ loading, data }) => { + return ; + }} + +
      ); } diff --git a/public/app/features/dashboard/dashgrid/QueriesTab.tsx b/public/app/features/dashboard/dashgrid/QueriesTab.tsx index 21159df00aa..671075b8c47 100644 --- a/public/app/features/dashboard/dashgrid/QueriesTab.tsx +++ b/public/app/features/dashboard/dashgrid/QueriesTab.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; -import { getAngularLoader, AngularComponent } from 'app/core/services/angular_loader'; +import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader'; interface Props { panel: PanelModel; diff --git a/public/app/plugins/panel/graph2/module.tsx b/public/app/plugins/panel/graph2/module.tsx index a0df192f9d8..79bf1890bae 100644 --- a/public/app/plugins/panel/graph2/module.tsx +++ b/public/app/plugins/panel/graph2/module.tsx @@ -1,7 +1,15 @@ import React, { PureComponent } from 'react'; import { PanelProps } from 'app/features/dashboard/dashgrid/DataPanel'; -export class Graph2 extends PureComponent { +interface Options { + showBars: boolean; +} + +interface Props extends PanelProps { + options: Options; +} + +export class Graph2 extends PureComponent { constructor(props) { super(props); } From cd4619dad17b695d7724504d6ad9ecba10434a55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 14 Oct 2018 15:39:34 +0200 Subject: [PATCH 44/54] working on react data / query exectution --- public/app/core/components/grafana_app.ts | 6 +- .../features/dashboard/dashgrid/DataPanel.tsx | 70 ++++++++++++++----- .../dashboard/dashgrid/PanelChrome.tsx | 70 ++++++++++++++----- public/app/features/dashboard/time_srv.ts | 30 +++++--- public/app/features/panel/panel_directive.ts | 1 - public/app/features/plugins/datasource_srv.ts | 10 +++ .../cloudwatch/query_parameter_ctrl.ts | 5 +- .../datasource/elasticsearch/bucket_agg.ts | 7 +- .../datasource/elasticsearch/metric_agg.ts | 7 +- .../datasource/graphite/add_graphite_func.ts | 4 +- .../datasource/graphite/func_editor.ts | 4 +- .../stackdriver/query_aggregation_ctrl.ts | 6 +- .../stackdriver/query_filter_ctrl.ts | 6 +- public/app/plugins/panel/graph/graph.ts | 1 - public/app/plugins/panel/graph/legend.ts | 6 +- .../panel/graph/series_overrides_ctrl.ts | 4 +- public/app/plugins/panel/graph2/module.tsx | 4 +- .../app/plugins/panel/heatmap/color_legend.ts | 8 +-- public/app/plugins/panel/text2/module.tsx | 4 +- public/app/types/index.ts | 5 ++ public/app/types/panel.ts | 6 ++ public/app/types/queries.ts | 19 +++++ 22 files changed, 204 insertions(+), 79 deletions(-) create mode 100644 public/app/types/panel.ts create mode 100644 public/app/types/queries.ts diff --git a/public/app/core/components/grafana_app.ts b/public/app/core/components/grafana_app.ts index a244a5393d1..d6291c94a6f 100644 --- a/public/app/core/components/grafana_app.ts +++ b/public/app/core/components/grafana_app.ts @@ -8,7 +8,8 @@ import appEvents from 'app/core/app_events'; import Drop from 'tether-drop'; import colors from 'app/core/utils/colors'; import { BackendSrv, setBackendSrv } from 'app/core/services/backend_srv'; -import { DatasourceSrv } from 'app/features/plugins/datasource_srv'; +import { TimeSrv, setTimeSrv } from 'app/features/dashboard/time_srv'; +import { DatasourceSrv, setDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { AngularLoader, setAngularLoader } from 'app/core/services/AngularLoader'; import { configureStore } from 'app/store/configureStore'; @@ -23,12 +24,15 @@ export class GrafanaCtrl { contextSrv, bridgeSrv, backendSrv: BackendSrv, + timeSrv: TimeSrv, datasourceSrv: DatasourceSrv, angularLoader: AngularLoader ) { // make angular loader service available to react components setAngularLoader(angularLoader); setBackendSrv(backendSrv); + setDatasourceSrv(datasourceSrv); + setTimeSrv(timeSrv); configureStore(); $scope.init = () => { diff --git a/public/app/features/dashboard/dashgrid/DataPanel.tsx b/public/app/features/dashboard/dashgrid/DataPanel.tsx index d19d6b10e2e..6b8aa8c4a91 100644 --- a/public/app/features/dashboard/dashgrid/DataPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DataPanel.tsx @@ -1,5 +1,11 @@ // Library -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; + +// Services +import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; + +// Types +import { TimeRange, LoadingState } from 'app/types'; interface RenderProps { loading: LoadingState; @@ -9,6 +15,11 @@ interface RenderProps { export interface Props { datasource: string | null; queries: any[]; + panelId?: number; + dashboardId?: number; + isVisible?: boolean; + timeRange?: TimeRange; + refreshCounter: number; children: (r: RenderProps) => JSX.Element; } @@ -18,16 +29,13 @@ export interface State { data: any; } -export enum LoadingState { - NotStarted = 'NotStarted', - Loading = 'Loading', - Done = 'Done', - Error = 'Error', -} +export class DataPanel extends PureComponent { + static defaultProps = { + isVisible: true, + panelId: 1, + dashboardId: 1, + }; -export interface PanelProps extends RenderProps {} - -export class DataPanel extends Component { constructor(props: Props) { super(props); @@ -44,20 +52,50 @@ export class DataPanel extends Component { } issueQueries = async () => { + const { isVisible, queries, datasource, panelId, dashboardId, timeRange } = this.props; + + if (!isVisible) { + return; + } + + if (!queries.length) { + this.setState({ data: [], loading: LoadingState.Done }); + return; + } + this.setState({ loading: LoadingState.Loading }); - await new Promise(resolve => { - setTimeout(() => { - this.setState({ loading: LoadingState.Done, data: [{ value: 10 }], isFirstLoad: false }); - }, 500); - }); + try { + const dataSourceSrv = getDatasourceSrv(); + const ds = await dataSourceSrv.get(datasource); + + const queryOptions = { + timezone: 'browser', + panelId: panelId, + dashboardId: dashboardId, + range: timeRange, + rangeRaw: timeRange.raw, + interval: '1s', + intervalMs: 1000, + targets: queries, + maxDataPoints: 500, + scopedVars: {}, + cacheTimeout: null, + }; + + const resp = await ds.query(queryOptions); + console.log(resp); + } catch (err) { + console.log('Loading error', err); + this.setState({ loading: LoadingState.Error }); + } }; render() { const { data, loading, isFirstLoad } = this.state; console.log('data panel render'); - if (isFirstLoad && loading === LoadingState.Loading) { + if (isFirstLoad && (loading === LoadingState.Loading || loading === LoadingState.NotStarted)) { return (

      Loading

      diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index a3e4b4eedc8..29797664cd9 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -1,8 +1,17 @@ -import React, { ComponentClass } from 'react'; +// Libraries +import React, { ComponentClass, PureComponent } from 'react'; + +// Services +import { getTimeSrv } from '../time_srv'; + +// Components +import { PanelHeader } from './PanelHeader'; +import { DataPanel } from './DataPanel'; + +// Types import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; -import { PanelHeader } from './PanelHeader'; -import { DataPanel, PanelProps } from './DataPanel'; +import { TimeRange, PanelProps } from 'app/types'; export interface Props { panel: PanelModel; @@ -10,30 +19,59 @@ export interface Props { component: ComponentClass; } -interface State {} - -export class PanelChrome extends React.Component { - panelComponent: DataPanel; +export interface State { + refreshCounter: number; + timeRange?: TimeRange; +} +export class PanelChrome extends PureComponent { constructor(props) { super(props); + + this.state = { + refreshCounter: 0, + }; + } + + componentDidMount() { + this.props.dashboard.panelInitialized(this.props.panel); + this.props.panel.events.on('refresh', this.onRefresh); + } + + componentWillUnmount() { + this.props.panel.events.off('refresh', this.onRefresh); + } + + onRefresh = () => { + const timeSrv = getTimeSrv(); + const timeRange = timeSrv.timeRange(); + + this.setState({ + refreshCounter: this.state.refreshCounter + 1, + timeRange: timeRange, + }); + }; + + get isVisible() { + return this.props.dashboard.otherPanelInFullscreen(this.props.panel); } render() { - const { datasource, targets } = this.props.panel; + const { panel, dashboard } = this.props; + const { datasource, targets } = panel; + const { refreshCounter } = this.state; const PanelComponent = this.props.component; - // if (!PanelComponent) { - // PanelComponent = dataPanels[type] = DataPanelWrapper(this.props.component); - // } - - console.log('PanelChrome render', PanelComponent); - return (
      - +
      - + {({ loading, data }) => { return ; }} diff --git a/public/app/features/dashboard/time_srv.ts b/public/app/features/dashboard/time_srv.ts index 89aa94ed336..06cadfa6530 100644 --- a/public/app/features/dashboard/time_srv.ts +++ b/public/app/features/dashboard/time_srv.ts @@ -1,9 +1,15 @@ +// Libraries import moment from 'moment'; import _ from 'lodash'; -import coreModule from 'app/core/core_module'; + +// Utils import kbn from 'app/core/utils/kbn'; +import coreModule from 'app/core/core_module'; import * as dateMath from 'app/core/utils/datemath'; +// Types +import { TimeRange } from 'app/types'; + export class TimeSrv { time: any; refreshTimer: any; @@ -200,7 +206,7 @@ export class TimeSrv { return range; } - timeRange() { + timeRange(): TimeRange { // make copies if they are moment (do not want to return out internal moment, because they are mutable!) const raw = { from: moment.isMoment(this.time.from) ? moment(this.time.from) : this.time.from, @@ -222,17 +228,21 @@ export class TimeSrv { const timespan = range.to.valueOf() - range.from.valueOf(); const center = range.to.valueOf() - timespan / 2; - let to = center + timespan * factor / 2; - let from = center - timespan * factor / 2; - - if (to > Date.now() && range.to <= Date.now()) { - const offset = to - Date.now(); - from = from - offset; - to = Date.now(); - } + const to = center + timespan * factor / 2; + const from = center - timespan * factor / 2; this.setTime({ from: moment.utc(from), to: moment.utc(to) }); } } +let singleton; + +export function setTimeSrv(srv: TimeSrv) { + singleton = srv; +} + +export function getTimeSrv(): TimeSrv { + return singleton; +} + coreModule.service('timeSrv', TimeSrv); diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index e4d70ff3cf9..d4554a2eed9 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/public/app/features/panel/panel_directive.ts @@ -145,7 +145,6 @@ module.directive('grafanaPanel', ($rootScope, $document, $timeout) => { ctrl.calculatePanelHeight(); ctrl.events.on('render', () => { - console.log('panel_directive: render', ctrl.panel.id); if (transparentLastState !== ctrl.panel.transparent) { panelContainer.toggleClass('panel-transparent', ctrl.panel.transparent === true); transparentLastState = ctrl.panel.transparent; diff --git a/public/app/features/plugins/datasource_srv.ts b/public/app/features/plugins/datasource_srv.ts index 7ef82519668..22861eaeef8 100644 --- a/public/app/features/plugins/datasource_srv.ts +++ b/public/app/features/plugins/datasource_srv.ts @@ -162,5 +162,15 @@ export class DatasourceSrv { } } +let singleton: DatasourceSrv; + +export function setDatasourceSrv(srv: DatasourceSrv) { + singleton = srv; +} + +export function getDatasourceSrv(): DatasourceSrv { + return singleton; +} + coreModule.service('datasourceSrv', DatasourceSrv); export default DatasourceSrv; diff --git a/public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.ts b/public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.ts index 4f4b2961761..ba5a39688b3 100644 --- a/public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.ts +++ b/public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.ts @@ -1,4 +1,5 @@ import angular from 'angular'; +import coreModule from 'app/core/core_module'; import _ from 'lodash'; export class CloudWatchQueryParameter { @@ -239,5 +240,5 @@ export class CloudWatchQueryParameterCtrl { } } -angular.module('grafana.controllers').directive('cloudwatchQueryParameter', CloudWatchQueryParameter); -angular.module('grafana.controllers').controller('CloudWatchQueryParameterCtrl', CloudWatchQueryParameterCtrl); +coreModule.directive('cloudwatchQueryParameter', CloudWatchQueryParameter); +coreModule.controller('CloudWatchQueryParameterCtrl', CloudWatchQueryParameterCtrl); diff --git a/public/app/plugins/datasource/elasticsearch/bucket_agg.ts b/public/app/plugins/datasource/elasticsearch/bucket_agg.ts index 8963f2c3f4b..cacf86201fe 100644 --- a/public/app/plugins/datasource/elasticsearch/bucket_agg.ts +++ b/public/app/plugins/datasource/elasticsearch/bucket_agg.ts @@ -1,4 +1,4 @@ -import angular from 'angular'; +import coreModule from 'app/core/core_module'; import _ from 'lodash'; import * as queryDef from './query_def'; @@ -226,6 +226,5 @@ export class ElasticBucketAggCtrl { } } -const module = angular.module('grafana.directives'); -module.directive('elasticBucketAgg', elasticBucketAgg); -module.controller('ElasticBucketAggCtrl', ElasticBucketAggCtrl); +coreModule.directive('elasticBucketAgg', elasticBucketAgg); +coreModule.controller('ElasticBucketAggCtrl', ElasticBucketAggCtrl); diff --git a/public/app/plugins/datasource/elasticsearch/metric_agg.ts b/public/app/plugins/datasource/elasticsearch/metric_agg.ts index 623eed68914..1dd0d892360 100644 --- a/public/app/plugins/datasource/elasticsearch/metric_agg.ts +++ b/public/app/plugins/datasource/elasticsearch/metric_agg.ts @@ -1,4 +1,4 @@ -import angular from 'angular'; +import coreModule from 'app/core/core_module'; import _ from 'lodash'; import * as queryDef from './query_def'; @@ -203,6 +203,5 @@ export class ElasticMetricAggCtrl { } } -const module = angular.module('grafana.directives'); -module.directive('elasticMetricAgg', elasticMetricAgg); -module.controller('ElasticMetricAggCtrl', ElasticMetricAggCtrl); +coreModule.directive('elasticMetricAgg', elasticMetricAgg); +coreModule.controller('ElasticMetricAggCtrl', ElasticMetricAggCtrl); diff --git a/public/app/plugins/datasource/graphite/add_graphite_func.ts b/public/app/plugins/datasource/graphite/add_graphite_func.ts index a5c1dc49959..ea3dfe8ff5e 100644 --- a/public/app/plugins/datasource/graphite/add_graphite_func.ts +++ b/public/app/plugins/datasource/graphite/add_graphite_func.ts @@ -1,8 +1,8 @@ -import angular from 'angular'; import _ from 'lodash'; import $ from 'jquery'; import rst2html from 'rst2html'; import Drop from 'tether-drop'; +import coreModule from 'app/core/core_module'; /** @ngInject */ export function graphiteAddFunc($compile) { @@ -130,7 +130,7 @@ export function graphiteAddFunc($compile) { }; } -angular.module('grafana.directives').directive('graphiteAddFunc', graphiteAddFunc); +coreModule.directive('graphiteAddFunc', graphiteAddFunc); function createFunctionDropDownMenu(funcDefs) { const categories = {}; diff --git a/public/app/plugins/datasource/graphite/func_editor.ts b/public/app/plugins/datasource/graphite/func_editor.ts index 68cc6f1452e..9e19083a9c3 100644 --- a/public/app/plugins/datasource/graphite/func_editor.ts +++ b/public/app/plugins/datasource/graphite/func_editor.ts @@ -1,7 +1,7 @@ -import angular from 'angular'; import _ from 'lodash'; import $ from 'jquery'; import rst2html from 'rst2html'; +import coreModule from 'app/core/core_module'; /** @ngInject */ export function graphiteFuncEditor($compile, templateSrv, popoverSrv) { @@ -315,4 +315,4 @@ export function graphiteFuncEditor($compile, templateSrv, popoverSrv) { }; } -angular.module('grafana.directives').directive('graphiteFuncEditor', graphiteFuncEditor); +coreModule.directive('graphiteFuncEditor', graphiteFuncEditor); diff --git a/public/app/plugins/datasource/stackdriver/query_aggregation_ctrl.ts b/public/app/plugins/datasource/stackdriver/query_aggregation_ctrl.ts index 98a1258cb15..6cd6c805463 100644 --- a/public/app/plugins/datasource/stackdriver/query_aggregation_ctrl.ts +++ b/public/app/plugins/datasource/stackdriver/query_aggregation_ctrl.ts @@ -1,4 +1,4 @@ -import angular from 'angular'; +import coreModule from 'app/core/core_module'; import _ from 'lodash'; import * as options from './constants'; import kbn from 'app/core/utils/kbn'; @@ -83,5 +83,5 @@ export class StackdriverAggregationCtrl { } } -angular.module('grafana.controllers').directive('stackdriverAggregation', StackdriverAggregation); -angular.module('grafana.controllers').controller('StackdriverAggregationCtrl', StackdriverAggregationCtrl); +coreModule.directive('stackdriverAggregation', StackdriverAggregation); +coreModule.controller('StackdriverAggregationCtrl', StackdriverAggregationCtrl); diff --git a/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts b/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts index 786b2831e89..7af76720d23 100644 --- a/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts +++ b/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts @@ -1,4 +1,4 @@ -import angular from 'angular'; +import coreModule from 'app/core/core_module'; import _ from 'lodash'; import { FilterSegments, DefaultRemoveFilterValue } from './filter_segments'; import appEvents from 'app/core/app_events'; @@ -281,5 +281,5 @@ export class StackdriverFilterCtrl { } } -angular.module('grafana.controllers').directive('stackdriverFilter', StackdriverFilter); -angular.module('grafana.controllers').controller('StackdriverFilterCtrl', StackdriverFilterCtrl); +coreModule.directive('stackdriverFilter', StackdriverFilter); +coreModule.controller('StackdriverFilterCtrl', StackdriverFilterCtrl); diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index ec4b16fbc79..7a8e24539f7 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -80,7 +80,6 @@ class GraphElement { this.annotations = this.ctrl.annotations || []; this.buildFlotPairs(this.data); const graphHeight = this.elem.height(); - console.log('graphHeight', graphHeight); updateLegendValues(this.data, this.panel, graphHeight); this.ctrl.events.emit('render-legend'); diff --git a/public/app/plugins/panel/graph/legend.ts b/public/app/plugins/panel/graph/legend.ts index cf317389941..7b01c46c4d3 100644 --- a/public/app/plugins/panel/graph/legend.ts +++ b/public/app/plugins/panel/graph/legend.ts @@ -1,11 +1,9 @@ -import angular from 'angular'; import _ from 'lodash'; import $ from 'jquery'; import baron from 'baron'; +import coreModule from 'app/core/core_module'; -const module = angular.module('grafana.directives'); - -module.directive('graphLegend', (popoverSrv, $timeout) => { +coreModule.directive('graphLegend', (popoverSrv, $timeout) => { return { link: (scope, elem) => { let firstRender = true; diff --git a/public/app/plugins/panel/graph/series_overrides_ctrl.ts b/public/app/plugins/panel/graph/series_overrides_ctrl.ts index deb7bd8ba61..540d19fb47a 100644 --- a/public/app/plugins/panel/graph/series_overrides_ctrl.ts +++ b/public/app/plugins/panel/graph/series_overrides_ctrl.ts @@ -1,5 +1,5 @@ import _ from 'lodash'; -import angular from 'angular'; +import coreModule from 'app/core/core_module'; /** @ngInject */ export function SeriesOverridesCtrl($scope, $element, popoverSrv) { @@ -156,4 +156,4 @@ export function SeriesOverridesCtrl($scope, $element, popoverSrv) { $scope.updateCurrentOverrides(); } -angular.module('grafana.controllers').controller('SeriesOverridesCtrl', SeriesOverridesCtrl); +coreModule.controller('SeriesOverridesCtrl', SeriesOverridesCtrl); diff --git a/public/app/plugins/panel/graph2/module.tsx b/public/app/plugins/panel/graph2/module.tsx index 79bf1890bae..c5245137211 100644 --- a/public/app/plugins/panel/graph2/module.tsx +++ b/public/app/plugins/panel/graph2/module.tsx @@ -1,5 +1,5 @@ import React, { PureComponent } from 'react'; -import { PanelProps } from 'app/features/dashboard/dashgrid/DataPanel'; +import { PanelProps } from 'app/types'; interface Options { showBars: boolean; @@ -22,6 +22,8 @@ export class Graph2 extends PureComponent { value = data[0].value; } + console.log('graph2 render'); + return

      Text Panel {value}

      ; } } diff --git a/public/app/plugins/panel/heatmap/color_legend.ts b/public/app/plugins/panel/heatmap/color_legend.ts index 628186569dd..0e011e59439 100644 --- a/public/app/plugins/panel/heatmap/color_legend.ts +++ b/public/app/plugins/panel/heatmap/color_legend.ts @@ -1,12 +1,10 @@ -import angular from 'angular'; import _ from 'lodash'; import $ from 'jquery'; import * as d3 from 'd3'; import { contextSrv } from 'app/core/core'; import { tickStep } from 'app/core/utils/ticks'; import { getColorScale, getOpacityScale } from './color_scale'; - -const module = angular.module('grafana.directives'); +import coreModule from 'app/core/core_module'; const LEGEND_HEIGHT_PX = 6; const LEGEND_WIDTH_PX = 100; @@ -16,7 +14,7 @@ const LEGEND_VALUE_MARGIN = 0; /** * Color legend for heatmap editor. */ -module.directive('colorLegend', () => { +coreModule.directive('colorLegend', () => { return { restrict: 'E', template: '
      ', @@ -52,7 +50,7 @@ module.directive('colorLegend', () => { /** * Heatmap legend with scale values. */ -module.directive('heatmapLegend', () => { +coreModule.directive('heatmapLegend', () => { return { restrict: 'E', template: `
      `, diff --git a/public/app/plugins/panel/text2/module.tsx b/public/app/plugins/panel/text2/module.tsx index df00b85176a..9fcd89b612c 100644 --- a/public/app/plugins/panel/text2/module.tsx +++ b/public/app/plugins/panel/text2/module.tsx @@ -1,5 +1,5 @@ import React, { PureComponent } from 'react'; -import { PanelProps } from 'app/features/dashboard/dashgrid/DataPanel'; +import { PanelProps } from 'app/types'; export class Text2 extends PureComponent { constructor(props) { @@ -14,7 +14,7 @@ export class Text2 extends PureComponent { value = data[0].value; } - return

      Graph Panel! {value}

      ; + return

      Text Panel! {value}

      ; } } diff --git a/public/app/types/index.ts b/public/app/types/index.ts index 26f15d582ac..9a1045f8c7c 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -9,6 +9,8 @@ import { ApiKey, ApiKeysState, NewApiKey } from './apiKeys'; import { Invitee, OrgUser, User, UsersState } from './user'; import { DataSource, DataSourcesState } from './datasources'; import { PluginMeta, Plugin, PluginsState } from './plugins'; +import { TimeRange, LoadingState } from './queries'; +import { PanelProps } from './panel'; export { Team, @@ -45,6 +47,9 @@ export { OrgUser, User, UsersState, + TimeRange, + LoadingState, + PanelProps, }; export interface StoreState { diff --git a/public/app/types/panel.ts b/public/app/types/panel.ts new file mode 100644 index 00000000000..4cb93e5728d --- /dev/null +++ b/public/app/types/panel.ts @@ -0,0 +1,6 @@ +import { LoadingState } from './queries'; + +export interface PanelProps { + data: any; + loading: LoadingState; +} diff --git a/public/app/types/queries.ts b/public/app/types/queries.ts new file mode 100644 index 00000000000..5c350efbb08 --- /dev/null +++ b/public/app/types/queries.ts @@ -0,0 +1,19 @@ +import { Moment } from 'moment'; + +export enum LoadingState { + NotStarted = 'NotStarted', + Loading = 'Loading', + Done = 'Done', + Error = 'Error', +} + +export interface RawTimeRange { + from: Moment | string; + to: Moment | string; +} + +export interface TimeRange { + from: Moment; + to: Moment; + raw: RawTimeRange; +} From 543c67a2974e1cf58c2cf210216c5f1a334a1992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 14 Oct 2018 16:31:20 +0200 Subject: [PATCH 45/54] react panels: got data --- public/app/app.ts | 45 ++++++++----------- public/app/core/core.ts | 2 - public/app/core/core_module.ts | 19 +++++++- .../features/dashboard/dashgrid/DataPanel.tsx | 16 ++++++- .../dashboard/dashgrid/PanelChrome.tsx | 7 +-- .../dashboard/dashgrid/QueriesTab.tsx | 4 -- public/app/features/panel/metrics_tab.ts | 3 ++ .../grafana_app.ts => routes/GrafanaCtrl.ts} | 0 8 files changed, 57 insertions(+), 39 deletions(-) rename public/app/{core/components/grafana_app.ts => routes/GrafanaCtrl.ts} (100%) diff --git a/public/app/app.ts b/public/app/app.ts index 298bf5609cd..9647fbe5416 100644 --- a/public/app/app.ts +++ b/public/app/app.ts @@ -26,8 +26,12 @@ _.move = (array, fromIndex, toIndex) => { return array; }; -import { coreModule, registerAngularDirectives } from './core/core'; -import { setupAngularRoutes } from './routes/routes'; +import { coreModule, angularModules } from 'app/core/core_module'; +import { registerAngularDirectives } from 'app/core/core'; +import { setupAngularRoutes } from 'app/routes/routes'; + +import 'app/routes/GrafanaCtrl'; +import 'app/features/all'; // import symlinked extensions const extensionsIndex = (require as any).context('.', true, /extensions\/index.ts/); @@ -109,39 +113,26 @@ export class GrafanaApp { 'react', ]; - const moduleTypes = ['controllers', 'directives', 'factories', 'services', 'filters', 'routes']; - - _.each(moduleTypes, type => { - const moduleName = 'grafana.' + type; - this.useModule(angular.module(moduleName, [])); - }); - // makes it possible to add dynamic stuff - this.useModule(coreModule); + _.each(angularModules, m => { + this.useModule(m); + }); // register react angular wrappers coreModule.config(setupAngularRoutes); registerAngularDirectives(); - const preBootRequires = [import('app/features/all')]; + // disable tool tip animation + $.fn.tooltip.defaults.animation = false; - Promise.all(preBootRequires) - .then(() => { - // disable tool tip animation - $.fn.tooltip.defaults.animation = false; - - // bootstrap the app - angular.bootstrap(document, this.ngModuleDependencies).invoke(() => { - _.each(this.preBootModules, module => { - _.extend(module, this.registerFunctions); - }); - - this.preBootModules = null; - }); - }) - .catch(err => { - console.log('Application boot failed:', err); + // bootstrap the app + angular.bootstrap(document, this.ngModuleDependencies).invoke(() => { + _.each(this.preBootModules, module => { + _.extend(module, this.registerFunctions); }); + + this.preBootModules = null; + }); } } diff --git a/public/app/core/core.ts b/public/app/core/core.ts index 173d6b80b15..18a625d3307 100644 --- a/public/app/core/core.ts +++ b/public/app/core/core.ts @@ -19,7 +19,6 @@ import './components/colorpicker/spectrum_picker'; import './services/search_srv'; import './services/ng_react'; -import { grafanaAppDirective } from './components/grafana_app'; import { searchDirective } from './components/search/search'; import { infoPopover } from './components/info_popover'; import { navbarDirective } from './components/navbar/navbar'; @@ -60,7 +59,6 @@ export { registerAngularDirectives, arrayJoin, coreModule, - grafanaAppDirective, navbarDirective, searchDirective, liveSrv, diff --git a/public/app/core/core_module.ts b/public/app/core/core_module.ts index f6c30e6cf15..94b0aeaff7f 100644 --- a/public/app/core/core_module.ts +++ b/public/app/core/core_module.ts @@ -1,2 +1,19 @@ import angular from 'angular'; -export default angular.module('grafana.core', ['ngRoute']); + +console.log('core module code'); +const coreModule = angular.module('grafana.core', ['ngRoute']); + +// legacy modules +const angularModules = [ + coreModule, + angular.module('grafana.controllers', []), + angular.module('grafana.directives', []), + angular.module('grafana.factories', []), + angular.module('grafana.services', []), + angular.module('grafana.filters', []), + angular.module('grafana.routes', []), +]; + +export { angularModules, coreModule }; + +export default coreModule; diff --git a/public/app/features/dashboard/dashgrid/DataPanel.tsx b/public/app/features/dashboard/dashgrid/DataPanel.tsx index 6b8aa8c4a91..a786e9de6d1 100644 --- a/public/app/features/dashboard/dashgrid/DataPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DataPanel.tsx @@ -1,5 +1,5 @@ // Library -import React, { PureComponent } from 'react'; +import React, { Component } from 'react'; // Services import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; @@ -29,7 +29,7 @@ export interface State { data: any; } -export class DataPanel extends PureComponent { +export class DataPanel extends Component { static defaultProps = { isVisible: true, panelId: 1, @@ -48,9 +48,20 @@ export class DataPanel extends PureComponent { componentDidMount() { console.log('DataPanel mount'); + } + + async componentDidUpdate(prevProps: Props) { + if (!this.hasPropsChanged(prevProps)) { + return; + } + this.issueQueries(); } + hasPropsChanged(prevProps: Props) { + return this.props.refreshCounter !== prevProps.refreshCounter || this.props.isVisible !== prevProps.isVisible; + } + issueQueries = async () => { const { isVisible, queries, datasource, panelId, dashboardId, timeRange } = this.props; @@ -83,6 +94,7 @@ export class DataPanel extends PureComponent { cacheTimeout: null, }; + console.log('issueing react query', queryOptions); const resp = await ds.query(queryOptions); console.log(resp); } catch (err) { diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index 29797664cd9..9a4318e602f 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -34,8 +34,8 @@ export class PanelChrome extends PureComponent { } componentDidMount() { - this.props.dashboard.panelInitialized(this.props.panel); this.props.panel.events.on('refresh', this.onRefresh); + this.props.dashboard.panelInitialized(this.props.panel); } componentWillUnmount() { @@ -53,13 +53,13 @@ export class PanelChrome extends PureComponent { }; get isVisible() { - return this.props.dashboard.otherPanelInFullscreen(this.props.panel); + return !this.props.dashboard.otherPanelInFullscreen(this.props.panel); } render() { const { panel, dashboard } = this.props; const { datasource, targets } = panel; - const { refreshCounter } = this.state; + const { refreshCounter, timeRange } = this.state; const PanelComponent = this.props.component; return ( @@ -69,6 +69,7 @@ export class PanelChrome extends PureComponent { diff --git a/public/app/features/dashboard/dashgrid/QueriesTab.tsx b/public/app/features/dashboard/dashgrid/QueriesTab.tsx index 671075b8c47..efc6a6c0048 100644 --- a/public/app/features/dashboard/dashgrid/QueriesTab.tsx +++ b/public/app/features/dashboard/dashgrid/QueriesTab.tsx @@ -23,10 +23,6 @@ export class QueriesTab extends React.Component { const { panel, dashboard } = this.props; - // make sure the panel has datasource & queries properties - panel.datasource = panel.datasource || null; - panel.targets = panel.targets || [{}]; - const loader = getAngularLoader(); const template = ''; const scopeProps = { diff --git a/public/app/features/panel/metrics_tab.ts b/public/app/features/panel/metrics_tab.ts index c5f945778de..18b1102875e 100644 --- a/public/app/features/panel/metrics_tab.ts +++ b/public/app/features/panel/metrics_tab.ts @@ -25,6 +25,9 @@ export class MetricsTabCtrl { $scope.ctrl = this; this.panel = this.panelCtrl.panel; + this.panel.datasource = this.panel.datasource || null; + this.panel.targets = this.panel.targets || [{}]; + this.dashboard = this.panelCtrl.dashboard; this.datasources = datasourceSrv.getMetricSources(); this.panelDsValue = this.panelCtrl.panel.datasource; diff --git a/public/app/core/components/grafana_app.ts b/public/app/routes/GrafanaCtrl.ts similarity index 100% rename from public/app/core/components/grafana_app.ts rename to public/app/routes/GrafanaCtrl.ts From 8e85295b2b981442dc0a18bfec8128389eba258d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 14 Oct 2018 18:19:49 +0200 Subject: [PATCH 46/54] react panels query processing --- public/app/core/core_module.ts | 1 - .../features/dashboard/dashgrid/DataPanel.tsx | 26 ++++++---- .../dashboard/dashgrid/PanelChrome.tsx | 4 +- public/app/features/plugins/datasource_srv.ts | 8 ++- public/app/plugins/panel/graph/module.ts | 1 + public/app/plugins/panel/graph2/module.tsx | 30 +++++++---- public/app/plugins/panel/text2/module.tsx | 9 +--- public/app/types/index.ts | 6 ++- public/app/types/panel.ts | 4 +- public/app/types/queries.ts | 19 ------- public/app/types/series.ts | 50 +++++++++++++++++++ 11 files changed, 106 insertions(+), 52 deletions(-) delete mode 100644 public/app/types/queries.ts create mode 100644 public/app/types/series.ts diff --git a/public/app/core/core_module.ts b/public/app/core/core_module.ts index 94b0aeaff7f..c8401975c18 100644 --- a/public/app/core/core_module.ts +++ b/public/app/core/core_module.ts @@ -1,6 +1,5 @@ import angular from 'angular'; -console.log('core module code'); const coreModule = angular.module('grafana.core', ['ngRoute']); // legacy modules diff --git a/public/app/features/dashboard/dashgrid/DataPanel.tsx b/public/app/features/dashboard/dashgrid/DataPanel.tsx index a786e9de6d1..8014e06f43f 100644 --- a/public/app/features/dashboard/dashgrid/DataPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DataPanel.tsx @@ -5,11 +5,11 @@ import React, { Component } from 'react'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; // Types -import { TimeRange, LoadingState } from 'app/types'; +import { TimeRange, LoadingState, DataQueryResponse, TimeSeries } from 'app/types'; interface RenderProps { loading: LoadingState; - data: any; + timeSeries: TimeSeries[]; } export interface Props { @@ -26,7 +26,7 @@ export interface Props { export interface State { isFirstLoad: boolean; loading: LoadingState; - data: any; + response: DataQueryResponse; } export class DataPanel extends Component { @@ -41,7 +41,9 @@ export class DataPanel extends Component { this.state = { loading: LoadingState.NotStarted, - data: [], + response: { + data: [], + }, isFirstLoad: true, }; } @@ -70,7 +72,7 @@ export class DataPanel extends Component { } if (!queries.length) { - this.setState({ data: [], loading: LoadingState.Done }); + this.setState({ loading: LoadingState.Done }); return; } @@ -94,9 +96,14 @@ export class DataPanel extends Component { cacheTimeout: null, }; - console.log('issueing react query', queryOptions); + console.log('Issuing DataPanel query', queryOptions); const resp = await ds.query(queryOptions); - console.log(resp); + console.log('Issuing DataPanel query Resp', resp); + + this.setState({ + loading: LoadingState.Done, + response: resp, + }); } catch (err) { console.log('Loading error', err); this.setState({ loading: LoadingState.Error }); @@ -104,8 +111,9 @@ export class DataPanel extends Component { }; render() { - const { data, loading, isFirstLoad } = this.state; + const { response, loading, isFirstLoad } = this.state; console.log('data panel render'); + const timeSeries = response.data; if (isFirstLoad && (loading === LoadingState.Loading || loading === LoadingState.NotStarted)) { return ( @@ -119,7 +127,7 @@ export class DataPanel extends Component { <> {this.loadingSpinner} {this.props.children({ - data, + timeSeries, loading, })} diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index 9a4318e602f..d3e63d93e43 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -73,8 +73,8 @@ export class PanelChrome extends PureComponent { isVisible={this.isVisible} refreshCounter={refreshCounter} > - {({ loading, data }) => { - return ; + {({ loading, timeSeries }) => { + return ; }}
      diff --git a/public/app/features/plugins/datasource_srv.ts b/public/app/features/plugins/datasource_srv.ts index 22861eaeef8..71a417a882f 100644 --- a/public/app/features/plugins/datasource_srv.ts +++ b/public/app/features/plugins/datasource_srv.ts @@ -1,8 +1,14 @@ +// Libraries import _ from 'lodash'; import coreModule from 'app/core/core_module'; + +// Utils import config from 'app/core/config'; import { importPluginModule } from './plugin_loader'; +// Types +import { DataSourceApi } from 'app/types/series'; + export class DatasourceSrv { datasources: any; @@ -15,7 +21,7 @@ export class DatasourceSrv { this.datasources = {}; } - get(name?) { + get(name?): Promise { if (!name) { return this.get(config.defaultDatasource); } diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index 584b58ae3ce..1ebda67d41a 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -179,6 +179,7 @@ class GraphCtrl extends MetricsPanelCtrl { } onDataReceived(dataList) { + console.log(dataList); this.dataList = dataList; this.seriesList = this.processor.getSeriesList({ dataList: dataList, diff --git a/public/app/plugins/panel/graph2/module.tsx b/public/app/plugins/panel/graph2/module.tsx index c5245137211..7132efc9ac0 100644 --- a/public/app/plugins/panel/graph2/module.tsx +++ b/public/app/plugins/panel/graph2/module.tsx @@ -1,4 +1,8 @@ +// Libraries +import _ from 'lodash'; import React, { PureComponent } from 'react'; + +// Types import { PanelProps } from 'app/types'; interface Options { @@ -15,16 +19,24 @@ export class Graph2 extends PureComponent { } render() { - const { data } = this.props; - let value = 0; + const { timeSeries } = this.props; + let index = 0; - if (data.length) { - value = data[0].value; - } - - console.log('graph2 render'); - - return

      Text Panel {value}

      ; + return ( + + + {timeSeries.map(series => { + return ( + + + + + + ); + })} + +
      {series.target}{series.datapoints[0][0]}{series.datapoints[0][1]}
      + ); } } diff --git a/public/app/plugins/panel/text2/module.tsx b/public/app/plugins/panel/text2/module.tsx index 9fcd89b612c..b10dc8b545e 100644 --- a/public/app/plugins/panel/text2/module.tsx +++ b/public/app/plugins/panel/text2/module.tsx @@ -7,14 +7,7 @@ export class Text2 extends PureComponent { } render() { - const { data } = this.props; - let value = 0; - - if (data.length) { - value = data[0].value; - } - - return

      Text Panel! {value}

      ; + return

      Text Panel!

      ; } } diff --git a/public/app/types/index.ts b/public/app/types/index.ts index 9a1045f8c7c..f13a55b5361 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -9,7 +9,7 @@ import { ApiKey, ApiKeysState, NewApiKey } from './apiKeys'; import { Invitee, OrgUser, User, UsersState } from './user'; import { DataSource, DataSourcesState } from './datasources'; import { PluginMeta, Plugin, PluginsState } from './plugins'; -import { TimeRange, LoadingState } from './queries'; +import { TimeRange, LoadingState, TimeSeries, DataQuery, DataQueryResponse, DataQueryOptions } from './series'; import { PanelProps } from './panel'; export { @@ -50,6 +50,10 @@ export { TimeRange, LoadingState, PanelProps, + TimeSeries, + DataQuery, + DataQueryResponse, + DataQueryOptions, }; export interface StoreState { diff --git a/public/app/types/panel.ts b/public/app/types/panel.ts index 4cb93e5728d..8d12cb6ef21 100644 --- a/public/app/types/panel.ts +++ b/public/app/types/panel.ts @@ -1,6 +1,6 @@ -import { LoadingState } from './queries'; +import { LoadingState, TimeSeries } from './series'; export interface PanelProps { - data: any; + timeSeries: TimeSeries[]; loading: LoadingState; } diff --git a/public/app/types/queries.ts b/public/app/types/queries.ts deleted file mode 100644 index 5c350efbb08..00000000000 --- a/public/app/types/queries.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Moment } from 'moment'; - -export enum LoadingState { - NotStarted = 'NotStarted', - Loading = 'Loading', - Done = 'Done', - Error = 'Error', -} - -export interface RawTimeRange { - from: Moment | string; - to: Moment | string; -} - -export interface TimeRange { - from: Moment; - to: Moment; - raw: RawTimeRange; -} diff --git a/public/app/types/series.ts b/public/app/types/series.ts new file mode 100644 index 00000000000..006410a5757 --- /dev/null +++ b/public/app/types/series.ts @@ -0,0 +1,50 @@ +import { Moment } from 'moment'; + +export enum LoadingState { + NotStarted = 'NotStarted', + Loading = 'Loading', + Done = 'Done', + Error = 'Error', +} + +export interface RawTimeRange { + from: Moment | string; + to: Moment | string; +} + +export interface TimeRange { + from: Moment; + to: Moment; + raw: RawTimeRange; +} + +export type TimeSeriesValue = string | number | null; + +export type TimeSeriesPoints = TimeSeriesValue[][]; + +export interface TimeSeries { + target: string; + datapoints: TimeSeriesPoints; +} + +export interface DataQueryResponse { + data: TimeSeries[]; +} + +export interface DataQuery { + refId: string; +} + +export interface DataQueryOptions { + timezone: string; + range: TimeRange; + rangeRaw: RawTimeRange; + targets: DataQuery[]; + panelId: number; + dashboardId: number; + cacheTimeout?: string; +} + +export interface DataSourceApi { + query(options: DataQueryOptions): Promise; +} From 2fb721d3c684420b85736776afed47534b8c91f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 14 Oct 2018 21:14:11 +0200 Subject: [PATCH 47/54] various fixes to to queries tab (in react mode) --- .../dashboard/dashgrid/QueriesTab.tsx | 16 +++++----- public/app/features/dashboard/panel_model.ts | 12 +++++++- .../features/dashboard/specs/exporter.test.ts | 2 +- .../app/features/panel/metrics_panel_ctrl.ts | 22 -------------- public/app/features/panel/metrics_tab.ts | 30 +++++++++++++++++-- public/app/plugins/panel/graph/module.ts | 1 - 6 files changed, 49 insertions(+), 34 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/QueriesTab.tsx b/public/app/features/dashboard/dashgrid/QueriesTab.tsx index efc6a6c0048..f13f212826a 100644 --- a/public/app/features/dashboard/dashgrid/QueriesTab.tsx +++ b/public/app/features/dashboard/dashgrid/QueriesTab.tsx @@ -1,14 +1,19 @@ -import React from 'react'; +// Libraries +import React, { PureComponent } from 'react'; + +// Services & utils +import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader'; + +// Types import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; -import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader'; interface Props { panel: PanelModel; dashboard: DashboardModel; } -export class QueriesTab extends React.Component { +export class QueriesTab extends PureComponent { element: any; component: AngularComponent; @@ -29,10 +34,7 @@ export class QueriesTab extends React.Component { ctrl: { panel: panel, dashboard: dashboard, - panelCtrl: { - panel: panel, - dashboard: dashboard, - }, + refresh: () => panel.refresh(), }, }; diff --git a/public/app/features/dashboard/panel_model.ts b/public/app/features/dashboard/panel_model.ts index 415238d4e7b..ebf8a6bb224 100644 --- a/public/app/features/dashboard/panel_model.ts +++ b/public/app/features/dashboard/panel_model.ts @@ -16,6 +16,12 @@ const notPersistedProperties: { [str: string]: boolean } = { hasRefreshed: true, }; +const defaults: any = { + gridPos: { x: 0, y: 0, h: 3, w: 6 }, + datasource: null, + targets: [{}], +}; + export class PanelModel { id: number; gridPos: GridPos; @@ -51,7 +57,7 @@ export class PanelModel { } // defaults - this.gridPos = this.gridPos || { x: 0, y: 0, h: 3, w: 6 }; + _.defaultsDeep(this, _.cloneDeep(defaults)); } getSaveModel() { @@ -61,6 +67,10 @@ export class PanelModel { continue; } + if (_.isEqual(this[property], defaults[property])) { + continue; + } + model[property] = _.cloneDeep(this[property]); } diff --git a/public/app/features/dashboard/specs/exporter.test.ts b/public/app/features/dashboard/specs/exporter.test.ts index c7a232f925b..f21e151f3dd 100644 --- a/public/app/features/dashboard/specs/exporter.test.ts +++ b/public/app/features/dashboard/specs/exporter.test.ts @@ -240,5 +240,5 @@ stubs['-- Grafana --'] = { }; function getStub(arg) { - return Promise.resolve(stubs[arg]); + return Promise.resolve(stubs[arg || 'gfdb']); } diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts index 1cdf7c6953d..aa25308e121 100644 --- a/public/app/features/panel/metrics_panel_ctrl.ts +++ b/public/app/features/panel/metrics_panel_ctrl.ts @@ -12,7 +12,6 @@ import { metricsTabDirective } from './metrics_tab'; class MetricsPanelCtrl extends PanelCtrl { scope: any; datasource: any; - datasourceName: any; $q: any; $timeout: any; contextSrv: any; @@ -287,27 +286,6 @@ class MetricsPanelCtrl extends PanelCtrl { }); } - setDatasource(datasource) { - // switching to mixed - if (datasource.meta.mixed) { - _.each(this.panel.targets, target => { - target.datasource = this.panel.datasource; - if (!target.datasource) { - target.datasource = config.defaultDatasource; - } - }); - } else if (this.datasource && this.datasource.meta.mixed) { - _.each(this.panel.targets, target => { - delete target.datasource; - }); - } - - this.panel.datasource = datasource.value; - this.datasourceName = datasource.name; - this.datasource = null; - this.refresh(); - } - getAdditionalMenuItems() { const items = []; if ( diff --git a/public/app/features/panel/metrics_tab.ts b/public/app/features/panel/metrics_tab.ts index 18b1102875e..f520b5eefc0 100644 --- a/public/app/features/panel/metrics_tab.ts +++ b/public/app/features/panel/metrics_tab.ts @@ -1,6 +1,13 @@ -import { DashboardModel } from '../dashboard/dashboard_model'; +// Libraries +import _ from 'lodash'; import Remarkable from 'remarkable'; + +// Services & utils import coreModule from 'app/core/core_module'; +import config from 'app/core/config'; + +// Types +import { DashboardModel } from '../dashboard/dashboard_model'; export class MetricsTabCtrl { dsName: string; @@ -70,10 +77,29 @@ export class MetricsTabCtrl { } this.datasourceInstance = option.datasource; - this.panelCtrl.setDatasource(option.datasource); + this.setDatasource(option.datasource); this.updateDatasourceOptions(); } + setDatasource(datasource) { + // switching to mixed + if (datasource.meta.mixed) { + _.each(this.panel.targets, target => { + target.datasource = this.panel.datasource; + if (!target.datasource) { + target.datasource = config.defaultDatasource; + } + }); + } else if (this.datasourceInstance && this.datasourceInstance.meta.mixed) { + _.each(this.panel.targets, target => { + delete target.datasource; + }); + } + + this.panel.datasource = datasource.value; + this.panel.refresh(); + } + addMixedQuery(option) { if (!option) { return; diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index 1ebda67d41a..584b58ae3ce 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -179,7 +179,6 @@ class GraphCtrl extends MetricsPanelCtrl { } onDataReceived(dataList) { - console.log(dataList); this.dataList = dataList; this.seriesList = this.processor.getSeriesList({ dataList: dataList, From 9adad76f5211b00257e255c5786ef8155ed03beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 14 Oct 2018 22:24:18 +0200 Subject: [PATCH 48/54] wip: began first steps for a react graph component --- public/app/plugins/panel/graph2/module.tsx | 19 +-- public/app/types/index.ts | 11 +- public/app/types/panel.ts | 3 +- public/app/types/series.ts | 7 + public/app/viz/Graph.tsx | 126 ++++++++++++++++++ .../app/viz/state/getTimeSeriesViewModel.ts | 9 ++ 6 files changed, 158 insertions(+), 17 deletions(-) create mode 100644 public/app/viz/Graph.tsx create mode 100644 public/app/viz/state/getTimeSeriesViewModel.ts diff --git a/public/app/plugins/panel/graph2/module.tsx b/public/app/plugins/panel/graph2/module.tsx index 7132efc9ac0..a984e6a2a6f 100644 --- a/public/app/plugins/panel/graph2/module.tsx +++ b/public/app/plugins/panel/graph2/module.tsx @@ -2,6 +2,9 @@ import _ from 'lodash'; import React, { PureComponent } from 'react'; +// Components +import { Graph } from 'app/viz/Graph'; + // Types import { PanelProps } from 'app/types'; @@ -22,21 +25,7 @@ export class Graph2 extends PureComponent { const { timeSeries } = this.props; let index = 0; - return ( - - - {timeSeries.map(series => { - return ( - - - - - - ); - })} - -
      {series.target}{series.datapoints[0][0]}{series.datapoints[0][1]}
      - ); + return ; } } diff --git a/public/app/types/index.ts b/public/app/types/index.ts index f13a55b5361..8d827a6c286 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -9,7 +9,15 @@ import { ApiKey, ApiKeysState, NewApiKey } from './apiKeys'; import { Invitee, OrgUser, User, UsersState } from './user'; import { DataSource, DataSourcesState } from './datasources'; import { PluginMeta, Plugin, PluginsState } from './plugins'; -import { TimeRange, LoadingState, TimeSeries, DataQuery, DataQueryResponse, DataQueryOptions } from './series'; +import { + TimeRange, + LoadingState, + TimeSeries, + DataQuery, + DataQueryResponse, + DataQueryOptions, + TimeSeriesViewModel, +} from './series'; import { PanelProps } from './panel'; export { @@ -51,6 +59,7 @@ export { LoadingState, PanelProps, TimeSeries, + TimeSeriesViewModel, DataQuery, DataQueryResponse, DataQueryOptions, diff --git a/public/app/types/panel.ts b/public/app/types/panel.ts index 8d12cb6ef21..5ece77fc5aa 100644 --- a/public/app/types/panel.ts +++ b/public/app/types/panel.ts @@ -1,6 +1,7 @@ -import { LoadingState, TimeSeries } from './series'; +import { LoadingState, TimeSeries, TimeRange } from './series'; export interface PanelProps { timeSeries: TimeSeries[]; + timeRange: TimeRange; loading: LoadingState; } diff --git a/public/app/types/series.ts b/public/app/types/series.ts index 006410a5757..dc1d1d7d393 100644 --- a/public/app/types/series.ts +++ b/public/app/types/series.ts @@ -25,6 +25,13 @@ export type TimeSeriesPoints = TimeSeriesValue[][]; export interface TimeSeries { target: string; datapoints: TimeSeriesPoints; + unit?: string; +} + +export interface TimeSeriesViewModel { + label: string; + color: string; + data: number[][]; } export interface DataQueryResponse { diff --git a/public/app/viz/Graph.tsx b/public/app/viz/Graph.tsx new file mode 100644 index 00000000000..ed638e7bd16 --- /dev/null +++ b/public/app/viz/Graph.tsx @@ -0,0 +1,126 @@ +// Libraries +import $ from 'jquery'; +import React, { PureComponent } from 'react'; +import { withSize } from 'react-sizeme'; +import 'vendor/flot/jquery.flot'; +import 'vendor/flot/jquery.flot.time'; + +// Types +import TimeSeries from 'app/core/time_series2'; +import { TimeRange } from 'app/types'; + +// Copied from graph.ts +function time_format(ticks, min, max) { + if (min && max && ticks) { + const range = max - min; + const secPerTick = range / ticks / 1000; + const oneDay = 86400000; + const oneYear = 31536000000; + + if (secPerTick <= 45) { + return '%H:%M:%S'; + } + if (secPerTick <= 7200 || range <= oneDay) { + return '%H:%M'; + } + if (secPerTick <= 80000) { + return '%m/%d %H:%M'; + } + if (secPerTick <= 2419200 || range <= oneYear) { + return '%m/%d'; + } + return '%Y-%m'; + } + + return '%H:%M'; +} + +const FLOT_OPTIONS = { + legend: { + show: false, + }, + series: { + lines: { + linewidth: 1, + zero: false, + }, + shadowSize: 0, + }, + grid: { + minBorderMargin: 0, + markings: [], + backgroundColor: null, + borderWidth: 0, + // hoverable: true, + clickable: true, + color: '#a1a1a1', + margin: { left: 0, right: 0 }, + labelMarginX: 0, + }, +}; + +interface GraphProps { + timeSeries: TimeSeries[]; + timeRange: TimeRange; + size?: { width: number; height: number }; +} + +export class Graph extends PureComponent { + element: any; + + componentDidUpdate(prevProps: GraphProps) { + if ( + prevProps.timeSeries !== this.props.timeSeries || + prevProps.timeRange !== this.props.timeRange || + prevProps.size !== this.props.size + ) { + this.draw(); + } + } + + componentDidMount() { + this.draw(); + } + + draw() { + const { size, timeSeries, timeRange } = this.props; + + const data = timeSeries.map((ts: TimeSeries) => ({ + color: ts.color, + label: ts.label, + data: ts.getFlotPairs('null'), + })); + + const ticks = (size.width || 0) / 100; + const min = timeRange.from.valueOf(); + const max = timeRange.to.valueOf(); + + const dynamicOptions = { + xaxis: { + mode: 'time', + min: min, + max: max, + label: 'Datetime', + ticks: ticks, + timeformat: time_format(ticks, min, max), + }, + }; + + const options = { + ...FLOT_OPTIONS, + ...dynamicOptions, + }; + + $.plot(this.element, data, options); + } + + render() { + return ( +
      +
      (this.element = e)} /> +
      + ); + } +} + +export default withSize()(Graph); diff --git a/public/app/viz/state/getTimeSeriesViewModel.ts b/public/app/viz/state/getTimeSeriesViewModel.ts new file mode 100644 index 00000000000..9ad2216d135 --- /dev/null +++ b/public/app/viz/state/getTimeSeriesViewModel.ts @@ -0,0 +1,9 @@ +import colors from 'app/core/utils/colors'; +import { TimeSeries, TimeSeriesViewModel } from 'app/types'; + +interface Options { + ts: TimeSeries; + seriesIndex: number; +} + +export function getTimeSeriesViewModel(ts: TimeSeries): TimeSeriesViewModel {} From a28c25a2e026ed5c0544a93b9d3f9baa077525e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 15 Oct 2018 08:22:20 +0200 Subject: [PATCH 49/54] progress on react time series infra --- .../dashboard/dashgrid/PanelChrome.tsx | 2 +- public/app/plugins/panel/graph2/module.tsx | 12 +++++++---- public/app/types/index.ts | 6 ++++-- public/app/types/series.ts | 10 +++++++-- public/app/viz/Graph.tsx | 15 ++++++------- .../app/viz/state/getTimeSeriesViewModel.ts | 9 -------- public/app/viz/state/timeSeries.ts | 21 +++++++++++++++++++ 7 files changed, 48 insertions(+), 27 deletions(-) delete mode 100644 public/app/viz/state/getTimeSeriesViewModel.ts create mode 100644 public/app/viz/state/timeSeries.ts diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index d3e63d93e43..82b366d8126 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -74,7 +74,7 @@ export class PanelChrome extends PureComponent { refreshCounter={refreshCounter} > {({ loading, timeSeries }) => { - return ; + return ; }}
      diff --git a/public/app/plugins/panel/graph2/module.tsx b/public/app/plugins/panel/graph2/module.tsx index a984e6a2a6f..e8d594e7968 100644 --- a/public/app/plugins/panel/graph2/module.tsx +++ b/public/app/plugins/panel/graph2/module.tsx @@ -3,7 +3,10 @@ import _ from 'lodash'; import React, { PureComponent } from 'react'; // Components -import { Graph } from 'app/viz/Graph'; +import Graph from 'app/viz/Graph'; + +// Utils +import { getTimeSeriesVMs } from 'app/viz/state/timeSeries'; // Types import { PanelProps } from 'app/types'; @@ -22,10 +25,11 @@ export class Graph2 extends PureComponent { } render() { - const { timeSeries } = this.props; - let index = 0; + const { timeSeries, timeRange } = this.props; + const viewModels = getTimeSeriesVMs({ timeSeries }); + console.log(viewModels); - return ; + return ; } } diff --git a/public/app/types/index.ts b/public/app/types/index.ts index 8d827a6c286..d274c99253c 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -13,10 +13,11 @@ import { TimeRange, LoadingState, TimeSeries, + TimeSeriesVM, + TimeSeriesVMs, DataQuery, DataQueryResponse, DataQueryOptions, - TimeSeriesViewModel, } from './series'; import { PanelProps } from './panel'; @@ -59,7 +60,8 @@ export { LoadingState, PanelProps, TimeSeries, - TimeSeriesViewModel, + TimeSeriesVM, + TimeSeriesVMs, DataQuery, DataQueryResponse, DataQueryOptions, diff --git a/public/app/types/series.ts b/public/app/types/series.ts index dc1d1d7d393..9a906377636 100644 --- a/public/app/types/series.ts +++ b/public/app/types/series.ts @@ -28,10 +28,16 @@ export interface TimeSeries { unit?: string; } -export interface TimeSeriesViewModel { +/** View model projection of a time series */ +export interface TimeSeriesVM { label: string; color: string; - data: number[][]; + data: TimeSeriesValue[][]; +} + +/** View model projection of many time series */ +export interface TimeSeriesVMs { + [index: number]: TimeSeriesVM; } export interface DataQueryResponse { diff --git a/public/app/viz/Graph.tsx b/public/app/viz/Graph.tsx index ed638e7bd16..38d29af8da9 100644 --- a/public/app/viz/Graph.tsx +++ b/public/app/viz/Graph.tsx @@ -6,8 +6,7 @@ import 'vendor/flot/jquery.flot'; import 'vendor/flot/jquery.flot.time'; // Types -import TimeSeries from 'app/core/time_series2'; -import { TimeRange } from 'app/types'; +import { TimeRange, TimeSeriesVMs } from 'app/types'; // Copied from graph.ts function time_format(ticks, min, max) { @@ -60,7 +59,7 @@ const FLOT_OPTIONS = { }; interface GraphProps { - timeSeries: TimeSeries[]; + timeSeries: TimeSeriesVMs; timeRange: TimeRange; size?: { width: number; height: number }; } @@ -85,11 +84,9 @@ export class Graph extends PureComponent { draw() { const { size, timeSeries, timeRange } = this.props; - const data = timeSeries.map((ts: TimeSeries) => ({ - color: ts.color, - label: ts.label, - data: ts.getFlotPairs('null'), - })); + if (!size) { + return; + } const ticks = (size.width || 0) / 100; const min = timeRange.from.valueOf(); @@ -111,7 +108,7 @@ export class Graph extends PureComponent { ...dynamicOptions, }; - $.plot(this.element, data, options); + $.plot(this.element, timeSeries, options); } render() { diff --git a/public/app/viz/state/getTimeSeriesViewModel.ts b/public/app/viz/state/getTimeSeriesViewModel.ts deleted file mode 100644 index 9ad2216d135..00000000000 --- a/public/app/viz/state/getTimeSeriesViewModel.ts +++ /dev/null @@ -1,9 +0,0 @@ -import colors from 'app/core/utils/colors'; -import { TimeSeries, TimeSeriesViewModel } from 'app/types'; - -interface Options { - ts: TimeSeries; - seriesIndex: number; -} - -export function getTimeSeriesViewModel(ts: TimeSeries): TimeSeriesViewModel {} diff --git a/public/app/viz/state/timeSeries.ts b/public/app/viz/state/timeSeries.ts new file mode 100644 index 00000000000..7658a2b2120 --- /dev/null +++ b/public/app/viz/state/timeSeries.ts @@ -0,0 +1,21 @@ +import colors from 'app/core/utils/colors'; +import { TimeSeries, TimeSeriesVMs } from 'app/types'; + +interface Options { + timeSeries: TimeSeries[]; +} + +export function getTimeSeriesVMs({ timeSeries }: Options): TimeSeriesVMs { + const vmSeries = timeSeries.map((item, index) => { + const colorIndex = index % colors.length; + const label = item.target; + + return { + data: item.datapoints, + label: label, + color: colors[colorIndex], + }; + }); + + return vmSeries; +} From ae7a1bc1392c65a641b0820a1248b1b0f49d846c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 15 Oct 2018 21:52:24 +0200 Subject: [PATCH 50/54] Can render graph --- .../features/dashboard/dashgrid/DataPanel.tsx | 6 +- public/app/plugins/panel/graph/graph.ts | 1 + public/app/plugins/panel/graph2/module.tsx | 13 +- public/app/types/index.ts | 4 + public/app/types/series.ts | 28 ++++ public/app/viz/Graph.tsx | 1 + public/app/viz/state/timeSeries.ts | 153 +++++++++++++++++- 7 files changed, 194 insertions(+), 12 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/DataPanel.tsx b/public/app/features/dashboard/dashgrid/DataPanel.tsx index 8014e06f43f..85821c19742 100644 --- a/public/app/features/dashboard/dashgrid/DataPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DataPanel.tsx @@ -5,7 +5,7 @@ import React, { Component } from 'react'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; // Types -import { TimeRange, LoadingState, DataQueryResponse, TimeSeries } from 'app/types'; +import { TimeRange, LoadingState, DataQueryOptions, DataQueryResponse, TimeSeries } from 'app/types'; interface RenderProps { loading: LoadingState; @@ -82,14 +82,14 @@ export class DataPanel extends Component { const dataSourceSrv = getDatasourceSrv(); const ds = await dataSourceSrv.get(datasource); - const queryOptions = { + const queryOptions: DataQueryOptions = { timezone: 'browser', panelId: panelId, dashboardId: dashboardId, range: timeRange, rangeRaw: timeRange.raw, interval: '1s', - intervalMs: 1000, + intervalMs: 60000, targets: queries, maxDataPoints: 500, scopedVars: {}, diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 7a8e24539f7..bee9b9ec9b0 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -339,6 +339,7 @@ class GraphElement { callPlot(options, incrementRenderCounter) { try { + console.log('plot', this.sortedSeries); this.plot = $.plot(this.elem, this.sortedSeries, options); if (this.ctrl.renderError) { delete this.ctrl.error; diff --git a/public/app/plugins/panel/graph2/module.tsx b/public/app/plugins/panel/graph2/module.tsx index e8d594e7968..c2b8c355440 100644 --- a/public/app/plugins/panel/graph2/module.tsx +++ b/public/app/plugins/panel/graph2/module.tsx @@ -4,12 +4,10 @@ import React, { PureComponent } from 'react'; // Components import Graph from 'app/viz/Graph'; - -// Utils import { getTimeSeriesVMs } from 'app/viz/state/timeSeries'; // Types -import { PanelProps } from 'app/types'; +import { PanelProps, NullValueMode } from 'app/types'; interface Options { showBars: boolean; @@ -26,10 +24,13 @@ export class Graph2 extends PureComponent { render() { const { timeSeries, timeRange } = this.props; - const viewModels = getTimeSeriesVMs({ timeSeries }); - console.log(viewModels); - return ; + const vmSeries = getTimeSeriesVMs({ + timeSeries: timeSeries, + nullValueMode: NullValueMode.Ignore, + }); + + return ; } } diff --git a/public/app/types/index.ts b/public/app/types/index.ts index d274c99253c..b45b5f3e320 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -15,6 +15,8 @@ import { TimeSeries, TimeSeriesVM, TimeSeriesVMs, + TimeSeriesStats, + NullValueMode, DataQuery, DataQueryResponse, DataQueryOptions, @@ -62,6 +64,8 @@ export { TimeSeries, TimeSeriesVM, TimeSeriesVMs, + NullValueMode, + TimeSeriesStats, DataQuery, DataQueryResponse, DataQueryOptions, diff --git a/public/app/types/series.ts b/public/app/types/series.ts index 9a906377636..5396880611b 100644 --- a/public/app/types/series.ts +++ b/public/app/types/series.ts @@ -33,6 +33,30 @@ export interface TimeSeriesVM { label: string; color: string; data: TimeSeriesValue[][]; + stats: TimeSeriesStats; +} + +export interface TimeSeriesStats { + total: number; + max: number; + min: number; + logmin: number; + avg: number | null; + current: number | null; + first: number | null; + delta: number; + diff: number | null; + range: number | null; + timeStep: number; + count: number; + allIsNull: boolean; + allIsZero: boolean; +} + +export enum NullValueMode { + Null = 'null', + Ignore = 'connected', + AsZero = 'null as zero', } /** View model projection of many time series */ @@ -56,6 +80,10 @@ export interface DataQueryOptions { panelId: number; dashboardId: number; cacheTimeout?: string; + interval: string; + intervalMs: number; + maxDataPoints: number; + scopedVars: object; } export interface DataSourceApi { diff --git a/public/app/viz/Graph.tsx b/public/app/viz/Graph.tsx index 38d29af8da9..fab65225715 100644 --- a/public/app/viz/Graph.tsx +++ b/public/app/viz/Graph.tsx @@ -108,6 +108,7 @@ export class Graph extends PureComponent { ...dynamicOptions, }; + console.log('plot', timeSeries, options); $.plot(this.element, timeSeries, options); } diff --git a/public/app/viz/state/timeSeries.ts b/public/app/viz/state/timeSeries.ts index 7658a2b2120..e22cb4681b7 100644 --- a/public/app/viz/state/timeSeries.ts +++ b/public/app/viz/state/timeSeries.ts @@ -1,19 +1,166 @@ +// Libraries +import _ from 'lodash'; + +// Utils import colors from 'app/core/utils/colors'; -import { TimeSeries, TimeSeriesVMs } from 'app/types'; + +// Types +import { TimeSeries, TimeSeriesVMs, NullValueMode } from 'app/types'; interface Options { timeSeries: TimeSeries[]; + nullValueMode: NullValueMode; } -export function getTimeSeriesVMs({ timeSeries }: Options): TimeSeriesVMs { +export function getTimeSeriesVMs({ timeSeries, nullValueMode }: Options): TimeSeriesVMs { const vmSeries = timeSeries.map((item, index) => { const colorIndex = index % colors.length; const label = item.target; + const result = []; + + // stat defaults + let total = 0; + let max = -Number.MAX_VALUE; + let min = Number.MAX_VALUE; + let logmin = Number.MAX_VALUE; + let avg = null; + let current = null; + let first = null; + let delta = 0; + let diff = null; + let range = null; + let timeStep = Number.MAX_VALUE; + let allIsNull = true; + let allIsZero = true; + + const ignoreNulls = nullValueMode === NullValueMode.Ignore; + const nullAsZero = nullValueMode === NullValueMode.AsZero; + + let currentTime; + let currentValue; + let nonNulls = 0; + let previousTime; + let previousValue = 0; + let previousDeltaUp = true; + + for (let i = 0; i < item.datapoints.length; i++) { + currentValue = item.datapoints[i][0]; + currentTime = item.datapoints[i][1]; + + // Due to missing values we could have different timeStep all along the series + // so we have to find the minimum one (could occur with aggregators such as ZimSum) + if (previousTime !== undefined) { + const currentStep = currentTime - previousTime; + if (currentStep < timeStep) { + timeStep = currentStep; + } + } + + previousTime = currentTime; + + if (currentValue === null) { + if (ignoreNulls) { + continue; + } + if (nullAsZero) { + currentValue = 0; + } + } + + if (currentValue !== null) { + if (_.isNumber(currentValue)) { + total += currentValue; + allIsNull = false; + nonNulls++; + } + + if (currentValue > max) { + max = currentValue; + } + + if (currentValue < min) { + min = currentValue; + } + + if (first === null) { + first = currentValue; + } else { + if (previousValue > currentValue) { + // counter reset + previousDeltaUp = false; + if (i === item.datapoints.length - 1) { + // reset on last + delta += currentValue; + } + } else { + if (previousDeltaUp) { + delta += currentValue - previousValue; // normal increment + } else { + delta += currentValue; // account for counter reset + } + previousDeltaUp = true; + } + } + previousValue = currentValue; + + if (currentValue < logmin && currentValue > 0) { + logmin = currentValue; + } + + if (currentValue !== 0) { + allIsZero = false; + } + } + + result.push([currentTime, currentValue]); + } + + if (max === -Number.MAX_VALUE) { + max = null; + } + + if (min === Number.MAX_VALUE) { + min = null; + } + + if (result.length && !allIsNull) { + avg = total / nonNulls; + current = result[result.length - 1][1]; + if (current === null && result.length > 1) { + current = result[result.length - 2][1]; + } + } + + if (max !== null && min !== null) { + range = max - min; + } + + if (current !== null && first !== null) { + diff = current - first; + } + + const count = result.length; return { - data: item.datapoints, + data: result, label: label, color: colors[colorIndex], + stats: { + total, + min, + max, + current, + logmin, + avg, + diff, + delta, + timeStep, + range, + count, + first, + allIsZero, + allIsNull, + }, }; }); From 11f66bb909ac5ee45a7b77581abc66264824a40d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 19 Oct 2018 09:43:54 +0200 Subject: [PATCH 51/54] fixed issue with template refresh --- public/app/features/dashboard/dashboard_ctrl.ts | 7 +++++++ public/app/features/dashboard/dashboard_model.ts | 4 ++++ .../features/dashboard/dashgrid/DashboardGrid.tsx | 1 - public/app/features/dashboard/time_srv.ts | 4 ++-- public/app/features/panel/metrics_panel_ctrl.ts | 1 - public/app/features/panel/panel_directive.ts | 1 - public/app/features/plugins/plugin_component.ts | 1 - public/app/features/templating/variable_srv.ts | 15 ++++++--------- .../app/plugins/datasource/testdata/datasource.ts | 1 - public/app/plugins/panel/graph/graph.ts | 1 - 10 files changed, 19 insertions(+), 17 deletions(-) diff --git a/public/app/features/dashboard/dashboard_ctrl.ts b/public/app/features/dashboard/dashboard_ctrl.ts index 696a40ef71a..65421a01392 100644 --- a/public/app/features/dashboard/dashboard_ctrl.ts +++ b/public/app/features/dashboard/dashboard_ctrl.ts @@ -162,10 +162,17 @@ export class DashboardCtrl { this.dashboard.removePanel(panel); } + onDestroy() { + if (this.dashboard) { + this.dashboard.destroy(); + } + } + init(dashboard) { this.$scope.onAppEvent('show-json-editor', this.showJsonEditor.bind(this)); this.$scope.onAppEvent('template-variable-value-updated', this.templateVariableUpdated.bind(this)); this.$scope.onAppEvent('panel-remove', this.onRemovingPanel.bind(this)); + this.$scope.$on('$destroy', this.onDestroy.bind(this)); this.setupDashboard(dashboard); } } diff --git a/public/app/features/dashboard/dashboard_model.ts b/public/app/features/dashboard/dashboard_model.ts index aba5be24e8b..65a234a2b94 100644 --- a/public/app/features/dashboard/dashboard_model.ts +++ b/public/app/features/dashboard/dashboard_model.ts @@ -200,6 +200,10 @@ export class DashboardModel { this.events.emit('view-mode-changed', panel); } + timeRangeUpdated() { + this.events.emit('time-range-updated'); + } + startRefresh() { this.events.emit('refresh'); diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx index 4da929b981e..1f5fa4cbe12 100644 --- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx @@ -172,7 +172,6 @@ export class DashboardGrid extends React.Component { renderPanels() { const panelElements = []; - console.log('render panels'); for (const panel of this.props.dashboard.panels) { const panelClasses = classNames({ panel: true, 'panel--fullscreen': panel.fullscreen }); diff --git a/public/app/features/dashboard/time_srv.ts b/public/app/features/dashboard/time_srv.ts index 06cadfa6530..03b4a408125 100644 --- a/public/app/features/dashboard/time_srv.ts +++ b/public/app/features/dashboard/time_srv.ts @@ -6,8 +6,8 @@ import _ from 'lodash'; import kbn from 'app/core/utils/kbn'; import coreModule from 'app/core/core_module'; import * as dateMath from 'app/core/utils/datemath'; - // Types + import { TimeRange } from 'app/types'; export class TimeSrv { @@ -147,7 +147,7 @@ export class TimeSrv { } refreshDashboard() { - this.dashboard.startRefresh(); + this.dashboard.timeRangeUpdated(); } private startNextRefreshTimer(afterMs) { diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts index aa25308e121..e517c48bb59 100644 --- a/public/app/features/panel/metrics_panel_ctrl.ts +++ b/public/app/features/panel/metrics_panel_ctrl.ts @@ -61,7 +61,6 @@ class MetricsPanelCtrl extends PanelCtrl { } private onMetricsPanelRefresh() { - console.log('metrics_panel_ctrl:onRefresh'); // ignore fetching data if another panel is in fullscreen if (this.otherPanelInFullscreenMode()) { return; diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index d4554a2eed9..77ebf754b3a 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/public/app/features/panel/panel_directive.ts @@ -135,7 +135,6 @@ module.directive('grafanaPanel', ($rootScope, $document, $timeout) => { ctrl.events.on('panel-size-changed', () => { ctrl.calculatePanelHeight(); $timeout(() => { - console.log('panel directive panel size changed, render'); resizeScrollableContent(); ctrl.render(); }); diff --git a/public/app/features/plugins/plugin_component.ts b/public/app/features/plugins/plugin_component.ts index 4b421ca446a..142eb942a30 100644 --- a/public/app/features/plugins/plugin_component.ts +++ b/public/app/features/plugins/plugin_component.ts @@ -211,7 +211,6 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $ elem.append(child); setTimeout(() => { scope.$applyAsync(() => { - console.log('post appendAndCompile, broadcast refresh', scope.panel); scope.$broadcast('component-did-mount'); }); }); diff --git a/public/app/features/templating/variable_srv.ts b/public/app/features/templating/variable_srv.ts index 89ff8dcfb9f..6139dbf6dad 100644 --- a/public/app/features/templating/variable_srv.ts +++ b/public/app/features/templating/variable_srv.ts @@ -13,13 +13,12 @@ export class VariableSrv { /** @ngInject */ constructor(private $rootScope, private $q, private $location, private $injector, private templateSrv) { - // update time variant variables - $rootScope.$on('refresh', this.onDashboardRefresh.bind(this), $rootScope); $rootScope.$on('template-variable-value-updated', this.updateUrlParamsWithCurrentVariables.bind(this), $rootScope); } init(dashboard) { this.dashboard = dashboard; + this.dashboard.on('time-range-updated', this.onTimeRangeUpdated.bind(this)); // create working class models representing variables this.variables = dashboard.templating.list = dashboard.templating.list.map(this.createVariableFromModel.bind(this)); @@ -42,11 +41,7 @@ export class VariableSrv { }); } - onDashboardRefresh(evt, payload) { - if (payload && payload.fromVariableValueUpdated) { - return Promise.resolve({}); - } - + onTimeRangeUpdated() { const promises = this.variables.filter(variable => variable.refresh === 2).map(variable => { const previousOptions = variable.options.slice(); @@ -57,7 +52,9 @@ export class VariableSrv { }); }); - return this.$q.all(promises); + return this.$q.all(promises).then(() => { + this.dashboard.startRefresh(); + }); } processVariable(variable, queryParams) { @@ -136,7 +133,7 @@ export class VariableSrv { return this.$q.all(promises).then(() => { if (emitChangeEvents) { this.$rootScope.$emit('template-variable-value-updated'); - this.$rootScope.$broadcast('refresh', { fromVariableValueUpdated: true }); + this.dashboard.startRefresh(); } }); } diff --git a/public/app/plugins/datasource/testdata/datasource.ts b/public/app/plugins/datasource/testdata/datasource.ts index d112e656f3f..0197626cd0b 100644 --- a/public/app/plugins/datasource/testdata/datasource.ts +++ b/public/app/plugins/datasource/testdata/datasource.ts @@ -62,7 +62,6 @@ class TestDataDatasource { }); } - console.log(res); return { data: data }; }); } diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index bee9b9ec9b0..7a8e24539f7 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -339,7 +339,6 @@ class GraphElement { callPlot(options, incrementRenderCounter) { try { - console.log('plot', this.sortedSeries); this.plot = $.plot(this.elem, this.sortedSeries, options); if (this.ctrl.renderError) { delete this.ctrl.error; From 936fe560801d9df64d9b9de698e33295cefabc4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 19 Oct 2018 10:05:48 +0200 Subject: [PATCH 52/54] fix: another set of fixes for refresh --- public/app/core/services/keybindingSrv.ts | 4 ++-- public/app/features/dashboard/dashboard_ctrl.ts | 4 ---- public/app/features/dashboard/settings/settings.ts | 2 +- public/app/features/dashboard/share_snapshot_ctrl.ts | 3 +-- public/app/features/dashboard/timepicker/settings.html | 2 +- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/public/app/core/services/keybindingSrv.ts b/public/app/core/services/keybindingSrv.ts index d8dfc958dd4..f43dc96cd37 100644 --- a/public/app/core/services/keybindingSrv.ts +++ b/public/app/core/services/keybindingSrv.ts @@ -148,7 +148,7 @@ export class KeybindingSrv { this.bind('mod+o', () => { dashboard.graphTooltip = (dashboard.graphTooltip + 1) % 3; appEvents.emit('graph-hover-clear'); - this.$rootScope.$broadcast('refresh'); + dashboard.startRefresh(); }); this.bind('mod+s', e => { @@ -257,7 +257,7 @@ export class KeybindingSrv { }); this.bind('d r', () => { - this.$rootScope.$broadcast('refresh'); + dashboard.startRefresh(); }); this.bind('d s', () => { diff --git a/public/app/features/dashboard/dashboard_ctrl.ts b/public/app/features/dashboard/dashboard_ctrl.ts index 65421a01392..c34b9ddaff2 100644 --- a/public/app/features/dashboard/dashboard_ctrl.ts +++ b/public/app/features/dashboard/dashboard_ctrl.ts @@ -117,10 +117,6 @@ export class DashboardCtrl { return this.dashboard; } - timezoneChanged() { - this.$rootScope.$broadcast('refresh'); - } - getPanelContainer() { return this; } diff --git a/public/app/features/dashboard/settings/settings.ts b/public/app/features/dashboard/settings/settings.ts index 048a51efead..b6a70ee4b98 100755 --- a/public/app/features/dashboard/settings/settings.ts +++ b/public/app/features/dashboard/settings/settings.ts @@ -32,7 +32,7 @@ export class SettingsCtrl { this.$scope.$on('$destroy', () => { this.dashboard.updateSubmenuVisibility(); - this.$rootScope.$broadcast('refresh'); + this.dashboard.startRefresh(); setTimeout(() => { this.$rootScope.appEvent('dash-scroll', { restore: true }); }); diff --git a/public/app/features/dashboard/share_snapshot_ctrl.ts b/public/app/features/dashboard/share_snapshot_ctrl.ts index ec487801948..ac09d63054d 100644 --- a/public/app/features/dashboard/share_snapshot_ctrl.ts +++ b/public/app/features/dashboard/share_snapshot_ctrl.ts @@ -46,8 +46,7 @@ export class ShareSnapshotCtrl { $scope.loading = true; $scope.snapshot.external = external; - - $rootScope.$broadcast('refresh'); + $scope.dashboard.startRefresh(); $timeout(() => { $scope.saveSnapshot(external); diff --git a/public/app/features/dashboard/timepicker/settings.html b/public/app/features/dashboard/timepicker/settings.html index 3cb8ca061fb..fd5170013c2 100644 --- a/public/app/features/dashboard/timepicker/settings.html +++ b/public/app/features/dashboard/timepicker/settings.html @@ -5,7 +5,7 @@
      - +
      From b95d64e7a0688e1cc82db7958afdb7bf9b17a41c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 19 Oct 2018 11:14:02 +0200 Subject: [PATCH 53/54] fix: fixed variable srv tests --- .../features/templating/specs/variable_srv.test.ts | 11 +++++++---- .../templating/specs/variable_srv_init.test.ts | 5 +++-- public/app/features/templating/variable_srv.ts | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/public/app/features/templating/specs/variable_srv.test.ts b/public/app/features/templating/specs/variable_srv.test.ts index 359d5b79a38..3df6ccb8b5b 100644 --- a/public/app/features/templating/specs/variable_srv.test.ts +++ b/public/app/features/templating/specs/variable_srv.test.ts @@ -1,5 +1,6 @@ import '../all'; import { VariableSrv } from '../variable_srv'; +import { DashboardModel } from '../../dashboard/dashboard_model'; import moment from 'moment'; import $q from 'q'; @@ -56,10 +57,12 @@ describe('VariableSrv', function(this: any) { return getVarMockConstructor(ctr, model, ctx); }; - ctx.variableSrv.init({ - templating: { list: [] }, - updateSubmenuVisibility: () => {}, - }); + ctx.variableSrv.init( + new DashboardModel({ + templating: { list: [] }, + updateSubmenuVisibility: () => {}, + }) + ); scenario.variable = ctx.variableSrv.createVariableFromModel(scenario.variableModel); ctx.variableSrv.addVariable(scenario.variable); diff --git a/public/app/features/templating/specs/variable_srv_init.test.ts b/public/app/features/templating/specs/variable_srv_init.test.ts index b5d00a5289e..bda5b6aa577 100644 --- a/public/app/features/templating/specs/variable_srv_init.test.ts +++ b/public/app/features/templating/specs/variable_srv_init.test.ts @@ -2,6 +2,7 @@ import '../all'; import _ from 'lodash'; import { VariableSrv } from '../variable_srv'; +import { DashboardModel } from '../../dashboard/dashboard_model'; import $q from 'q'; describe('VariableSrv init', function(this: any) { @@ -56,9 +57,9 @@ describe('VariableSrv init', function(this: any) { ctx.variableSrv.datasourceSrv = ctx.datasourceSrv; ctx.variableSrv.$location.search = () => scenario.urlParams; - ctx.variableSrv.dashboard = { + ctx.variableSrv.dashboard = new DashboardModel({ templating: { list: scenario.variables }, - }; + }); await ctx.variableSrv.init(ctx.variableSrv.dashboard); diff --git a/public/app/features/templating/variable_srv.ts b/public/app/features/templating/variable_srv.ts index 6139dbf6dad..a676f9c2848 100644 --- a/public/app/features/templating/variable_srv.ts +++ b/public/app/features/templating/variable_srv.ts @@ -18,7 +18,7 @@ export class VariableSrv { init(dashboard) { this.dashboard = dashboard; - this.dashboard.on('time-range-updated', this.onTimeRangeUpdated.bind(this)); + this.dashboard.events.on('time-range-updated', this.onTimeRangeUpdated.bind(this)); // create working class models representing variables this.variables = dashboard.templating.list = dashboard.templating.list.map(this.createVariableFromModel.bind(this)); From 5b8178cdaed5a4a78b5037d2e762e0279d1e6080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 19 Oct 2018 11:32:36 +0200 Subject: [PATCH 54/54] fixed gofmt issue after go update --- pkg/services/sqlstore/alert_notification_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/services/sqlstore/alert_notification_test.go b/pkg/services/sqlstore/alert_notification_test.go index 92910fb41de..629a6292eb5 100644 --- a/pkg/services/sqlstore/alert_notification_test.go +++ b/pkg/services/sqlstore/alert_notification_test.go @@ -44,8 +44,8 @@ func TestAlertNotificationSQLAccess(t *testing.T) { s := *query.Result cmd := models.SetAlertNotificationStateToPendingCommand{ - Id: s.Id, - Version: s.Version, + Id: s.Id, + Version: s.Version, AlertRuleStateUpdatedVersion: s.AlertRuleStateUpdatedVersion, } @@ -100,8 +100,8 @@ func TestAlertNotificationSQLAccess(t *testing.T) { s := *query.Result s.Version = 1000 cmd := models.SetAlertNotificationStateToPendingCommand{ - Id: s.NotifierId, - Version: s.Version, + Id: s.NotifierId, + Version: s.Version, AlertRuleStateUpdatedVersion: s.AlertRuleStateUpdatedVersion, } err := SetAlertNotificationStateToPendingCommand(context.Background(), &cmd) @@ -111,8 +111,8 @@ func TestAlertNotificationSQLAccess(t *testing.T) { Convey("Updating existing state to pending with incorrect version since alert rule state update version is higher", func() { s := *query.Result cmd := models.SetAlertNotificationStateToPendingCommand{ - Id: s.Id, - Version: s.Version, + Id: s.Id, + Version: s.Version, AlertRuleStateUpdatedVersion: 1000, } err := SetAlertNotificationStateToPendingCommand(context.Background(), &cmd) @@ -125,8 +125,8 @@ func TestAlertNotificationSQLAccess(t *testing.T) { s := *query.Result s.Version = 1000 cmd := models.SetAlertNotificationStateToPendingCommand{ - Id: s.Id, - Version: s.Version, + Id: s.Id, + Version: s.Version, AlertRuleStateUpdatedVersion: s.AlertRuleStateUpdatedVersion, } err := SetAlertNotificationStateToPendingCommand(context.Background(), &cmd)