diff --git a/public/app/core/controllers/json_editor_ctrl.ts b/public/app/core/controllers/json_editor_ctrl.ts index 9c3f9d9e98d..7439433c55e 100644 --- a/public/app/core/controllers/json_editor_ctrl.ts +++ b/public/app/core/controllers/json_editor_ctrl.ts @@ -4,13 +4,13 @@ import coreModule from '../core_module'; export class JsonEditorCtrl { /** @ngInject */ constructor($scope) { - $scope.json = angular.toJson($scope.object, true); - $scope.canUpdate = $scope.updateHandler !== void 0 && $scope.contextSrv.isEditor; - $scope.canCopy = $scope.enableCopy; + $scope.json = angular.toJson($scope.model.object, true); + $scope.canUpdate = $scope.model.updateHandler !== void 0 && $scope.contextSrv.isEditor; + $scope.canCopy = $scope.model.enableCopy; $scope.update = () => { const newObject = angular.fromJson($scope.json); - $scope.updateHandler(newObject, $scope.object); + $scope.model.updateHandler(newObject, $scope.model.object); }; $scope.getContentForClipboard = () => $scope.json; diff --git a/public/app/core/reducers/location.ts b/public/app/core/reducers/location.ts index 7c7dffd04b9..c9fe478dd9d 100644 --- a/public/app/core/reducers/location.ts +++ b/public/app/core/reducers/location.ts @@ -23,7 +23,9 @@ export const locationReducer = (state = initialState, action: Action): LocationS return { url: renderUrl(path || state.path, query), path: path || state.path, - query: query, + query: { + ...query, + }, routeParams: routeParams || state.routeParams, }; } diff --git a/public/app/core/services/bridge_srv.ts b/public/app/core/services/bridge_srv.ts index ee184c243ac..1c91673495d 100644 --- a/public/app/core/services/bridge_srv.ts +++ b/public/app/core/services/bridge_srv.ts @@ -4,7 +4,7 @@ import { store } from 'app/store/configureStore'; import locationUtil from 'app/core/utils/location_util'; import { updateLocation } from 'app/core/actions'; -// Services that handles angular -> mobx store sync & other react <-> angular sync +// Services that handles angular -> redux store sync & other react <-> angular sync export class BridgeSrv { private fullPageReloadRoutes; diff --git a/public/app/features/dashboard/dashboard_ctrl.ts b/public/app/features/dashboard/dashboard_ctrl.ts index 5871a579f3c..6611a728803 100644 --- a/public/app/features/dashboard/dashboard_ctrl.ts +++ b/public/app/features/dashboard/dashboard_ctrl.ts @@ -2,13 +2,13 @@ import config from 'app/core/config'; import appEvents from 'app/core/app_events'; import coreModule from 'app/core/core_module'; +import { removePanel } from 'app/features/dashboard/utils/panel'; // Services import { AnnotationsSrv } from '../annotations/annotations_srv'; // Types import { DashboardModel } from './dashboard_model'; -import { PanelModel } from './panel_model'; export class DashboardCtrl { dashboard: DashboardModel; @@ -19,7 +19,6 @@ export class DashboardCtrl { /** @ngInject */ constructor( private $scope, - private $rootScope, private keybindingSrv, private timeSrv, private variableSrv, @@ -112,12 +111,14 @@ export class DashboardCtrl { } showJsonEditor(evt, options) { - const editScope = this.$rootScope.$new(); - editScope.object = options.object; - editScope.updateHandler = options.updateHandler; + const model = { + object: options.object, + updateHandler: options.updateHandler, + }; + this.$scope.appEvent('show-dash-editor', { src: 'public/app/partials/edit_json.html', - scope: editScope, + model: model, }); } @@ -136,34 +137,7 @@ export class DashboardCtrl { } const panelInfo = this.dashboard.getPanelInfoById(options.panelId); - this.removePanel(panelInfo.panel, true); - } - - removePanel(panel: PanelModel, ask: boolean) { - // confirm deletion - if (ask !== false) { - let text2, confirmText; - - if (panel.alert) { - text2 = 'Panel includes an alert rule, removing panel will also remove alert rule'; - confirmText = 'YES'; - } - - this.$scope.appEvent('confirm-modal', { - title: 'Remove Panel', - text: 'Are you sure you want to remove this panel?', - text2: text2, - icon: 'fa-trash', - confirmText: confirmText, - yesText: 'Remove', - onConfirm: () => { - this.removePanel(panel, false); - }, - }); - return; - } - - this.dashboard.removePanel(panel); + removePanel(this.dashboard, panelInfo.panel, true); } onDestroy() { diff --git a/public/app/features/dashboard/dashboard_model.ts b/public/app/features/dashboard/dashboard_model.ts index 65a234a2b94..3320783ec67 100644 --- a/public/app/features/dashboard/dashboard_model.ts +++ b/public/app/features/dashboard/dashboard_model.ts @@ -232,11 +232,6 @@ 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 fe55e64634f..12b4809f51a 100644 --- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx @@ -83,7 +83,6 @@ 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() { @@ -176,7 +175,12 @@ 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 7dd8a06996d..6853ade474b 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { PureComponent } from 'react'; import config from 'app/core/config'; import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; @@ -11,16 +11,17 @@ import { PanelChrome } from './PanelChrome'; import { PanelEditor } from './PanelEditor'; export interface Props { - panelType: string; panel: PanelModel; dashboard: DashboardModel; + isEditing: boolean; + isFullscreen: boolean; } export interface State { pluginExports: PluginExports; } -export class DashboardPanel extends React.Component { +export class DashboardPanel extends PureComponent { element: any; angularPanel: AngularComponent; pluginInfo: any; @@ -113,9 +114,8 @@ export class DashboardPanel extends React.Component { 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'; - + const containerClass = this.props.isEditing ? 'panel-editor-container' : 'panel-height-helper'; + const panelWrapperClass = this.props.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 return ( diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index 953dfd62368..f6fc9028726 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -5,7 +5,7 @@ import React, { ComponentClass, PureComponent } from 'react'; import { getTimeSrv } from '../time_srv'; // Components -import { PanelHeader } from './PanelHeader'; +import { PanelHeader } from './PanelHeader/PanelHeader'; import { DataPanel } from './DataPanel'; // Types @@ -49,17 +49,19 @@ export class PanelChrome extends PureComponent { const timeSrv = getTimeSrv(); const timeRange = timeSrv.timeRange(); - this.setState({ + this.setState(prevState => ({ + ...prevState, refreshCounter: this.state.refreshCounter + 1, timeRange: timeRange, - }); + })); }; onRender = () => { console.log('onRender'); - this.setState({ + this.setState(prevState => ({ + ...prevState, renderCounter: this.state.renderCounter + 1, - }); + })); }; get isVisible() { @@ -68,12 +70,12 @@ export class PanelChrome extends PureComponent { render() { const { panel, dashboard } = this.props; + const { refreshCounter, timeRange, renderCounter } = this.state; + const { datasource, targets } = panel; - const { timeRange, renderCounter, refreshCounter } = this.state; const PanelComponent = this.props.component; - console.log('Panel chrome render'); - + console.log('panelChrome render'); return (
diff --git a/public/app/features/dashboard/dashgrid/PanelHeader.tsx b/public/app/features/dashboard/dashgrid/PanelHeader.tsx deleted file mode 100644 index 12d5cd37253..00000000000 --- a/public/app/features/dashboard/dashgrid/PanelHeader.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import React from 'react'; -import classNames from 'classnames'; -import { PanelModel } from '../panel_model'; -import { DashboardModel } from '../dashboard_model'; -import { store } from 'app/store/configureStore'; -import { updateLocation } from 'app/core/actions'; - -interface PanelHeaderProps { - panel: PanelModel; - dashboard: DashboardModel; -} - -export class PanelHeader extends React.Component { - onEditPanel = () => { - store.dispatch( - updateLocation({ - query: { - panelId: this.props.panel.id, - edit: true, - fullscreen: true, - }, - }) - ); - }; - - onViewPanel = () => { - store.dispatch( - updateLocation({ - query: { - panelId: this.props.panel.id, - edit: false, - fullscreen: true, - }, - }) - ); - }; - - render() { - const isFullscreen = false; - const isLoading = false; - const panelHeaderClass = classNames({ 'panel-header': true, 'grid-drag-handle': !isFullscreen }); - - return ( -
- - - - - - {isLoading && ( - - - - )} - -
- - - {this.props.panel.title} - - - - - - 4m - - -
-
- ); - } -} diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx new file mode 100644 index 00000000000..ab3081ba21e --- /dev/null +++ b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx @@ -0,0 +1,51 @@ +import React, { PureComponent } from 'react'; +import classNames from 'classnames'; + +import { PanelHeaderMenu } from './PanelHeaderMenu'; + +import { DashboardModel } from 'app/features/dashboard/dashboard_model'; +import { PanelModel } from 'app/features/dashboard/panel_model'; + +export interface Props { + panel: PanelModel; + dashboard: DashboardModel; +} + +export class PanelHeader extends PureComponent { + render() { + const isFullscreen = false; + const isLoading = false; + const panelHeaderClass = classNames({ 'panel-header': true, 'grid-drag-handle': !isFullscreen }); + const { panel, dashboard } = this.props; + + return ( +
+ + + + + + {isLoading && ( + + + + )} + +
+
+ + + {panel.title} + + + + + + 4m + +
+
+
+ ); + } +} diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenu.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenu.tsx new file mode 100644 index 00000000000..8eb6b31bf27 --- /dev/null +++ b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenu.tsx @@ -0,0 +1,40 @@ +import React, { PureComponent } from 'react'; +import { DashboardModel } from 'app/features/dashboard/dashboard_model'; +import { PanelModel } from 'app/features/dashboard/panel_model'; +import { PanelHeaderMenuItem } from './PanelHeaderMenuItem'; +import { getPanelMenu } from 'app/features/dashboard/utils/getPanelMenu'; +import { PanelMenuItem } from 'app/types/panel'; + +export interface Props { + panel: PanelModel; + dashboard: DashboardModel; +} + +export class PanelHeaderMenu extends PureComponent { + renderItems = (menu: PanelMenuItem[], isSubMenu = false) => { + return ( +
    + {menu.map((menuItem, idx: number) => { + return ( + + {menuItem.subMenu && this.renderItems(menuItem.subMenu, true)} + + ); + })} +
+ ); + }; + + render() { + const { dashboard, panel } = this.props; + const menu = getPanelMenu(dashboard, panel); + return
{this.renderItems(menu)}
; + } +} diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuItem.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuItem.tsx new file mode 100644 index 00000000000..92a64a2f24d --- /dev/null +++ b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeaderMenuItem.tsx @@ -0,0 +1,23 @@ +import React, { SFC } from 'react'; +import { PanelMenuItem } from 'app/types/panel'; + +interface Props { + children: any; +} + +export const PanelHeaderMenuItem: SFC = props => { + const isSubMenu = props.type === 'submenu'; + const isDivider = props.type === 'divider'; + return isDivider ? ( +
  • + ) : ( +
  • + + {props.iconClassName && } + {props.text} + {props.shortcut && {props.shortcut}} + + {props.children} +
  • + ); +}; diff --git a/public/app/features/dashboard/export/export_modal.ts b/public/app/features/dashboard/export/export_modal.ts index 0e48041ca87..8136c77cd8f 100644 --- a/public/app/features/dashboard/export/export_modal.ts +++ b/public/app/features/dashboard/export/export_modal.ts @@ -48,14 +48,15 @@ export class DashExportCtrl { saveAs(blob, dash.title + '-' + new Date().getTime() + '.json'); } - private openJsonModal(clone: any) { - const editScope = this.$rootScope.$new(); - editScope.object = clone; - editScope.enableCopy = true; + private openJsonModal(clone: object) { + const model = { + object: clone, + enableCopy: true, + }; this.$rootScope.appEvent('show-modal', { src: 'public/app/partials/edit_json.html', - scope: editScope, + model: model, }); this.dismiss(); diff --git a/public/app/features/dashboard/shareModalCtrl.ts b/public/app/features/dashboard/shareModalCtrl.ts index c00a6d8d57f..b4ce25485b8 100644 --- a/public/app/features/dashboard/shareModalCtrl.ts +++ b/public/app/features/dashboard/shareModalCtrl.ts @@ -12,6 +12,8 @@ export function ShareModalCtrl($scope, $rootScope, $location, $timeout, timeSrv, $scope.editor = { index: $scope.tabIndex || 0 }; $scope.init = () => { + $scope.panel = $scope.model && $scope.model.panel ? $scope.model.panel : $scope.panel; // React pass panel and dashboard in the "model" property + $scope.dashboard = $scope.model && $scope.model.dashboard ? $scope.model.dashboard : $scope.dashboard; // ^ $scope.modeSharePanel = $scope.panel ? true : false; $scope.tabs = [{ title: 'Link', src: 'shareLink.html' }]; diff --git a/public/app/features/dashboard/utils/getPanelMenu.ts b/public/app/features/dashboard/utils/getPanelMenu.ts new file mode 100644 index 00000000000..709eef3be38 --- /dev/null +++ b/public/app/features/dashboard/utils/getPanelMenu.ts @@ -0,0 +1,120 @@ +import { updateLocation } from 'app/core/actions'; +import { store } from 'app/store/configureStore'; + +import { removePanel, duplicatePanel, copyPanel, editPanelJson, sharePanel } from 'app/features/dashboard/utils/panel'; +import { PanelModel } from 'app/features/dashboard/panel_model'; +import { DashboardModel } from 'app/features/dashboard/dashboard_model'; +import { PanelMenuItem } from 'app/types/panel'; + +export const getPanelMenu = (dashboard: DashboardModel, panel: PanelModel) => { + const onViewPanel = () => { + store.dispatch( + updateLocation({ + query: { + panelId: panel.id, + edit: false, + fullscreen: true, + }, + partial: true, + }) + ); + }; + + const onEditPanel = () => { + store.dispatch( + updateLocation({ + query: { + panelId: panel.id, + edit: true, + fullscreen: true, + }, + partial: true, + }) + ); + }; + + const onSharePanel = () => { + sharePanel(dashboard, panel); + }; + + const onDuplicatePanel = () => { + duplicatePanel(dashboard, panel); + }; + + const onCopyPanel = () => { + copyPanel(panel); + }; + + const onEditPanelJson = () => { + editPanelJson(dashboard, panel); + }; + + const onRemovePanel = () => { + removePanel(dashboard, panel, true); + }; + + const menu: PanelMenuItem[] = []; + + menu.push({ + text: 'View', + iconClassName: 'fa fa-fw fa-eye', + onClick: onViewPanel, + shortcut: 'v', + }); + + if (dashboard.meta.canEdit) { + menu.push({ + text: 'Edit', + iconClassName: 'fa fa-fw fa-edit', + onClick: onEditPanel, + shortcut: 'e', + }); + } + + menu.push({ + text: 'Share', + iconClassName: 'fa fa-fw fa-share', + onClick: onSharePanel, + shortcut: 'p s', + }); + + const subMenu: PanelMenuItem[] = []; + + if (!panel.fullscreen && dashboard.meta.canEdit) { + subMenu.push({ + text: 'Duplicate', + onClick: onDuplicatePanel, + shortcut: 'p d', + }); + + subMenu.push({ + text: 'Copy', + onClick: onCopyPanel, + }); + } + + subMenu.push({ + text: 'Panel JSON', + onClick: onEditPanelJson, + }); + + menu.push({ + type: 'submenu', + text: 'More...', + iconClassName: 'fa fa-fw fa-cube', + subMenu: subMenu, + }); + + if (dashboard.meta.canEdit) { + menu.push({ type: 'divider' }); + + menu.push({ + text: 'Remove', + iconClassName: 'fa fa-fw fa-trash', + onClick: onRemovePanel, + shortcut: 'p r', + }); + } + + return menu; +}; diff --git a/public/app/features/dashboard/utils/panel.ts b/public/app/features/dashboard/utils/panel.ts new file mode 100644 index 00000000000..151c1ea8d61 --- /dev/null +++ b/public/app/features/dashboard/utils/panel.ts @@ -0,0 +1,86 @@ +import appEvents from 'app/core/app_events'; +import { DashboardModel } from 'app/features/dashboard/dashboard_model'; +import { PanelModel } from 'app/features/dashboard/panel_model'; +import store from 'app/core/store'; +import { LS_PANEL_COPY_KEY } from 'app/core/constants'; + +export const removePanel = (dashboard: DashboardModel, panel: PanelModel, ask: boolean) => { + // confirm deletion + if (ask !== false) { + const text2 = panel.alert ? 'Panel includes an alert rule, removing panel will also remove alert rule' : null; + const confirmText = panel.alert ? 'YES' : null; + + appEvents.emit('confirm-modal', { + title: 'Remove Panel', + text: 'Are you sure you want to remove this panel?', + text2: text2, + icon: 'fa-trash', + confirmText: confirmText, + yesText: 'Remove', + onConfirm: () => removePanel(dashboard, panel, false), + }); + return; + } + dashboard.removePanel(panel); +}; + +export const duplicatePanel = (dashboard: DashboardModel, panel: PanelModel) => { + dashboard.duplicatePanel(panel); +}; + +export const copyPanel = (panel: PanelModel) => { + store.set(LS_PANEL_COPY_KEY, JSON.stringify(panel.getSaveModel())); + appEvents.emit('alert-success', ['Panel copied. Open Add Panel to paste']); +}; + +const replacePanel = (dashboard: DashboardModel, newPanel: PanelModel, oldPanel: PanelModel) => { + const index = dashboard.panels.findIndex(panel => { + return panel.id === oldPanel.id; + }); + + const deletedPanel = dashboard.panels.splice(index, 1); + dashboard.events.emit('panel-removed', deletedPanel); + + newPanel = new PanelModel(newPanel); + newPanel.id = oldPanel.id; + + dashboard.panels.splice(index, 0, newPanel); + dashboard.sortPanelsByGridPos(); + dashboard.events.emit('panel-added', newPanel); +}; + +export const editPanelJson = (dashboard: DashboardModel, panel: PanelModel) => { + const model = { + object: panel.getSaveModel(), + updateHandler: (newPanel: PanelModel, oldPanel: PanelModel) => { + replacePanel(dashboard, newPanel, oldPanel); + }, + enableCopy: true, + }; + + appEvents.emit('show-modal', { + src: 'public/app/partials/edit_json.html', + model: model, + }); +}; + +export const sharePanel = (dashboard: DashboardModel, panel: PanelModel) => { + appEvents.emit('show-modal', { + src: 'public/app/features/dashboard/partials/shareModal.html', + model: { + dashboard: dashboard, + panel: panel, + }, + }); +}; + +export const refreshPanel = (panel: PanelModel) => { + panel.refresh(); +}; + +export const toggleLegend = (panel: PanelModel) => { + console.log('Toggle legend is not implemented yet'); + // We need to set panel.legend defaults first + // panel.legend.show = !panel.legend.show; + refreshPanel(panel); +}; diff --git a/public/app/features/panel/panel_ctrl.ts b/public/app/features/panel/panel_ctrl.ts index 08605132e82..92932142690 100644 --- a/public/app/features/panel/panel_ctrl.ts +++ b/public/app/features/panel/panel_ctrl.ts @@ -1,11 +1,15 @@ import config from 'app/core/config'; import _ from 'lodash'; import $ from 'jquery'; -import { appEvents, profiler } from 'app/core/core'; -import { PanelModel } from 'app/features/dashboard/panel_model'; +import { profiler } from 'app/core/core'; +import { + duplicatePanel, + copyPanel as copyPanelUtil, + editPanelJson as editPanelJsonUtil, + sharePanel as sharePanelUtil, +} from 'app/features/dashboard/utils/panel'; 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 { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN } from 'app/core/constants'; const TITLE_HEIGHT = 27; const PANEL_BORDER = 2; @@ -241,7 +245,7 @@ export class PanelCtrl { } duplicate() { - this.dashboard.duplicatePanel(this.panel); + duplicatePanel(this.dashboard, this.panel); } removePanel() { @@ -251,48 +255,15 @@ export class PanelCtrl { } editPanelJson() { - const editScope = this.$scope.$root.$new(); - editScope.object = this.panel.getSaveModel(); - editScope.updateHandler = this.replacePanel.bind(this); - editScope.enableCopy = true; - - this.publishAppEvent('show-modal', { - src: 'public/app/partials/edit_json.html', - scope: editScope, - }); + editPanelJsonUtil(this.dashboard, this.panel); } copyPanel() { - store.set(LS_PANEL_COPY_KEY, JSON.stringify(this.panel.getSaveModel())); - appEvents.emit('alert-success', ['Panel copied. Open Add Panel to paste']); - } - - replacePanel(newPanel, oldPanel) { - const dashboard = this.dashboard; - const index = _.findIndex(dashboard.panels, panel => { - return panel.id === oldPanel.id; - }); - - const deletedPanel = dashboard.panels.splice(index, 1); - this.dashboard.events.emit('panel-removed', deletedPanel); - - newPanel = new PanelModel(newPanel); - newPanel.id = oldPanel.id; - - dashboard.panels.splice(index, 0, newPanel); - dashboard.sortPanelsByGridPos(); - dashboard.events.emit('panel-added', newPanel); + copyPanelUtil(this.panel); } sharePanel() { - const shareScope = this.$scope.$new(); - shareScope.panel = this.panel; - shareScope.dashboard = this.dashboard; - - this.publishAppEvent('show-modal', { - src: 'public/app/features/dashboard/partials/shareModal.html', - scope: shareScope, - }); + sharePanelUtil(this.dashboard, this.panel); } getInfoMode() { diff --git a/public/app/features/panel/viz_tab.ts b/public/app/features/panel/viz_tab.ts index dfaf7395b97..b80b84a341a 100644 --- a/public/app/features/panel/viz_tab.ts +++ b/public/app/features/panel/viz_tab.ts @@ -16,9 +16,7 @@ export class VizTabCtrl { $scope.ctrl = this; } - onTypeChanged = (plugin: PanelPlugin) => { - this.dashboard.changePanelType(this.panelCtrl.panel, plugin.id); - }; + onTypeChanged = (plugin: PanelPlugin) => {}; } const template = ` diff --git a/public/app/types/panel.ts b/public/app/types/panel.ts index 7febd0cad26..e82c3711b06 100644 --- a/public/app/types/panel.ts +++ b/public/app/types/panel.ts @@ -12,3 +12,12 @@ export interface PanelOptionsProps { options: T; onChange: (options: T) => void; } + +export interface PanelMenuItem { + type?: 'submenu' | 'divider'; + text?: string; + iconClassName?: string; + onClick?: () => void; + shortcut?: string; + subMenu?: PanelMenuItem[]; +} diff --git a/public/sass/components/_dropdown.scss b/public/sass/components/_dropdown.scss index 37dbdcd89ef..9e7f46fe514 100644 --- a/public/sass/components/_dropdown.scss +++ b/public/sass/components/_dropdown.scss @@ -183,6 +183,11 @@ display: block; } + & > .dropdown > .dropdown-menu { + // Panel menu. TODO: See if we can merge this with above + display: block; + } + &.cascade-open { .dropdown-menu { display: block; diff --git a/public/sass/pages/_dashboard.scss b/public/sass/pages/_dashboard.scss index 795766a22de..125edac500f 100644 --- a/public/sass/pages/_dashboard.scss +++ b/public/sass/pages/_dashboard.scss @@ -138,7 +138,6 @@ div.flot-text { padding: 3px 5px; visibility: hidden; opacity: 0; - position: absolute; width: 16px; height: 16px; left: 1px;