diff --git a/packages/grafana-data/src/types/appEvent.ts b/packages/grafana-data/src/types/appEvent.ts new file mode 100644 index 00000000000..7edf2fdbb6c --- /dev/null +++ b/packages/grafana-data/src/types/appEvent.ts @@ -0,0 +1,4 @@ +export interface AppEvent { + readonly name: string; + payload?: T; +} diff --git a/packages/grafana-data/src/types/events.ts b/packages/grafana-data/src/types/events.ts new file mode 100644 index 00000000000..ee029c29999 --- /dev/null +++ b/packages/grafana-data/src/types/events.ts @@ -0,0 +1,7 @@ +import { eventFactory } from './utils'; + +export type AlertPayload = [string, string?]; + +export const alertSuccess = eventFactory('alert-success'); +export const alertWarning = eventFactory('alert-warning'); +export const alertError = eventFactory('alert-error'); diff --git a/packages/grafana-data/src/types/index.ts b/packages/grafana-data/src/types/index.ts index 24ffa20123d..11cbebe9ab4 100644 --- a/packages/grafana-data/src/types/index.ts +++ b/packages/grafana-data/src/types/index.ts @@ -13,3 +13,7 @@ export * from './graph'; export * from './ScopedVars'; export * from './transformations'; export * from './vector'; +export * from './appEvent'; + +import * as AppEvents from './events'; +export { AppEvents }; diff --git a/packages/grafana-data/src/types/utils.ts b/packages/grafana-data/src/types/utils.ts index 06c797ae5fa..a70e0d2101b 100644 --- a/packages/grafana-data/src/types/utils.ts +++ b/packages/grafana-data/src/types/utils.ts @@ -1,2 +1,14 @@ +import { AppEvent } from './appEvent'; + export type Omit = Pick>; export type Subtract = Omit; + +const typeList: Set = new Set(); +export function eventFactory(name: string): AppEvent { + if (typeList.has(name)) { + throw new Error(`There is already an event defined with type '${name}'`); + } + + typeList.add(name); + return { name }; +} diff --git a/packages/grafana-ui/src/types/events.ts b/packages/grafana-ui/src/types/events.ts new file mode 100644 index 00000000000..45acbb8ab78 --- /dev/null +++ b/packages/grafana-ui/src/types/events.ts @@ -0,0 +1,34 @@ +import { eventFactory } from '@grafana/data'; +import { DataQueryResponseData, DataQueryError } from '.'; + +/** Payloads */ + +export interface PanelChangeViewPayload { + fullscreen?: boolean; + edit?: boolean; + panelId?: number; + toggle?: boolean; +} + +export interface MenuElement { + text: string; + click: string; + role?: string; + shortcut?: string; +} + +/** Events */ + +export const refresh = eventFactory('refresh'); +export const componentDidMount = eventFactory('component-did-mount'); +export const dataError = eventFactory('data-error'); +export const dataReceived = eventFactory('data-received'); +export const dataSnapshotLoad = eventFactory('data-snapshot-load'); +export const editModeInitialized = eventFactory('init-edit-mode'); +export const initPanelActions = eventFactory('init-panel-actions'); +export const panelChangeView = eventFactory('panel-change-view'); +export const panelInitialized = eventFactory('panel-initialized'); +export const panelSizeChanged = eventFactory('panel-size-changed'); +export const panelTeardown = eventFactory('panel-teardown'); +export const render = eventFactory('render'); +export const viewModeChanged = eventFactory('view-mode-changed'); diff --git a/packages/grafana-ui/src/types/index.ts b/packages/grafana-ui/src/types/index.ts index 26de1293e06..a4e07453bca 100644 --- a/packages/grafana-ui/src/types/index.ts +++ b/packages/grafana-ui/src/types/index.ts @@ -4,3 +4,6 @@ export * from './app'; export * from './datasource'; export * from './theme'; export * from './input'; + +import * as PanelEvents from './events'; +export { PanelEvents }; diff --git a/public/app/core/components/AppNotifications/AppNotificationList.tsx b/public/app/core/components/AppNotifications/AppNotificationList.tsx index 991ac37eda6..5cc03140696 100644 --- a/public/app/core/components/AppNotifications/AppNotificationList.tsx +++ b/public/app/core/components/AppNotifications/AppNotificationList.tsx @@ -4,11 +4,13 @@ import AppNotificationItem from './AppNotificationItem'; import { notifyApp, clearAppNotification } from 'app/core/actions'; import { connectWithStore } from 'app/core/utils/connectWithReduxStore'; import { AppNotification, StoreState } from 'app/types'; + import { createErrorNotification, createSuccessNotification, createWarningNotification, } from '../../copy/appNotification'; +import { AppEvents } from '@grafana/data'; export interface Props { appNotifications: AppNotification[]; @@ -20,9 +22,9 @@ export class AppNotificationList extends PureComponent { componentDidMount() { const { notifyApp } = this.props; - appEvents.on('alert-warning', (options: string[]) => notifyApp(createWarningNotification(options[0], options[1]))); - appEvents.on('alert-success', (options: string[]) => notifyApp(createSuccessNotification(options[0], options[1]))); - appEvents.on('alert-error', (options: string[]) => notifyApp(createErrorNotification(options[0], options[1]))); + appEvents.on(AppEvents.alertWarning, payload => notifyApp(createWarningNotification(...payload))); + appEvents.on(AppEvents.alertSuccess, payload => notifyApp(createSuccessNotification(...payload))); + appEvents.on(AppEvents.alertError, payload => notifyApp(createErrorNotification(...payload))); } onClearAppNotification = (id: number) => { diff --git a/public/app/core/components/Login/ChangePassword.tsx b/public/app/core/components/Login/ChangePassword.tsx index bf0fe4e92a5..89459416b50 100644 --- a/public/app/core/components/Login/ChangePassword.tsx +++ b/public/app/core/components/Login/ChangePassword.tsx @@ -1,6 +1,7 @@ import React, { PureComponent, SyntheticEvent, ChangeEvent } from 'react'; import { Tooltip } from '@grafana/ui'; import appEvents from 'app/core/app_events'; +import { AppEvents } from '@grafana/data'; interface Props { onSubmit: (pw: string) => void; @@ -42,7 +43,7 @@ export class ChangePassword extends PureComponent { if (valid) { this.props.onSubmit(newPassword); } else { - appEvents.emit('alert-warning', ['New passwords do not match', '']); + appEvents.emit(AppEvents.alertWarning, ['New passwords do not match']); } }; diff --git a/public/app/core/components/Login/LoginCtrl.tsx b/public/app/core/components/Login/LoginCtrl.tsx index 5f4ae10c4b1..598d43fc3a6 100644 --- a/public/app/core/components/Login/LoginCtrl.tsx +++ b/public/app/core/components/Login/LoginCtrl.tsx @@ -8,6 +8,7 @@ import { PureComponent } from 'react'; import { getBackendSrv } from '@grafana/runtime'; import { hot } from 'react-hot-loader'; import appEvents from 'app/core/app_events'; +import { AppEvents } from '@grafana/data'; const isOauthEnabled = () => { return !!config.oauth && Object.keys(config.oauth).length > 0; @@ -52,7 +53,7 @@ export class LoginCtrl extends PureComponent { }; if (config.loginError) { - appEvents.emit('alert-warning', ['Login Failed', config.loginError]); + appEvents.emit(AppEvents.alertWarning, ['Login Failed', config.loginError]); } } diff --git a/public/app/core/components/PageHeader/PageHeader.tsx b/public/app/core/components/PageHeader/PageHeader.tsx index 1f754d1077b..65d9e13c4e1 100644 --- a/public/app/core/components/PageHeader/PageHeader.tsx +++ b/public/app/core/components/PageHeader/PageHeader.tsx @@ -2,6 +2,7 @@ import React, { FormEvent } from 'react'; import classNames from 'classnames'; import appEvents from 'app/core/app_events'; import { NavModel, NavModelItem, NavModelBreadcrumb } from '@grafana/data'; +import { CoreEvents } from 'app/types'; export interface Props { model: NavModel; @@ -15,7 +16,7 @@ const SelectNav = ({ main, customCss }: { main: NavModelItem; customCss: string const gotoUrl = (evt: FormEvent) => { const element = evt.target as HTMLSelectElement; const url = element.options[element.selectedIndex].value; - appEvents.emit('location-change', { href: url }); + appEvents.emit(CoreEvents.locationChange, { href: url }); }; return ( diff --git a/public/app/core/components/layout_selector/layout_selector.ts b/public/app/core/components/layout_selector/layout_selector.ts index a72a2fd5b4a..26988228df4 100644 --- a/public/app/core/components/layout_selector/layout_selector.ts +++ b/public/app/core/components/layout_selector/layout_selector.ts @@ -1,5 +1,7 @@ import store from 'app/core/store'; import coreModule from 'app/core/core_module'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; +import { CoreEvents } from 'app/types'; const template = `
@@ -16,20 +18,20 @@ export class LayoutSelectorCtrl { mode: string; /** @ngInject */ - constructor(private $rootScope: any) { + constructor(private $rootScope: GrafanaRootScope) { this.mode = store.get('grafana.list.layout.mode') || 'grid'; } listView() { this.mode = 'list'; store.set('grafana.list.layout.mode', 'list'); - this.$rootScope.appEvent('layout-mode-changed', 'list'); + this.$rootScope.appEvent(CoreEvents.layoutModeChanged, 'list'); } gridView() { this.mode = 'grid'; store.set('grafana.list.layout.mode', 'grid'); - this.$rootScope.appEvent('layout-mode-changed', 'grid'); + this.$rootScope.appEvent(CoreEvents.layoutModeChanged, 'grid'); } } @@ -46,7 +48,7 @@ export function layoutSelector() { } /** @ngInject */ -export function layoutMode($rootScope: any) { +export function layoutMode($rootScope: GrafanaRootScope) { return { restrict: 'A', scope: {}, @@ -56,7 +58,7 @@ export function layoutMode($rootScope: any) { elem.addClass(className); $rootScope.onAppEvent( - 'layout-mode-changed', + CoreEvents.layoutModeChanged, (evt: any, newLayout: any) => { elem.removeClass(className); className = 'card-list-layout-' + newLayout; diff --git a/public/app/core/components/manage_dashboards/manage_dashboards.ts b/public/app/core/components/manage_dashboards/manage_dashboards.ts index c57554ec484..b4ae964f0f3 100644 --- a/public/app/core/components/manage_dashboards/manage_dashboards.ts +++ b/public/app/core/components/manage_dashboards/manage_dashboards.ts @@ -5,6 +5,7 @@ import { SearchSrv } from 'app/core/services/search_srv'; import { BackendSrv } from 'app/core/services/backend_srv'; import { NavModelSrv } from 'app/core/nav_model_srv'; import { ContextSrv } from 'app/core/services/context_srv'; +import { CoreEvents } from 'app/types'; export interface Section { id: number; @@ -200,7 +201,7 @@ export class ManageDashboardsCtrl { text += `selected dashboard${dashCount === 1 ? '' : 's'}?`; } - appEvents.emit('confirm-modal', { + appEvents.emit(CoreEvents.showConfirmModal, { title: 'Delete', text: text, text2: text2, @@ -236,7 +237,7 @@ export class ManageDashboardsCtrl { '' + ''; - appEvents.emit('show-modal', { + appEvents.emit(CoreEvents.showModal, { templateHtml: template, modalClass: 'modal--narrow', model: { diff --git a/public/app/core/components/search/search.ts b/public/app/core/components/search/search.ts index fbeb9092298..1316a33b669 100644 --- a/public/app/core/components/search/search.ts +++ b/public/app/core/components/search/search.ts @@ -6,6 +6,7 @@ import { contextSrv } from 'app/core/services/context_srv'; import appEvents from 'app/core/app_events'; import { parse, SearchParserOptions, SearchParserResult } from 'search-query-parser'; import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; +import { CoreEvents } from 'app/types'; export interface SearchQuery { query: string; @@ -60,9 +61,9 @@ export class SearchCtrl { /** @ngInject */ constructor($scope: any, private $location: any, private $timeout: any, private searchSrv: SearchSrv) { - appEvents.on('show-dash-search', this.openSearch.bind(this), $scope); - appEvents.on('hide-dash-search', this.closeSearch.bind(this), $scope); - appEvents.on('search-query', debounce(this.search.bind(this), 500), $scope); + appEvents.on(CoreEvents.showDashSearch, this.openSearch.bind(this), $scope); + appEvents.on(CoreEvents.hideDashSearch, this.closeSearch.bind(this), $scope); + appEvents.on(CoreEvents.searchQuery, debounce(this.search.bind(this), 500), $scope); this.initialFolderFilterTitle = 'All'; this.isEditor = contextSrv.isEditor; @@ -95,7 +96,7 @@ export class SearchCtrl { } else { this.query = query; } - appEvents.emit('search-query'); + appEvents.emit(CoreEvents.searchQuery); } openSearch(payload: OpenSearchParams = {}) { diff --git a/public/app/core/components/search/search_results.ts b/public/app/core/components/search/search_results.ts index 20cd49d3443..e8a7c23755f 100644 --- a/public/app/core/components/search/search_results.ts +++ b/public/app/core/components/search/search_results.ts @@ -1,6 +1,7 @@ import _ from 'lodash'; import coreModule from '../../core_module'; import appEvents from 'app/core/app_events'; +import { CoreEvents } from 'app/types'; export class SearchResultsCtrl { results: any; @@ -65,7 +66,7 @@ export class SearchResultsCtrl { onItemClick(item: any) { //Check if one string can be found in the other if (this.$location.path().indexOf(item.url) > -1 || item.url.indexOf(this.$location.path()) > -1) { - appEvents.emit('hide-dash-search'); + appEvents.emit(CoreEvents.hideDashSearch); } } diff --git a/public/app/core/components/sidemenu/BottomNavLinks.test.tsx b/public/app/core/components/sidemenu/BottomNavLinks.test.tsx index d59973fe02d..e3a98bd9736 100644 --- a/public/app/core/components/sidemenu/BottomNavLinks.test.tsx +++ b/public/app/core/components/sidemenu/BottomNavLinks.test.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import BottomNavLinks from './BottomNavLinks'; import appEvents from '../../app_events'; +import { CoreEvents } from 'app/types'; jest.mock('../../app_events', () => ({ emit: jest.fn(), @@ -93,7 +94,7 @@ describe('Functions', () => { const instance = wrapper.instance() as BottomNavLinks; instance.itemClicked(mockEvent as any, child); - expect(appEvents.emit).toHaveBeenCalledWith('show-modal', { templateHtml: '' }); + expect(appEvents.emit).toHaveBeenCalledWith(CoreEvents.showModal, { templateHtml: '' }); }); }); }); diff --git a/public/app/core/components/sidemenu/BottomNavLinks.tsx b/public/app/core/components/sidemenu/BottomNavLinks.tsx index 7e9f5164728..f86f353e3a6 100644 --- a/public/app/core/components/sidemenu/BottomNavLinks.tsx +++ b/public/app/core/components/sidemenu/BottomNavLinks.tsx @@ -2,6 +2,7 @@ import React, { PureComponent } from 'react'; import appEvents from '../../app_events'; import { User } from '../../services/context_srv'; import { NavModelItem } from '@grafana/data'; +import { CoreEvents } from 'app/types'; export interface Props { link: NavModelItem; @@ -12,14 +13,14 @@ class BottomNavLinks extends PureComponent { itemClicked = (event: React.SyntheticEvent, child: NavModelItem) => { if (child.url === '/shortcuts') { event.preventDefault(); - appEvents.emit('show-modal', { + appEvents.emit(CoreEvents.showModal, { templateHtml: '', }); } }; switchOrg = () => { - appEvents.emit('show-modal', { + appEvents.emit(CoreEvents.showModal, { templateHtml: '', }); }; diff --git a/public/app/core/components/sidemenu/SideMenu.test.tsx b/public/app/core/components/sidemenu/SideMenu.test.tsx index 6352833490a..4732e355142 100644 --- a/public/app/core/components/sidemenu/SideMenu.test.tsx +++ b/public/app/core/components/sidemenu/SideMenu.test.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { SideMenu } from './SideMenu'; import appEvents from '../../app_events'; +import { CoreEvents } from 'app/types'; jest.mock('../../app_events', () => ({ emit: jest.fn(), @@ -58,7 +59,7 @@ describe('Functions', () => { instance.toggleSideMenuSmallBreakpoint(); it('should emit toggle sidemenu event', () => { - expect(appEvents.emit).toHaveBeenCalledWith('toggle-sidemenu-mobile'); + expect(appEvents.emit).toHaveBeenCalledWith(CoreEvents.toggleSidemenuMobile); }); }); }); diff --git a/public/app/core/components/sidemenu/SideMenu.tsx b/public/app/core/components/sidemenu/SideMenu.tsx index b0ef053f746..632b200438e 100644 --- a/public/app/core/components/sidemenu/SideMenu.tsx +++ b/public/app/core/components/sidemenu/SideMenu.tsx @@ -3,12 +3,13 @@ import appEvents from '../../app_events'; import TopSection from './TopSection'; import BottomSection from './BottomSection'; import config from 'app/core/config'; +import { CoreEvents } from 'app/types'; const homeUrl = config.appSubUrl || '/'; export class SideMenu extends PureComponent { toggleSideMenuSmallBreakpoint = () => { - appEvents.emit('toggle-sidemenu-mobile'); + appEvents.emit(CoreEvents.toggleSidemenuMobile); }; render() { diff --git a/public/app/core/controllers/error_ctrl.ts b/public/app/core/controllers/error_ctrl.ts index 7b834946f40..780b0ecfce0 100644 --- a/public/app/core/controllers/error_ctrl.ts +++ b/public/app/core/controllers/error_ctrl.ts @@ -1,6 +1,7 @@ import config from 'app/core/config'; import coreModule from '../core_module'; import appEvents from 'app/core/app_events'; +import { CoreEvents } from 'app/types'; export class ErrorCtrl { /** @ngInject */ @@ -9,12 +10,12 @@ export class ErrorCtrl { $scope.appSubUrl = config.appSubUrl; if (!contextSrv.isSignedIn) { - appEvents.emit('toggle-sidemenu-hidden'); + appEvents.emit(CoreEvents.toggleSidemenuHidden); } $scope.$on('destroy', () => { if (!contextSrv.isSignedIn) { - appEvents.emit('toggle-sidemenu-hidden'); + appEvents.emit(CoreEvents.toggleSidemenuHidden); } }); } diff --git a/public/app/core/controllers/reset_password_ctrl.ts b/public/app/core/controllers/reset_password_ctrl.ts index 86c0caf8b23..cc5f0a78088 100644 --- a/public/app/core/controllers/reset_password_ctrl.ts +++ b/public/app/core/controllers/reset_password_ctrl.ts @@ -1,6 +1,7 @@ import coreModule from '../core_module'; import config from 'app/core/config'; import { BackendSrv } from '../services/backend_srv'; +import { AppEvents } from '@grafana/data'; export class ResetPasswordCtrl { /** @ngInject */ @@ -41,7 +42,7 @@ export class ResetPasswordCtrl { } if ($scope.formModel.newPassword !== $scope.formModel.confirmPassword) { - $scope.appEvent('alert-warning', ['New passwords do not match', '']); + $scope.appEvent(AppEvents.alertWarning, ['New passwords do not match']); return; } diff --git a/public/app/core/copy/appNotification.ts b/public/app/core/copy/appNotification.ts index 4c44b4f68d3..92cf7f8e118 100644 --- a/public/app/core/copy/appNotification.ts +++ b/public/app/core/copy/appNotification.ts @@ -25,14 +25,14 @@ const defaultErrorNotification = { timeout: AppNotificationTimeout.Error, }; -export const createSuccessNotification = (title: string, text?: string): AppNotification => ({ +export const createSuccessNotification = (title: string, text = ''): AppNotification => ({ ...defaultSuccessNotification, title: title, text: text, id: Date.now(), }); -export const createErrorNotification = (title: string, text?: any): AppNotification => { +export const createErrorNotification = (title: string, text = ''): AppNotification => { return { ...defaultErrorNotification, title: title, @@ -41,7 +41,7 @@ export const createErrorNotification = (title: string, text?: any): AppNotificat }; }; -export const createWarningNotification = (title: string, text?: string): AppNotification => ({ +export const createWarningNotification = (title: string, text = ''): AppNotification => ({ ...defaultWarningNotification, title: title, text: text, diff --git a/public/app/core/directives/diff-view.ts b/public/app/core/directives/diff-view.ts index b7c41e022c9..665f7049fa9 100644 --- a/public/app/core/directives/diff-view.ts +++ b/public/app/core/directives/diff-view.ts @@ -1,14 +1,16 @@ import angular from 'angular'; import coreModule from '../core_module'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; +import { CoreEvents } from 'app/types'; export class DeltaCtrl { observer: any; /** @ngInject */ - constructor(private $rootScope: any) { + constructor(private $rootScope: GrafanaRootScope) { const waitForCompile = (mutations: any) => { if (mutations.length === 1) { - this.$rootScope.appEvent('json-diff-ready'); + this.$rootScope.appEvent(CoreEvents.jsonDiffReady); } }; @@ -42,7 +44,7 @@ coreModule.directive('diffDelta', delta); // Link to JSON line number export class LinkJSONCtrl { /** @ngInject */ - constructor(private $scope: any, private $rootScope: any, private $anchorScroll: any) {} + constructor(private $scope: any, private $rootScope: GrafanaRootScope, private $anchorScroll: any) {} goToLine(line: number) { let unbind: () => void; diff --git a/public/app/core/directives/misc.ts b/public/app/core/directives/misc.ts index 1fa0eacac4f..4695d943ea7 100644 --- a/public/app/core/directives/misc.ts +++ b/public/app/core/directives/misc.ts @@ -3,6 +3,7 @@ import Clipboard from 'clipboard'; import coreModule from '../core_module'; import kbn from 'app/core/utils/kbn'; import { appEvents } from 'app/core/core'; +import { AppEvents } from '@grafana/data'; /** @ngInject */ function tip($compile: any) { @@ -34,7 +35,7 @@ function clipboardButton() { }); scope.clipboard.on('success', () => { - appEvents.emit('alert-success', ['Content copied to clipboard']); + appEvents.emit(AppEvents.alertSuccess, ['Content copied to clipboard']); }); scope.$on('$destroy', () => { diff --git a/public/app/core/directives/value_select_dropdown.ts b/public/app/core/directives/value_select_dropdown.ts index 7675f7e28ec..145463407e2 100644 --- a/public/app/core/directives/value_select_dropdown.ts +++ b/public/app/core/directives/value_select_dropdown.ts @@ -1,6 +1,7 @@ import angular from 'angular'; import _ from 'lodash'; import coreModule from '../core_module'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; export class ValueSelectDropdownCtrl { dropdownVisible: any; @@ -245,7 +246,7 @@ export class ValueSelectDropdownCtrl { } /** @ngInject */ -export function valueSelectDropdown($compile: any, $window: any, $timeout: any, $rootScope: any) { +export function valueSelectDropdown($compile: any, $window: any, $timeout: any, $rootScope: GrafanaRootScope) { return { scope: { dashboard: '=', variable: '=', onUpdated: '&' }, templateUrl: 'public/app/partials/valueSelectDropdown.html', diff --git a/public/app/core/profiler.ts b/public/app/core/profiler.ts index 02f58b92d22..a55c46ab9aa 100644 --- a/public/app/core/profiler.ts +++ b/public/app/core/profiler.ts @@ -1,10 +1,12 @@ +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; + export class Profiler { panelsRendered: number; enabled: boolean; - $rootScope: any; + $rootScope: GrafanaRootScope; window: any; - init(config: any, $rootScope: any) { + init(config: any, $rootScope: GrafanaRootScope) { this.$rootScope = $rootScope; this.window = window; diff --git a/public/app/core/services/AngularLoader.ts b/public/app/core/services/AngularLoader.ts index cd5506a9458..021d3c6da3d 100644 --- a/public/app/core/services/AngularLoader.ts +++ b/public/app/core/services/AngularLoader.ts @@ -7,10 +7,11 @@ import { AngularLoader as AngularLoaderInterface, setAngularLoader as setAngularLoaderInterface, } from '@grafana/runtime'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; export class AngularLoader implements AngularLoaderInterface { /** @ngInject */ - constructor(private $compile: any, private $rootScope: any) {} + constructor(private $compile: any, private $rootScope: GrafanaRootScope) {} load(elem: any, scopeProps: any, template: string): AngularComponent { const scope = this.$rootScope.$new(); diff --git a/public/app/core/services/analytics.ts b/public/app/core/services/analytics.ts index 1bf143d00cf..3214bb4b611 100644 --- a/public/app/core/services/analytics.ts +++ b/public/app/core/services/analytics.ts @@ -1,10 +1,11 @@ import $ from 'jquery'; import coreModule from 'app/core/core_module'; import config from 'app/core/config'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; export class Analytics { /** @ngInject */ - constructor(private $rootScope: any, private $location: any) {} + constructor(private $rootScope: GrafanaRootScope, private $location: any) {} gaInit() { $.ajax({ diff --git a/public/app/core/services/backend_srv.ts b/public/app/core/services/backend_srv.ts index bb8cc44a7a9..aa783de2e3b 100644 --- a/public/app/core/services/backend_srv.ts +++ b/public/app/core/services/backend_srv.ts @@ -6,8 +6,9 @@ import config from 'app/core/config'; import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; import { DashboardSearchHit } from 'app/types/search'; import { ContextSrv } from './context_srv'; -import { FolderInfo, DashboardDTO } from 'app/types'; +import { FolderInfo, DashboardDTO, CoreEvents } from 'app/types'; import { BackendSrv as BackendService, getBackendSrv as getBackendService, BackendSrvRequest } from '@grafana/runtime'; +import { AppEvents } from '@grafana/data'; export class BackendSrv implements BackendService { private inFlightRequests: { [key: string]: Array> } = {}; @@ -60,16 +61,10 @@ export class BackendSrv implements BackendService { } if (err.status === 422) { - appEvents.emit('alert-warning', ['Validation failed', data.message]); + appEvents.emit(AppEvents.alertWarning, ['Validation failed', data.message]); throw data; } - let severity = 'error'; - - if (err.status < 500) { - severity = 'warning'; - } - if (data.message) { let description = ''; let message = data.message; @@ -78,7 +73,7 @@ export class BackendSrv implements BackendService { message = 'Error'; } - appEvents.emit('alert-' + severity, [message, description]); + appEvents.emit(err.status < 500 ? AppEvents.alertWarning : AppEvents.alertError, [message, description]); } throw data; @@ -105,7 +100,7 @@ export class BackendSrv implements BackendService { if (options.method !== 'GET') { if (results && results.data.message) { if (options.showSuccessAlert !== false) { - appEvents.emit('alert-success', [results.data.message]); + appEvents.emit(AppEvents.alertSuccess, [results.data.message]); } } } @@ -192,7 +187,7 @@ export class BackendSrv implements BackendService { return this.$http(options) .then((response: any) => { if (!options.silent) { - appEvents.emit('ds-request-response', response); + appEvents.emit(CoreEvents.dsRequestResponse, response); } return response; }) @@ -232,7 +227,7 @@ export class BackendSrv implements BackendService { err.data.message = err.data.error; } if (!options.silent) { - appEvents.emit('ds-request-error', err); + appEvents.emit(CoreEvents.dsRequestError, err); } throw err; }) diff --git a/public/app/core/services/bridge_srv.ts b/public/app/core/services/bridge_srv.ts index c5769a61c6b..88bcaadc6e7 100644 --- a/public/app/core/services/bridge_srv.ts +++ b/public/app/core/services/bridge_srv.ts @@ -3,7 +3,9 @@ import appEvents from 'app/core/app_events'; import { store } from 'app/store/store'; import locationUtil from 'app/core/utils/location_util'; import { updateLocation } from 'app/core/actions'; -import { ITimeoutService, ILocationService, IWindowService, IRootScopeService } from 'angular'; +import { ITimeoutService, ILocationService, IWindowService } from 'angular'; +import { CoreEvents } from 'app/types'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; // Services that handles angular -> redux store sync & other react <-> angular sync export class BridgeSrv { @@ -14,7 +16,7 @@ export class BridgeSrv { private $location: ILocationService, private $timeout: ITimeoutService, private $window: IWindowService, - private $rootScope: IRootScopeService, + private $rootScope: GrafanaRootScope, private $route: any ) { this.fullPageReloadRoutes = ['/logout']; @@ -62,7 +64,7 @@ export class BridgeSrv { } }); - appEvents.on('location-change', (payload: any) => { + appEvents.on(CoreEvents.locationChange, payload => { const urlWithoutBase = locationUtil.stripBaseFromUrl(payload.href); if (this.fullPageReloadRoutes.indexOf(urlWithoutBase) > -1) { this.$window.location.href = payload.href; diff --git a/public/app/core/services/keybindingSrv.ts b/public/app/core/services/keybindingSrv.ts index 0cdfa731f92..02a0e5bfb7a 100644 --- a/public/app/core/services/keybindingSrv.ts +++ b/public/app/core/services/keybindingSrv.ts @@ -5,11 +5,14 @@ import appEvents from 'app/core/app_events'; import { getExploreUrl } from 'app/core/utils/explore'; import locationUtil from 'app/core/utils/location_util'; import { store } from 'app/store/store'; +import { CoreEvents, AppEventEmitter } from 'app/types'; import Mousetrap from 'mousetrap'; +import { PanelEvents } from '@grafana/ui'; import 'mousetrap-global-bind'; import { ContextSrv } from './context_srv'; -import { ILocationService, ITimeoutService } from 'angular'; +import { ILocationService, ITimeoutService, IRootScopeService } from 'angular'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; export class KeybindingSrv { helpModal: boolean; @@ -18,7 +21,7 @@ export class KeybindingSrv { /** @ngInject */ constructor( - private $rootScope: any, + private $rootScope: GrafanaRootScope, private $location: ILocationService, private $timeout: ITimeoutService, private datasourceSrv: any, @@ -33,9 +36,9 @@ export class KeybindingSrv { }); this.setupGlobal(); - appEvents.on('show-modal', () => (this.modalOpen = true)); - appEvents.on('timepickerOpen', () => (this.timepickerOpen = true)); - appEvents.on('timepickerClosed', () => (this.timepickerOpen = false)); + appEvents.on(CoreEvents.showModal, () => (this.modalOpen = true)); + appEvents.on(CoreEvents.timepickerOpen, () => (this.timepickerOpen = true)); + appEvents.on(CoreEvents.timepickerClosed, () => (this.timepickerOpen = false)); } setupGlobal() { @@ -78,7 +81,7 @@ export class KeybindingSrv { } openSearch() { - appEvents.emit('show-dash-search'); + appEvents.emit(CoreEvents.showDashSearch); } openAlerting() { @@ -94,11 +97,11 @@ export class KeybindingSrv { } showHelpModal() { - appEvents.emit('show-modal', { templateHtml: '' }); + appEvents.emit(CoreEvents.showModal, { templateHtml: '' }); } exit() { - appEvents.emit('hide-modal'); + appEvents.emit(CoreEvents.hideModal); if (this.modalOpen) { this.modalOpen = false; @@ -106,7 +109,7 @@ export class KeybindingSrv { } if (this.timepickerOpen) { - this.$rootScope.appEvent('closeTimepicker'); + this.$rootScope.appEvent(CoreEvents.closeTimepicker); this.timepickerOpen = false; return; } @@ -120,12 +123,12 @@ export class KeybindingSrv { } if (search.fullscreen) { - appEvents.emit('panel-change-view', { fullscreen: false, edit: false }); + appEvents.emit(PanelEvents.panelChangeView, { fullscreen: false, edit: false }); return; } if (search.kiosk) { - this.$rootScope.appEvent('toggle-kiosk-mode', { exit: true }); + this.$rootScope.appEvent(CoreEvents.toggleKioskMode, { exit: true }); } } @@ -164,37 +167,37 @@ export class KeybindingSrv { this.$location.search(search); } - setupDashboardBindings(scope: any, dashboard: any) { + setupDashboardBindings(scope: IRootScopeService & AppEventEmitter, dashboard: any) { this.bind('mod+o', () => { dashboard.graphTooltip = (dashboard.graphTooltip + 1) % 3; - appEvents.emit('graph-hover-clear'); + appEvents.emit(CoreEvents.graphHoverClear); dashboard.startRefresh(); }); this.bind('mod+s', () => { - scope.appEvent('save-dashboard'); + scope.appEvent(CoreEvents.saveDashboard); }); this.bind('t z', () => { - scope.appEvent('zoom-out', 2); + scope.appEvent(CoreEvents.zoomOut, 2); }); this.bind('ctrl+z', () => { - scope.appEvent('zoom-out', 2); + scope.appEvent(CoreEvents.zoomOut, 2); }); this.bind('t left', () => { - scope.appEvent('shift-time', -1); + scope.appEvent(CoreEvents.shiftTime, -1); }); this.bind('t right', () => { - scope.appEvent('shift-time', 1); + scope.appEvent(CoreEvents.shiftTime, 1); }); // edit panel this.bind('e', () => { if (dashboard.meta.focusPanelId && dashboard.meta.canEdit) { - appEvents.emit('panel-change-view', { + appEvents.emit(PanelEvents.panelChangeView, { fullscreen: true, edit: true, panelId: dashboard.meta.focusPanelId, @@ -206,7 +209,7 @@ export class KeybindingSrv { // view panel this.bind('v', () => { if (dashboard.meta.focusPanelId) { - appEvents.emit('panel-change-view', { + appEvents.emit(PanelEvents.panelChangeView, { fullscreen: true, panelId: dashboard.meta.focusPanelId, toggle: true, @@ -233,7 +236,7 @@ export class KeybindingSrv { // delete panel this.bind('p r', () => { if (dashboard.meta.focusPanelId && dashboard.meta.canEdit) { - appEvents.emit('remove-panel', dashboard.meta.focusPanelId); + appEvents.emit(CoreEvents.removePanel, dashboard.meta.focusPanelId); dashboard.meta.focusPanelId = 0; } }); @@ -249,12 +252,12 @@ export class KeybindingSrv { // share panel this.bind('p s', () => { if (dashboard.meta.focusPanelId) { - const shareScope = scope.$new(); + const shareScope: any = scope.$new(); const panelInfo = dashboard.getPanelInfoById(dashboard.meta.focusPanelId); shareScope.panel = panelInfo.panel; shareScope.dashboard = dashboard; - appEvents.emit('show-modal', { + appEvents.emit(CoreEvents.showModal, { src: 'public/app/features/dashboard/components/ShareModal/template.html', scope: shareScope, }); @@ -301,11 +304,11 @@ export class KeybindingSrv { }); this.bind('d k', () => { - appEvents.emit('toggle-kiosk-mode'); + appEvents.emit(CoreEvents.toggleKioskMode); }); this.bind('d v', () => { - appEvents.emit('toggle-view-mode'); + appEvents.emit(CoreEvents.toggleViewMode); }); //Autofit panels diff --git a/public/app/core/services/popover_srv.ts b/public/app/core/services/popover_srv.ts index 3442415cbb2..89f682dd154 100644 --- a/public/app/core/services/popover_srv.ts +++ b/public/app/core/services/popover_srv.ts @@ -2,9 +2,10 @@ import _ from 'lodash'; import coreModule from 'app/core/core_module'; // @ts-ignore import Drop from 'tether-drop'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; /** @ngInject */ -function popoverSrv(this: any, $compile: any, $rootScope: any, $timeout: any) { +function popoverSrv(this: any, $compile: any, $rootScope: GrafanaRootScope, $timeout: any) { let openDrop: any = null; this.close = () => { diff --git a/public/app/core/services/util_srv.ts b/public/app/core/services/util_srv.ts index 75d707275d4..a43ef9e5e1c 100644 --- a/public/app/core/services/util_srv.ts +++ b/public/app/core/services/util_srv.ts @@ -1,16 +1,18 @@ import coreModule from 'app/core/core_module'; import appEvents from 'app/core/app_events'; +import { CoreEvents } from 'app/types'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; export class UtilSrv { modalScope: any; /** @ngInject */ - constructor(private $rootScope: any, private $modal: any) {} + constructor(private $rootScope: GrafanaRootScope, private $modal: any) {} init() { - appEvents.on('show-modal', this.showModal.bind(this), this.$rootScope); - appEvents.on('hide-modal', this.hideModal.bind(this), this.$rootScope); - appEvents.on('confirm-modal', this.showConfirmModal.bind(this), this.$rootScope); + appEvents.on(CoreEvents.showModal, this.showModal.bind(this), this.$rootScope); + appEvents.on(CoreEvents.hideModal, this.hideModal.bind(this), this.$rootScope); + appEvents.on(CoreEvents.showConfirmModal, this.showConfirmModal.bind(this), this.$rootScope); } hideModal() { @@ -50,7 +52,7 @@ export class UtilSrv { } showConfirmModal(payload: any) { - const scope = this.$rootScope.$new(); + const scope: any = this.$rootScope.$new(); scope.updateConfirmText = (value: any) => { scope.confirmTextValid = payload.confirmText.toLowerCase() === value.toLowerCase(); @@ -70,7 +72,7 @@ export class UtilSrv { scope.noText = payload.noText || 'Cancel'; scope.confirmTextValid = scope.confirmText ? false : true; - appEvents.emit('show-modal', { + appEvents.emit(CoreEvents.showModal, { src: 'public/app/partials/confirm_modal.html', scope: scope, modalClass: 'confirm-modal', diff --git a/public/app/core/specs/emitter.test.ts b/public/app/core/specs/emitter.test.ts index 549dcb2d84a..8d19d5d8724 100644 --- a/public/app/core/specs/emitter.test.ts +++ b/public/app/core/specs/emitter.test.ts @@ -1,4 +1,7 @@ import { Emitter } from '../utils/emitter'; +import { eventFactory } from '@grafana/data'; + +const testEvent = eventFactory('test'); describe('Emitter', () => { describe('given 2 subscribers', () => { @@ -7,14 +10,14 @@ describe('Emitter', () => { let sub1Called = false; let sub2Called = false; - events.on('test', () => { + events.on(testEvent, () => { sub1Called = true; }); - events.on('test', () => { + events.on(testEvent, () => { sub2Called = true; }); - events.emit('test', null); + events.emit(testEvent, null); expect(sub1Called).toBe(true); expect(sub2Called).toBe(true); @@ -28,10 +31,10 @@ describe('Emitter', () => { sub1Called += 1; } - events.on('test', handler); - events.on('test', handler); + events.on(testEvent, handler); + events.on(testEvent, handler); - events.emit('test', null); + events.emit(testEvent, null); expect(sub1Called).toBe(2); }); @@ -41,20 +44,20 @@ describe('Emitter', () => { let sub1Called = 0; let sub2Called = 0; - events.on('test', () => { + events.on(testEvent, () => { sub1Called++; throw { message: 'hello' }; }); - events.on('test', () => { + events.on(testEvent, () => { sub2Called++; }); try { - events.emit('test', null); + events.emit(testEvent, null); } catch (_) {} try { - events.emit('test', null); + events.emit(testEvent, null); } catch (_) {} expect(sub1Called).toBe(2); diff --git a/public/app/core/specs/search_results.test.ts b/public/app/core/specs/search_results.test.ts index 5893f99bbc7..5db527e885f 100644 --- a/public/app/core/specs/search_results.test.ts +++ b/public/app/core/specs/search_results.test.ts @@ -1,6 +1,7 @@ import { SearchResultsCtrl } from '../components/search/search_results'; import { beforeEach, afterEach } from 'test/lib/common'; import appEvents from 'app/core/app_events'; +import { CoreEvents } from 'app/types'; jest.mock('app/core/app_events', () => { return { @@ -121,7 +122,7 @@ describe('SearchResultsCtrl', () => { it('should close the search', () => { expect(appEventsMock.emit.mock.calls.length).toBe(1); - expect(appEventsMock.emit.mock.calls[0][0]).toBe('hide-dash-search'); + expect(appEventsMock.emit.mock.calls[0][0]).toBe(CoreEvents.hideDashSearch); }); }); diff --git a/public/app/core/utils/emitter.ts b/public/app/core/utils/emitter.ts index 9495900b4ce..3060cc3da17 100644 --- a/public/app/core/utils/emitter.ts +++ b/public/app/core/utils/emitter.ts @@ -1,4 +1,5 @@ import { EventEmitter } from 'eventemitter3'; +import { AppEvent } from '@grafana/data'; export class Emitter { private emitter: EventEmitter; @@ -7,29 +8,83 @@ export class Emitter { this.emitter = new EventEmitter(); } - emit(name: string, data?: any) { - this.emitter.emit(name, data); + /** + * DEPRECATED. + */ + emit(name: string, data?: any): void; + + /** + * Emits an `event` with `payload`. + */ + emit(event: AppEvent): void; + emit extends T ? Partial : never>(event: AppEvent): void; + emit(event: AppEvent, payload: T): void; + emit(event: AppEvent | string, payload?: T | any): void { + if (typeof event === 'string') { + console.log(`Using strings as events is deprecated and will be removed in a future version. (${event})`); + this.emitter.emit(event, payload); + } else { + this.emitter.emit(event.name, payload); + } } - on(name: string, handler: (payload?: any) => void, scope?: any) { - this.emitter.on(name, handler); + /** + * DEPRECATED. + */ + on(name: string, handler: (payload?: any) => void, scope?: any): void; + + /** + * Handles `event` with `handler()` when emitted. + */ + on(event: AppEvent, handler: () => void, scope?: any): void; + on extends T ? Partial : never>(event: AppEvent, handler: () => void, scope?: any): void; + on(event: AppEvent, handler: (payload: T) => void, scope?: any): void; + on(event: AppEvent | string, handler: (payload?: T | any) => void, scope?: any) { + if (typeof event === 'string') { + console.log(`Using strings as events is deprecated and will be removed in a future version. (${event})`); + this.emitter.on(event, handler); + + if (scope) { + const unbind = scope.$on('$destroy', () => { + this.emitter.off(event, handler); + unbind(); + }); + } + return; + } + + this.emitter.on(event.name, handler); if (scope) { const unbind = scope.$on('$destroy', () => { - this.emitter.off(name, handler); + this.emitter.off(event.name, handler); unbind(); }); } } + /** + * DEPRECATED. + */ + off(name: string, handler: (payload?: any) => void): void; + + off(event: AppEvent, handler: () => void): void; + off extends T ? Partial : never>(event: AppEvent, handler: () => void, scope?: any): void; + off(event: AppEvent, handler: (payload: T) => void): void; + off(event: AppEvent | string, handler: (payload?: T | any) => void) { + if (typeof event === 'string') { + console.log(`Using strings as events is deprecated and will be removed in a future version. (${event})`); + this.emitter.off(event, handler); + return; + } + + this.emitter.off(event.name, handler); + } + removeAllListeners(evt?: string) { this.emitter.removeAllListeners(evt); } - off(name: string, handler: (payload?: any) => void) { - this.emitter.off(name, handler); - } - getEventCount(): number { return (this.emitter as any)._eventsCount; } diff --git a/public/app/features/admin/AdminEditUserCtrl.ts b/public/app/features/admin/AdminEditUserCtrl.ts index 5eb95f2656c..9aeb0f7e0ea 100644 --- a/public/app/features/admin/AdminEditUserCtrl.ts +++ b/public/app/features/admin/AdminEditUserCtrl.ts @@ -1,13 +1,19 @@ import _ from 'lodash'; -import { dateTime } from '@grafana/data'; import { BackendSrv } from 'app/core/services/backend_srv'; import { NavModelSrv } from 'app/core/core'; import { User } from 'app/core/services/context_srv'; -import { UserSession } from 'app/types'; +import { UserSession, Scope, CoreEvents, AppEventEmitter } from 'app/types'; +import { dateTime } from '@grafana/data'; export default class AdminEditUserCtrl { /** @ngInject */ - constructor($scope: any, $routeParams: any, backendSrv: BackendSrv, $location: any, navModelSrv: NavModelSrv) { + constructor( + $scope: Scope & AppEventEmitter, + $routeParams: any, + backendSrv: BackendSrv, + $location: any, + navModelSrv: NavModelSrv + ) { $scope.user = {}; $scope.sessions = []; $scope.newOrg = { name: '', role: 'Editor' }; @@ -162,7 +168,7 @@ export default class AdminEditUserCtrl { }; $scope.deleteUser = (user: any) => { - $scope.appEvent('confirm-modal', { + $scope.appEvent(CoreEvents.showConfirmModal, { title: 'Delete', text: 'Do you want to delete ' + user.login + '?', icon: 'fa-trash', diff --git a/public/app/features/admin/AdminListOrgsCtrl.ts b/public/app/features/admin/AdminListOrgsCtrl.ts index 356f30cebb1..6aec34dabd0 100644 --- a/public/app/features/admin/AdminListOrgsCtrl.ts +++ b/public/app/features/admin/AdminListOrgsCtrl.ts @@ -1,9 +1,10 @@ import { BackendSrv } from 'app/core/services/backend_srv'; import { NavModelSrv } from 'app/core/core'; +import { Scope, CoreEvents, AppEventEmitter } from 'app/types'; export default class AdminListOrgsCtrl { /** @ngInject */ - constructor($scope: any, backendSrv: BackendSrv, navModelSrv: NavModelSrv) { + constructor($scope: Scope & AppEventEmitter, backendSrv: BackendSrv, navModelSrv: NavModelSrv) { $scope.init = () => { $scope.navModel = navModelSrv.getNav('admin', 'global-orgs', 0); $scope.getOrgs(); @@ -16,9 +17,9 @@ export default class AdminListOrgsCtrl { }; $scope.deleteOrg = (org: any) => { - $scope.appEvent('confirm-modal', { + $scope.appEvent(CoreEvents.showConfirmModal, { title: 'Delete', - text: 'Do you want to delete organization ' + org.name + '?', + text: `Do you want to delete organization ${org.name}?`, text2: 'All dashboards for this organization will be removed!', icon: 'fa-trash', yesText: 'Delete', diff --git a/public/app/features/alerting/AlertRuleList.test.tsx b/public/app/features/alerting/AlertRuleList.test.tsx index a25efca1bb1..da0121a9296 100644 --- a/public/app/features/alerting/AlertRuleList.test.tsx +++ b/public/app/features/alerting/AlertRuleList.test.tsx @@ -6,6 +6,7 @@ import appEvents from '../../core/app_events'; import { mockActionCreator } from 'app/core/redux'; import { updateLocation } from 'app/core/actions'; import { NavModel } from '@grafana/data'; +import { CoreEvents } from 'app/types'; jest.mock('../../core/app_events', () => ({ emit: jest.fn(), @@ -139,7 +140,7 @@ describe('Functions', () => { instance.onOpenHowTo(); - expect(appEvents.emit).toHaveBeenCalledWith('show-modal', { + expect(appEvents.emit).toHaveBeenCalledWith(CoreEvents.showModal, { src: 'public/app/features/alerting/partials/alert_howto.html', modalClass: 'confirm-modal', model: {}, diff --git a/public/app/features/alerting/AlertRuleList.tsx b/public/app/features/alerting/AlertRuleList.tsx index 032645dc272..96cb1887683 100644 --- a/public/app/features/alerting/AlertRuleList.tsx +++ b/public/app/features/alerting/AlertRuleList.tsx @@ -6,7 +6,7 @@ import AlertRuleItem from './AlertRuleItem'; import appEvents from 'app/core/app_events'; import { updateLocation } from 'app/core/actions'; import { getNavModel } from 'app/core/selectors/navModel'; -import { StoreState, AlertRule } from 'app/types'; +import { StoreState, AlertRule, CoreEvents } from 'app/types'; import { getAlertRulesAsync, setSearchQuery, togglePauseAlertRule } from './state/actions'; import { getAlertRuleItems, getSearchQuery } from './state/selectors'; import { FilterInput } from 'app/core/components/FilterInput/FilterInput'; @@ -64,7 +64,7 @@ export class AlertRuleList extends PureComponent { }; onOpenHowTo = () => { - appEvents.emit('show-modal', { + appEvents.emit(CoreEvents.showModal, { src: 'public/app/features/alerting/partials/alert_howto.html', modalClass: 'confirm-modal', model: {}, diff --git a/public/app/features/alerting/AlertTab.tsx b/public/app/features/alerting/AlertTab.tsx index 8e09beda49a..21f18feaa49 100644 --- a/public/app/features/alerting/AlertTab.tsx +++ b/public/app/features/alerting/AlertTab.tsx @@ -19,6 +19,7 @@ import { TestRuleResult } from './TestRuleResult'; import { AppNotificationSeverity, StoreState } from 'app/types'; import { PanelEditorTabIds, getPanelEditorTab } from '../dashboard/panel_editor/state/reducers'; import { changePanelEditorTab } from '../dashboard/panel_editor/state/actions'; +import { CoreEvents } from 'app/types'; interface Props { angularPanel?: AngularComponent; @@ -116,7 +117,7 @@ class UnConnectedAlertTab extends PureComponent { title: 'Delete', btnType: 'danger', onClick: () => { - appEvents.emit('confirm-modal', { + appEvents.emit(CoreEvents.showConfirmModal, { title: 'Delete Alert', text: 'Are you sure you want to delete this alert rule?', text2: 'You need to save dashboard for the delete to take effect', diff --git a/public/app/features/alerting/AlertTabCtrl.ts b/public/app/features/alerting/AlertTabCtrl.ts index de82193b16b..793b3d632d9 100644 --- a/public/app/features/alerting/AlertTabCtrl.ts +++ b/public/app/features/alerting/AlertTabCtrl.ts @@ -11,6 +11,7 @@ import DatasourceSrv from '../plugins/datasource_srv'; import { DataQuery } from '@grafana/ui/src/types/datasource'; import { PanelModel } from 'app/features/dashboard/state'; import { getDefaultCondition } from './getAlertingValidationMessage'; +import { CoreEvents } from 'app/types'; export class AlertTabCtrl { panel: PanelModel; @@ -58,11 +59,11 @@ export class AlertTabCtrl { // subscribe to graph threshold handle changes const thresholdChangedEventHandler = this.graphThresholdChanged.bind(this); - this.panelCtrl.events.on('threshold-changed', thresholdChangedEventHandler); + this.panelCtrl.events.on(CoreEvents.thresholdChanged, thresholdChangedEventHandler); // set panel alert edit mode this.$scope.$on('$destroy', () => { - this.panelCtrl.events.off('threshold-changed', thresholdChangedEventHandler); + this.panelCtrl.events.off(CoreEvents.thresholdChanged, thresholdChangedEventHandler); this.panelCtrl.editingThresholds = false; this.panelCtrl.render(); }); @@ -352,7 +353,7 @@ export class AlertTabCtrl { } delete() { - appEvents.emit('confirm-modal', { + appEvents.emit(CoreEvents.showConfirmModal, { title: 'Delete Alert', text: 'Are you sure you want to delete this alert rule?', text2: 'You need to save dashboard for the delete to take effect', @@ -402,7 +403,7 @@ export class AlertTabCtrl { } clearHistory() { - appEvents.emit('confirm-modal', { + appEvents.emit(CoreEvents.showConfirmModal, { title: 'Delete Alert History', text: 'Are you sure you want to remove all history & annotations for this alert?', icon: 'fa-trash', diff --git a/public/app/features/alerting/NotificationsEditCtrl.ts b/public/app/features/alerting/NotificationsEditCtrl.ts index d71c169e557..219918a073e 100644 --- a/public/app/features/alerting/NotificationsEditCtrl.ts +++ b/public/app/features/alerting/NotificationsEditCtrl.ts @@ -1,6 +1,7 @@ import _ from 'lodash'; import { appEvents, coreModule, NavModelSrv } from 'app/core/core'; import { BackendSrv } from 'app/core/services/backend_srv'; +import { AppEvents } from '@grafana/data'; export class AlertNotificationEditCtrl { theForm: any; @@ -78,23 +79,23 @@ export class AlertNotificationEditCtrl { .put(`/api/alert-notifications/${this.model.id}`, this.model) .then((res: any) => { this.model = res; - appEvents.emit('alert-success', ['Notification updated', '']); + appEvents.emit(AppEvents.alertSuccess, ['Notification updated']); }) .catch((err: any) => { if (err.data && err.data.error) { - appEvents.emit('alert-error', [err.data.error]); + appEvents.emit(AppEvents.alertError, [err.data.error]); } }); } else { this.backendSrv .post(`/api/alert-notifications`, this.model) .then((res: any) => { - appEvents.emit('alert-success', ['Notification created', '']); + appEvents.emit(AppEvents.alertSuccess, ['Notification created']); this.$location.path('alerting/notifications'); }) .catch((err: any) => { if (err.data && err.data.error) { - appEvents.emit('alert-error', [err.data.error]); + appEvents.emit(AppEvents.alertError, [err.data.error]); } }); } diff --git a/public/app/features/alerting/StateHistory.tsx b/public/app/features/alerting/StateHistory.tsx index 2a114ec00d1..973bb4f499a 100644 --- a/public/app/features/alerting/StateHistory.tsx +++ b/public/app/features/alerting/StateHistory.tsx @@ -3,6 +3,7 @@ import alertDef from './state/alertDef'; import { getBackendSrv } from '@grafana/runtime'; import { DashboardModel } from '../dashboard/state/DashboardModel'; import appEvents from '../../core/app_events'; +import { CoreEvents } from 'app/types'; interface Props { dashboard: DashboardModel; @@ -42,7 +43,7 @@ class StateHistory extends PureComponent { clearHistory = () => { const { dashboard, onRefresh, panelId } = this.props; - appEvents.emit('confirm-modal', { + appEvents.emit(CoreEvents.showConfirmModal, { title: 'Delete Alert History', text: 'Are you sure you want to remove all history & annotations for this alert?', icon: 'fa-trash', diff --git a/public/app/features/alerting/TestRuleResult.tsx b/public/app/features/alerting/TestRuleResult.tsx index 282f5e60303..e2c103e85ff 100644 --- a/public/app/features/alerting/TestRuleResult.tsx +++ b/public/app/features/alerting/TestRuleResult.tsx @@ -5,6 +5,7 @@ import appEvents from 'app/core/app_events'; import { CopyToClipboard } from 'app/core/components/CopyToClipboard/CopyToClipboard'; import { DashboardModel } from '../dashboard/state/DashboardModel'; import { getBackendSrv, BackendSrv } from '@grafana/runtime'; +import { AppEvents } from '@grafana/data'; export interface Props { panelId: number; @@ -55,7 +56,7 @@ export class TestRuleResult extends PureComponent { }; onClipboardSuccess = () => { - appEvents.emit('alert-success', ['Content copied to clipboard']); + appEvents.emit(AppEvents.alertSuccess, ['Content copied to clipboard']); }; onToggleExpand = () => { diff --git a/public/app/features/annotations/annotations_srv.ts b/public/app/features/annotations/annotations_srv.ts index 5df84b4626f..8f8d85742e8 100644 --- a/public/app/features/annotations/annotations_srv.ts +++ b/public/app/features/annotations/annotations_srv.ts @@ -11,11 +11,12 @@ import { dedupAnnotations } from './events_processing'; // Types import { DashboardModel } from '../dashboard/state/DashboardModel'; -import { AnnotationEvent } from '@grafana/data'; import DatasourceSrv from '../plugins/datasource_srv'; import { BackendSrv } from 'app/core/services/backend_srv'; import { TimeSrv } from '../dashboard/services/TimeSrv'; -import { DataSourceApi } from '@grafana/ui'; +import { DataSourceApi, PanelEvents } from '@grafana/ui'; +import { AnnotationEvent, AppEvents } from '@grafana/data'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; export class AnnotationsSrv { globalAnnotationsPromise: any; @@ -24,7 +25,7 @@ export class AnnotationsSrv { /** @ngInject */ constructor( - private $rootScope: any, + private $rootScope: GrafanaRootScope, private $q: IQService, private datasourceSrv: DatasourceSrv, private backendSrv: BackendSrv, @@ -35,7 +36,7 @@ export class AnnotationsSrv { // always clearPromiseCaches when loading new dashboard this.clearPromiseCaches(); // clear promises on refresh events - dashboard.on('refresh', this.clearPromiseCaches.bind(this)); + dashboard.on(PanelEvents.refresh, this.clearPromiseCaches.bind(this)); } clearPromiseCaches() { @@ -75,7 +76,7 @@ export class AnnotationsSrv { err.message = err.data.message; } console.log('AnnotationSrv.query error', err); - this.$rootScope.appEvent('alert-error', ['Annotation Query Failed', err.message || err]); + this.$rootScope.appEvent(AppEvents.alertError, ['Annotation Query Failed', err.message || err]); return []; }); } diff --git a/public/app/features/annotations/editor_ctrl.ts b/public/app/features/annotations/editor_ctrl.ts index 9c8ba7428dd..a38e2321792 100644 --- a/public/app/features/annotations/editor_ctrl.ts +++ b/public/app/features/annotations/editor_ctrl.ts @@ -5,6 +5,7 @@ import coreModule from 'app/core/core_module'; import { DashboardModel } from 'app/features/dashboard/state'; import DatasourceSrv from '../plugins/datasource_srv'; import appEvents from 'app/core/app_events'; +import { AppEvents } from '@grafana/data'; export class AnnotationsEditorCtrl { mode: any; @@ -103,7 +104,7 @@ export class AnnotationsEditorCtrl { add() { const sameName: any = _.find(this.annotations, { name: this.currentAnnotation.name }); if (sameName) { - appEvents.emit('alert-warning', ['Validation', 'Annotations with the same name already exists']); + appEvents.emit(AppEvents.alertWarning, ['Validation', 'Annotations with the same name already exists']); return; } this.annotations.push(this.currentAnnotation); diff --git a/public/app/features/annotations/specs/annotations_srv.test.ts b/public/app/features/annotations/specs/annotations_srv.test.ts index 21259849d32..bb8074a44a8 100644 --- a/public/app/features/annotations/specs/annotations_srv.test.ts +++ b/public/app/features/annotations/specs/annotations_srv.test.ts @@ -1,7 +1,7 @@ import { AnnotationsSrv } from '../annotations_srv'; describe('AnnotationsSrv', () => { - const $rootScope = { + const $rootScope: any = { onAppEvent: jest.fn(), }; diff --git a/public/app/features/api-keys/ApiKeysPage.tsx b/public/app/features/api-keys/ApiKeysPage.tsx index c35c8c0686f..1c9d8278e94 100644 --- a/public/app/features/api-keys/ApiKeysPage.tsx +++ b/public/app/features/api-keys/ApiKeysPage.tsx @@ -13,13 +13,13 @@ import config from 'app/core/config'; import appEvents from 'app/core/app_events'; import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import { DeleteButton, EventsWithValidation, FormLabel, Input, ValidationEvents } from '@grafana/ui'; -import { NavModel } from '@grafana/data'; +import { NavModel, dateTime, isDateTime } from '@grafana/data'; import { FilterInput } from 'app/core/components/FilterInput/FilterInput'; import { store } from 'app/store/store'; import kbn from 'app/core/utils/kbn'; // Utils -import { dateTime, isDateTime } from '@grafana/data'; +import { CoreEvents } from 'app/types'; import { getTimeZone } from 'app/features/profile/state/selectors'; const timeRangeValidationEvents: ValidationEvents = { @@ -106,7 +106,7 @@ export class ApiKeysPage extends PureComponent { const rootPath = window.location.origin + config.appSubUrl; const modalTemplate = ReactDOMServer.renderToString(); - appEvents.emit('show-modal', { + appEvents.emit(CoreEvents.showModal, { templateHtml: modalTemplate, }); }; diff --git a/public/app/features/dashboard/components/AdHocFilters/AdHocFiltersCtrl.ts b/public/app/features/dashboard/components/AdHocFilters/AdHocFiltersCtrl.ts index 22dccbcb8f4..84650a5f97c 100644 --- a/public/app/features/dashboard/components/AdHocFilters/AdHocFiltersCtrl.ts +++ b/public/app/features/dashboard/components/AdHocFilters/AdHocFiltersCtrl.ts @@ -4,6 +4,7 @@ import coreModule from 'app/core/core_module'; import { DashboardModel } from 'app/features/dashboard/state'; import DatasourceSrv from 'app/features/plugins/datasource_srv'; import { VariableSrv } from 'app/features/templating/all'; +import { CoreEvents } from 'app/types'; export class AdHocFiltersCtrl { segments: any; @@ -24,7 +25,7 @@ export class AdHocFiltersCtrl { value: '-- remove filter --', }); this.buildSegmentModel(); - this.dashboard.events.on('template-variable-value-updated', this.buildSegmentModel.bind(this), $scope); + this.dashboard.events.on(CoreEvents.templateVariableValueUpdated, this.buildSegmentModel.bind(this), $scope); } buildSegmentModel() { diff --git a/public/app/features/dashboard/components/DashExportModal/DashExportCtrl.ts b/public/app/features/dashboard/components/DashExportModal/DashExportCtrl.ts index 27484a3fdab..e3d2976f394 100644 --- a/public/app/features/dashboard/components/DashExportModal/DashExportCtrl.ts +++ b/public/app/features/dashboard/components/DashExportModal/DashExportCtrl.ts @@ -5,6 +5,8 @@ import coreModule from 'app/core/core_module'; import { DashboardExporter } from './DashboardExporter'; import { DashboardSrv } from '../../services/DashboardSrv'; import DatasourceSrv from 'app/features/plugins/datasource_srv'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; +import { CoreEvents } from 'app/types'; export class DashExportCtrl { dash: any; @@ -17,7 +19,7 @@ export class DashExportCtrl { private dashboardSrv: DashboardSrv, datasourceSrv: DatasourceSrv, private $scope: any, - private $rootScope: any + private $rootScope: GrafanaRootScope ) { this.exporter = new DashboardExporter(datasourceSrv); @@ -61,7 +63,7 @@ export class DashExportCtrl { enableCopy: true, }; - this.$rootScope.appEvent('show-modal', { + this.$rootScope.appEvent(CoreEvents.showModal, { src: 'public/app/partials/edit_json.html', model: model, }); diff --git a/public/app/features/dashboard/components/DashLinks/DashLinksContainerCtrl.ts b/public/app/features/dashboard/components/DashLinks/DashLinksContainerCtrl.ts index 0eb22fe0dce..216784e413b 100644 --- a/public/app/features/dashboard/components/DashLinks/DashLinksContainerCtrl.ts +++ b/public/app/features/dashboard/components/DashLinks/DashLinksContainerCtrl.ts @@ -4,6 +4,9 @@ import { iconMap } from './DashLinksEditorCtrl'; import { LinkSrv } from 'app/features/panel/panellinks/link_srv'; import { BackendSrv } from 'app/core/services/backend_srv'; import { DashboardSrv } from '../../services/DashboardSrv'; +import { PanelEvents } from '@grafana/ui'; +import { CoreEvents } from 'app/types'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; export type DashboardLink = { tags: any; target: string; keepTime: any; includeVars: any }; @@ -84,7 +87,7 @@ function dashLink($compile: any, $sanitize: any, linkSrv: LinkSrv) { } update(); - dashboard.events.on('refresh', update, scope); + dashboard.events.on(PanelEvents.refresh, update, scope); }, }; } @@ -93,7 +96,7 @@ export class DashLinksContainerCtrl { /** @ngInject */ constructor( $scope: any, - $rootScope: any, + $rootScope: GrafanaRootScope, $q: IQService, backendSrv: BackendSrv, dashboardSrv: DashboardSrv, @@ -184,7 +187,7 @@ export class DashLinksContainerCtrl { }; updateDashLinks(); - $rootScope.onAppEvent('dash-links-updated', updateDashLinks, $scope); + $rootScope.onAppEvent(CoreEvents.dashLinksUpdated, updateDashLinks, $scope); } } diff --git a/public/app/features/dashboard/components/DashLinks/DashLinksEditorCtrl.ts b/public/app/features/dashboard/components/DashLinks/DashLinksEditorCtrl.ts index 9169d2d8182..c5386e5906d 100644 --- a/public/app/features/dashboard/components/DashLinks/DashLinksEditorCtrl.ts +++ b/public/app/features/dashboard/components/DashLinks/DashLinksEditorCtrl.ts @@ -1,6 +1,8 @@ import angular from 'angular'; import _ from 'lodash'; import { DashboardModel } from 'app/features/dashboard/state'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; +import { CoreEvents } from 'app/types'; export let iconMap = { 'external link': 'fa-external-link', @@ -32,13 +34,13 @@ export class DashLinksEditorCtrl { }; /** @ngInject */ - constructor($scope: any, $rootScope: any) { + constructor($scope: any, $rootScope: GrafanaRootScope) { this.iconMap = iconMap; this.dashboard.links = this.dashboard.links || []; this.mode = 'list'; $scope.$on('$destroy', () => { - $rootScope.appEvent('dash-links-updated'); + $rootScope.appEvent(CoreEvents.dashLinksUpdated); }); } diff --git a/public/app/features/dashboard/components/DashNav/DashNav.tsx b/public/app/features/dashboard/components/DashNav/DashNav.tsx index f925afcddda..4fab96b0ff3 100644 --- a/public/app/features/dashboard/components/DashNav/DashNav.tsx +++ b/public/app/features/dashboard/components/DashNav/DashNav.tsx @@ -16,7 +16,7 @@ import { updateLocation } from 'app/core/actions'; // Types import { DashboardModel } from '../../state'; -import { StoreState } from 'app/types'; +import { StoreState, CoreEvents } from 'app/types'; export interface OwnProps { dashboard: DashboardModel; @@ -43,11 +43,11 @@ export class DashNav extends PureComponent { } onDahboardNameClick = () => { - appEvents.emit('show-dash-search'); + appEvents.emit(CoreEvents.showDashSearch); }; onFolderNameClick = () => { - appEvents.emit('show-dash-search', { + appEvents.emit(CoreEvents.showDashSearch, { query: 'folder:current', }); }; @@ -67,7 +67,7 @@ export class DashNav extends PureComponent { }; onToggleTVMode = () => { - appEvents.emit('toggle-kiosk-mode'); + appEvents.emit(CoreEvents.toggleKioskMode); }; onSave = () => { @@ -112,7 +112,7 @@ export class DashNav extends PureComponent { modalScope.tabIndex = 0; modalScope.dashboard = this.props.dashboard; - appEvents.emit('show-modal', { + appEvents.emit(CoreEvents.showModal, { src: 'public/app/features/dashboard/components/ShareModal/template.html', scope: modalScope, }); diff --git a/public/app/features/dashboard/components/DashNav/DashNavTimeControls.tsx b/public/app/features/dashboard/components/DashNav/DashNavTimeControls.tsx index bb186f11ece..4195937d930 100644 --- a/public/app/features/dashboard/components/DashNav/DashNavTimeControls.tsx +++ b/public/app/features/dashboard/components/DashNav/DashNavTimeControls.tsx @@ -4,7 +4,7 @@ import { dateMath } from '@grafana/data'; // Types import { DashboardModel } from '../../state'; -import { LocationState } from 'app/types'; +import { LocationState, CoreEvents } from 'app/types'; import { TimeRange, TimeOption, RawTimeRange } from '@grafana/data'; // State @@ -43,10 +43,10 @@ export class DashNavTimeControls extends Component { }; onMoveBack = () => { - this.$rootScope.appEvent('shift-time', -1); + this.$rootScope.appEvent(CoreEvents.shiftTime, -1); }; onMoveForward = () => { - this.$rootScope.appEvent('shift-time', 1); + this.$rootScope.appEvent(CoreEvents.shiftTime, 1); }; onChangeTimePicker = (timeRange: TimeRange) => { @@ -65,7 +65,7 @@ export class DashNavTimeControls extends Component { }; onZoom = () => { - this.$rootScope.appEvent('zoom-out', 2); + this.$rootScope.appEvent(CoreEvents.zoomOut, 2); }; setActiveTimeOption = (timeOptions: TimeOption[], rawTimeRange: RawTimeRange): TimeOption[] => { diff --git a/public/app/features/dashboard/components/DashboardRow/DashboardRow.tsx b/public/app/features/dashboard/components/DashboardRow/DashboardRow.tsx index 9b163537b73..db45300cf60 100644 --- a/public/app/features/dashboard/components/DashboardRow/DashboardRow.tsx +++ b/public/app/features/dashboard/components/DashboardRow/DashboardRow.tsx @@ -4,6 +4,7 @@ import { PanelModel } from '../../state/PanelModel'; import { DashboardModel } from '../../state/DashboardModel'; import templateSrv from 'app/features/templating/template_srv'; import appEvents from 'app/core/app_events'; +import { CoreEvents } from 'app/types'; export interface DashboardRowProps { panel: PanelModel; @@ -18,11 +19,11 @@ export class DashboardRow extends React.Component { collapsed: this.props.panel.collapsed, }; - this.props.dashboard.on('template-variable-value-updated', this.onVariableUpdated); + this.props.dashboard.on(CoreEvents.templateVariableValueUpdated, this.onVariableUpdated); } componentWillUnmount() { - this.props.dashboard.off('template-variable-value-updated', this.onVariableUpdated); + this.props.dashboard.off(CoreEvents.templateVariableValueUpdated, this.onVariableUpdated); } onVariableUpdated = () => { @@ -43,7 +44,7 @@ export class DashboardRow extends React.Component { }; onOpenSettings = () => { - appEvents.emit('show-modal', { + appEvents.emit(CoreEvents.showModal, { templateHtml: ``, modalClass: 'modal--narrow', model: { @@ -54,7 +55,7 @@ export class DashboardRow extends React.Component { }; onDelete = () => { - appEvents.emit('confirm-modal', { + appEvents.emit(CoreEvents.showConfirmModal, { title: 'Delete Row', text: 'Are you sure you want to remove this row and all its panels?', altActionText: 'Delete row only', diff --git a/public/app/features/dashboard/components/DashboardSettings/SettingsCtrl.ts b/public/app/features/dashboard/components/DashboardSettings/SettingsCtrl.ts index 993b398d18b..17047dbcf54 100644 --- a/public/app/features/dashboard/components/DashboardSettings/SettingsCtrl.ts +++ b/public/app/features/dashboard/components/DashboardSettings/SettingsCtrl.ts @@ -6,6 +6,9 @@ import angular, { ILocationService } from 'angular'; import config from 'app/core/config'; import { BackendSrv } from 'app/core/services/backend_srv'; import { DashboardSrv } from '../../services/DashboardSrv'; +import { CoreEvents } from 'app/types'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; +import { AppEvents } from '@grafana/data'; export class SettingsCtrl { dashboard: DashboardModel; @@ -24,7 +27,7 @@ export class SettingsCtrl { private $scope: any, private $route: any, private $location: ILocationService, - private $rootScope: any, + private $rootScope: GrafanaRootScope, private backendSrv: BackendSrv, private dashboardSrv: DashboardSrv ) { @@ -35,7 +38,7 @@ export class SettingsCtrl { this.$scope.$on('$destroy', () => { this.dashboard.updateSubmenuVisibility(); setTimeout(() => { - this.$rootScope.appEvent('dash-scroll', { restore: true }); + this.$rootScope.appEvent(CoreEvents.dashScroll, { restore: true }); this.dashboard.startRefresh(); }); }); @@ -47,9 +50,9 @@ export class SettingsCtrl { this.buildSectionList(); this.onRouteUpdated(); - this.$rootScope.onAppEvent('$routeUpdate', this.onRouteUpdated.bind(this), $scope); - this.$rootScope.appEvent('dash-scroll', { animate: false, pos: 0 }); - this.$rootScope.onAppEvent('dashboard-saved', this.onPostSave.bind(this), $scope); + this.$rootScope.onAppEvent(CoreEvents.routeUpdated, this.onRouteUpdated.bind(this), $scope); + this.$rootScope.appEvent(CoreEvents.dashScroll, { animate: false, pos: 0 }); + this.$rootScope.onAppEvent(CoreEvents.dashboardSaved, this.onPostSave.bind(this), $scope); } buildSectionList() { @@ -185,7 +188,7 @@ export class SettingsCtrl { let text2 = this.dashboard.title; if (this.dashboard.meta.provisioned) { - appEvents.emit('confirm-modal', { + appEvents.emit(CoreEvents.showConfirmModal, { title: 'Cannot delete provisioned dashboard', text: ` This dashboard is managed by Grafanas provisioning and cannot be deleted. Remove the dashboard from the @@ -213,7 +216,7 @@ export class SettingsCtrl { text2 = `This dashboard contains ${alerts} alerts. Deleting this dashboard will also delete those alerts`; } - appEvents.emit('confirm-modal', { + appEvents.emit(CoreEvents.showConfirmModal, { title: 'Delete', text: 'Do you want to delete this dashboard?', text2: text2, @@ -229,7 +232,7 @@ export class SettingsCtrl { deleteDashboardConfirmed() { this.backendSrv.deleteDashboard(this.dashboard.uid, false).then(() => { - appEvents.emit('alert-success', ['Dashboard Deleted', this.dashboard.title + ' has been deleted']); + appEvents.emit(AppEvents.alertSuccess, ['Dashboard Deleted', this.dashboard.title + ' has been deleted']); this.$location.url('/'); }); } diff --git a/public/app/features/dashboard/components/ExportDataModal/ExportDataModalCtrl.ts b/public/app/features/dashboard/components/ExportDataModal/ExportDataModalCtrl.ts index 13d4279a00c..8e6515be239 100644 --- a/public/app/features/dashboard/components/ExportDataModal/ExportDataModalCtrl.ts +++ b/public/app/features/dashboard/components/ExportDataModal/ExportDataModalCtrl.ts @@ -2,6 +2,7 @@ import angular from 'angular'; import * as fileExport from 'app/core/utils/file_export'; import appEvents from 'app/core/app_events'; import { DashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; +import { CoreEvents } from 'app/types'; export class ExportDataModalCtrl { private data: any; @@ -34,7 +35,7 @@ export class ExportDataModalCtrl { } dismiss() { - appEvents.emit('hide-modal'); + appEvents.emit(CoreEvents.hideModal); } } diff --git a/public/app/features/dashboard/components/FolderPicker/FolderPickerCtrl.ts b/public/app/features/dashboard/components/FolderPicker/FolderPickerCtrl.ts index 12af5588ddc..ef4d7b192dd 100644 --- a/public/app/features/dashboard/components/FolderPicker/FolderPickerCtrl.ts +++ b/public/app/features/dashboard/components/FolderPicker/FolderPickerCtrl.ts @@ -4,6 +4,7 @@ import appEvents from 'app/core/app_events'; import { BackendSrv } from 'app/core/services/backend_srv'; import { ValidationSrv } from 'app/features/manage-dashboards'; import { ContextSrv } from 'app/core/services/context_srv'; +import { AppEvents } from '@grafana/data'; export class FolderPickerCtrl { initialTitle: string; @@ -105,7 +106,7 @@ export class FolderPickerCtrl { } return this.backendSrv.createFolder({ title: this.newFolderName }).then((result: { title: string; id: number }) => { - appEvents.emit('alert-success', ['Folder Created', 'OK']); + appEvents.emit(AppEvents.alertSuccess, ['Folder Created', 'OK']); this.closeCreateFolder(); this.folder = { text: result.title, value: result.id }; diff --git a/public/app/features/dashboard/components/ShareModal/ShareModalCtrl.test.ts b/public/app/features/dashboard/components/ShareModal/ShareModalCtrl.test.ts index 199b7445fd3..f30963bf31f 100644 --- a/public/app/features/dashboard/components/ShareModal/ShareModalCtrl.test.ts +++ b/public/app/features/dashboard/components/ShareModal/ShareModalCtrl.test.ts @@ -45,7 +45,7 @@ describe('ShareModalCtrl', () => { // @ts-ignore ctx.ctrl = new ShareModalCtrl( ctx.scope, - {}, + {} as any, ctx.$location, {}, ctx.timeSrv, diff --git a/public/app/features/dashboard/components/ShareModal/ShareModalCtrl.ts b/public/app/features/dashboard/components/ShareModal/ShareModalCtrl.ts index 4d330885e71..6749c66632c 100644 --- a/public/app/features/dashboard/components/ShareModal/ShareModalCtrl.ts +++ b/public/app/features/dashboard/components/ShareModal/ShareModalCtrl.ts @@ -5,11 +5,12 @@ import { appendQueryToUrl, toUrlParams } from 'app/core/utils/url'; import { TimeSrv } from '../../services/TimeSrv'; import { TemplateSrv } from 'app/features/templating/template_srv'; import { LinkSrv } from 'app/features/panel/panellinks/link_srv'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; /** @ngInject */ export function ShareModalCtrl( $scope: any, - $rootScope: any, + $rootScope: GrafanaRootScope, $location: ILocationService, $timeout: any, timeSrv: TimeSrv, diff --git a/public/app/features/dashboard/components/ShareModal/ShareSnapshotCtrl.ts b/public/app/features/dashboard/components/ShareModal/ShareSnapshotCtrl.ts index 9f0e58d3548..7d81d85eaf9 100644 --- a/public/app/features/dashboard/components/ShareModal/ShareSnapshotCtrl.ts +++ b/public/app/features/dashboard/components/ShareModal/ShareSnapshotCtrl.ts @@ -4,12 +4,13 @@ import { BackendSrv } from 'app/core/services/backend_srv'; import { TimeSrv } from '../../services/TimeSrv'; import { DashboardModel } from '../../state/DashboardModel'; import { PanelModel } from '../../state/PanelModel'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; export class ShareSnapshotCtrl { /** @ngInject */ constructor( $scope: any, - $rootScope: any, + $rootScope: GrafanaRootScope, $location: ILocationService, backendSrv: BackendSrv, $timeout: any, diff --git a/public/app/features/dashboard/components/VersionHistory/HistoryListCtrl.test.ts b/public/app/features/dashboard/components/VersionHistory/HistoryListCtrl.test.ts index 5d0fa874802..6bb2e5e3340 100644 --- a/public/app/features/dashboard/components/VersionHistory/HistoryListCtrl.test.ts +++ b/public/app/features/dashboard/components/VersionHistory/HistoryListCtrl.test.ts @@ -3,6 +3,7 @@ import { HistoryListCtrl } from './HistoryListCtrl'; import { versions, compare, restore } from './__mocks__/history'; // @ts-ignore import $q from 'q'; +import { CoreEvents } from 'app/types'; describe('HistoryListCtrl', () => { const RESTORE_ID = 4; @@ -118,9 +119,9 @@ describe('HistoryListCtrl', () => { historyListCtrl.resetFromSource = jest.fn(); }); - it('should listen for the `dashboard-saved` appEvent', () => { + it('should listen for the `dashboardSaved` appEvent', () => { expect($rootScope.onAppEvent).toHaveBeenCalledTimes(1); - expect($rootScope.onAppEvent.mock.calls[0][0]).toBe('dashboard-saved'); + expect($rootScope.onAppEvent.mock.calls[0][0]).toBe(CoreEvents.dashboardSaved); }); it('should call `onDashboardSaved` when the appEvent is received', () => { @@ -292,7 +293,7 @@ describe('HistoryListCtrl', () => { it('should display a modal allowing the user to restore or cancel', () => { expect($rootScope.appEvent).toHaveBeenCalledTimes(1); - expect($rootScope.appEvent.mock.calls[0][0]).toBe('confirm-modal'); + expect($rootScope.appEvent.mock.calls[0][0]).toBe(CoreEvents.showConfirmModal); }); describe('and restore fails to fetch', () => { diff --git a/public/app/features/dashboard/components/VersionHistory/HistoryListCtrl.ts b/public/app/features/dashboard/components/VersionHistory/HistoryListCtrl.ts index 10574a48ddc..891e8ba9f86 100644 --- a/public/app/features/dashboard/components/VersionHistory/HistoryListCtrl.ts +++ b/public/app/features/dashboard/components/VersionHistory/HistoryListCtrl.ts @@ -4,7 +4,9 @@ import angular, { ILocationService, IQService } from 'angular'; import locationUtil from 'app/core/utils/location_util'; import { DashboardModel } from '../../state/DashboardModel'; import { HistoryListOpts, RevisionsModel, CalculateDiffOptions, HistorySrv } from './HistorySrv'; -import { dateTime, toUtc, DateTimeInput } from '@grafana/data'; +import { dateTime, toUtc, DateTimeInput, AppEvents } from '@grafana/data'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; +import { CoreEvents } from 'app/types'; export class HistoryListCtrl { appending: boolean; @@ -25,7 +27,7 @@ export class HistoryListCtrl { /** @ngInject */ constructor( private $route: any, - private $rootScope: any, + private $rootScope: GrafanaRootScope, private $location: ILocationService, private $q: IQService, private historySrv: HistorySrv, @@ -40,7 +42,7 @@ export class HistoryListCtrl { this.start = 0; this.canCompare = false; - this.$rootScope.onAppEvent('dashboard-saved', this.onDashboardSaved.bind(this), $scope); + this.$rootScope.onAppEvent(CoreEvents.dashboardSaved, this.onDashboardSaved.bind(this), $scope); this.resetFromSource(); } @@ -56,7 +58,7 @@ export class HistoryListCtrl { } dismiss() { - this.$rootScope.appEvent('hide-dash-editor'); + this.$rootScope.appEvent(CoreEvents.hideDashEditor); } addToLog() { @@ -172,7 +174,7 @@ export class HistoryListCtrl { } restore(version: number) { - this.$rootScope.appEvent('confirm-modal', { + this.$rootScope.appEvent(CoreEvents.showConfirmModal, { title: 'Restore version', text: '', text2: `Are you sure you want to restore the dashboard to version ${version}? All unsaved changes will be lost.`, @@ -189,7 +191,7 @@ export class HistoryListCtrl { .then((response: any) => { this.$location.url(locationUtil.stripBaseFromUrl(response.url)).replace(); this.$route.reload(); - this.$rootScope.appEvent('alert-success', ['Dashboard restored', 'Restored from version ' + version]); + this.$rootScope.appEvent(AppEvents.alertSuccess, ['Dashboard restored', 'Restored from version ' + version]); }) .catch(() => { this.mode = 'list'; diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx index c1c089f7a63..6ef8ce489e3 100644 --- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx @@ -10,6 +10,9 @@ import sizeMe from 'react-sizeme'; import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, GRID_COLUMN_COUNT } from 'app/core/constants'; import { DashboardPanel } from './DashboardPanel'; import { DashboardModel, PanelModel } from '../state'; +import { CoreEvents } from 'app/types'; +import { PanelEvents } from '@grafana/ui'; +import { panelAdded, panelRemoved } from '../state/PanelModel'; let lastGridWidth = 1200; let ignoreNextWidthChange = false; @@ -93,22 +96,22 @@ export class DashboardGrid extends PureComponent { componentDidMount() { const { dashboard } = this.props; - dashboard.on('panel-added', this.triggerForceUpdate); - dashboard.on('panel-removed', this.triggerForceUpdate); - dashboard.on('repeats-processed', this.triggerForceUpdate); - dashboard.on('view-mode-changed', this.onViewModeChanged); - dashboard.on('row-collapsed', this.triggerForceUpdate); - dashboard.on('row-expanded', this.triggerForceUpdate); + dashboard.on(panelAdded, this.triggerForceUpdate); + dashboard.on(panelRemoved, this.triggerForceUpdate); + dashboard.on(CoreEvents.repeatsProcessed, this.triggerForceUpdate); + dashboard.on(PanelEvents.viewModeChanged, this.onViewModeChanged); + dashboard.on(CoreEvents.rowCollapsed, this.triggerForceUpdate); + dashboard.on(CoreEvents.rowExpanded, this.triggerForceUpdate); } componentWillUnmount() { const { dashboard } = this.props; - dashboard.off('panel-added', this.triggerForceUpdate); - dashboard.off('panel-removed', this.triggerForceUpdate); - dashboard.off('repeats-processed', this.triggerForceUpdate); - dashboard.off('view-mode-changed', this.onViewModeChanged); - dashboard.off('row-collapsed', this.triggerForceUpdate); - dashboard.off('row-expanded', this.triggerForceUpdate); + dashboard.off(panelAdded, this.triggerForceUpdate); + dashboard.off(panelRemoved, this.triggerForceUpdate); + dashboard.off(CoreEvents.repeatsProcessed, this.triggerForceUpdate); + dashboard.off(PanelEvents.viewModeChanged, this.onViewModeChanged); + dashboard.off(CoreEvents.rowCollapsed, this.triggerForceUpdate); + dashboard.off(CoreEvents.rowExpanded, this.triggerForceUpdate); } buildLayout() { diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index 183240e7a56..65294d95258 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -14,7 +14,8 @@ import templateSrv from 'app/features/templating/template_srv'; import config from 'app/core/config'; // Types import { DashboardModel, PanelModel } from '../state'; -import { LoadingState, ScopedVars, AbsoluteTimeRange, toUtc, toDataFrameDTO, DefaultTimeRange } from '@grafana/data'; +import { LoadingState, ScopedVars, AbsoluteTimeRange, DefaultTimeRange, toUtc, toDataFrameDTO } from '@grafana/data'; +import { PanelEvents } from '@grafana/ui'; const DEFAULT_PLUGIN_ERROR = 'Error in plugin'; @@ -59,8 +60,8 @@ export class PanelChrome extends PureComponent { componentDidMount() { const { panel, dashboard } = this.props; - panel.events.on('refresh', this.onRefresh); - panel.events.on('render', this.onRender); + panel.events.on(PanelEvents.refresh, this.onRefresh); + panel.events.on(PanelEvents.render, this.onRender); dashboard.panelInitialized(this.props.panel); // Move snapshot data into the query response @@ -79,8 +80,7 @@ export class PanelChrome extends PureComponent { } componentWillUnmount() { - this.props.panel.events.off('refresh', this.onRefresh); - + this.props.panel.events.off(PanelEvents.refresh, this.onRefresh); if (this.querySubscription) { this.querySubscription.unsubscribe(); this.querySubscription = null; diff --git a/public/app/features/dashboard/panel_editor/QueryEditorRow.tsx b/public/app/features/dashboard/panel_editor/QueryEditorRow.tsx index 36df01ad5bc..ea1ffcd8f6d 100644 --- a/public/app/features/dashboard/panel_editor/QueryEditorRow.tsx +++ b/public/app/features/dashboard/panel_editor/QueryEditorRow.tsx @@ -9,7 +9,7 @@ import { Emitter } from 'app/core/utils/emitter'; import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; // Types import { PanelModel } from '../state/PanelModel'; -import { DataQuery, DataSourceApi, PanelData, DataQueryRequest, ErrorBoundaryAlert } from '@grafana/ui'; +import { DataQuery, DataSourceApi, PanelData, DataQueryRequest, ErrorBoundaryAlert, PanelEvents } from '@grafana/ui'; import { TimeRange, LoadingState, toLegacyResponseData } from '@grafana/data'; import { DashboardModel } from '../state/DashboardModel'; @@ -273,9 +273,9 @@ function notifyAngularQueryEditorsOfData(panel: PanelModel, data: PanelData, edi if (data.state === LoadingState.Done) { const legacy = data.series.map(v => toLegacyResponseData(v)); - panel.events.emit('data-received', legacy); + panel.events.emit(PanelEvents.dataReceived, legacy); } else if (data.state === LoadingState.Error) { - panel.events.emit('data-error', data.error); + panel.events.emit(PanelEvents.dataError, data.error); } // Some query controllers listen to data error events and need a digest diff --git a/public/app/features/dashboard/panel_editor/QueryInspector.tsx b/public/app/features/dashboard/panel_editor/QueryInspector.tsx index 9aff948e6b0..fa0a14abedd 100644 --- a/public/app/features/dashboard/panel_editor/QueryInspector.tsx +++ b/public/app/features/dashboard/panel_editor/QueryInspector.tsx @@ -1,7 +1,9 @@ import React, { PureComponent } from 'react'; import appEvents from 'app/core/app_events'; import { CopyToClipboard } from 'app/core/components/CopyToClipboard/CopyToClipboard'; -import { LoadingPlaceholder, JSONFormatter } from '@grafana/ui'; +import { LoadingPlaceholder, JSONFormatter, PanelEvents } from '@grafana/ui'; +import { CoreEvents } from 'app/types'; +import { AppEvents } from '@grafana/data'; interface DsQuery { isLoading: boolean; @@ -39,20 +41,20 @@ export class QueryInspector extends PureComponent { componentDidMount() { const { panel } = this.props; - appEvents.on('ds-request-response', this.onDataSourceResponse); - appEvents.on('ds-request-error', this.onRequestError); + appEvents.on(CoreEvents.dsRequestResponse, this.onDataSourceResponse); + appEvents.on(CoreEvents.dsRequestError, this.onRequestError); - panel.events.on('refresh', this.onPanelRefresh); + panel.events.on(PanelEvents.refresh, this.onPanelRefresh); panel.refresh(); } componentWillUnmount() { const { panel } = this.props; - appEvents.off('ds-request-response', this.onDataSourceResponse); - appEvents.on('ds-request-error', this.onRequestError); + appEvents.off(CoreEvents.dsRequestResponse, this.onDataSourceResponse); + appEvents.on(CoreEvents.dsRequestError, this.onRequestError); - panel.events.off('refresh', this.onPanelRefresh); + panel.events.off(PanelEvents.refresh, this.onPanelRefresh); } handleMocking(response: any) { @@ -61,7 +63,7 @@ export class QueryInspector extends PureComponent { try { mockedData = JSON.parse(mockedResponse); } catch (err) { - appEvents.emit('alert-error', ['R: Failed to parse mocked response']); + appEvents.emit(AppEvents.alertError, ['R: Failed to parse mocked response']); return; } @@ -134,7 +136,7 @@ export class QueryInspector extends PureComponent { }; onClipboardSuccess = () => { - appEvents.emit('alert-success', ['Content copied to clipboard']); + appEvents.emit(AppEvents.alertSuccess, ['Content copied to clipboard']); }; onToggleExpand = () => { diff --git a/public/app/features/dashboard/services/ChangeTracker.test.ts b/public/app/features/dashboard/services/ChangeTracker.test.ts index 7448411a1b0..b81c7d3c971 100644 --- a/public/app/features/dashboard/services/ChangeTracker.test.ts +++ b/public/app/features/dashboard/services/ChangeTracker.test.ts @@ -10,7 +10,7 @@ jest.mock('app/core/services/context_srv', () => ({ })); describe('ChangeTracker', () => { - let rootScope; + let rootScope: any; let location; const timeout = () => {}; let tracker: ChangeTracker; @@ -57,7 +57,7 @@ describe('ChangeTracker', () => { path: jest.fn(), }; - tracker = new ChangeTracker(dash, scope, undefined, location as any, window, timeout, contextSrv, rootScope); + tracker = new ChangeTracker(dash, scope as any, undefined, location as any, window, timeout, contextSrv, rootScope); }); it('No changes should not have changes', () => { diff --git a/public/app/features/dashboard/services/ChangeTracker.ts b/public/app/features/dashboard/services/ChangeTracker.ts index 8d4fd9a8ae1..75c359622ff 100644 --- a/public/app/features/dashboard/services/ChangeTracker.ts +++ b/public/app/features/dashboard/services/ChangeTracker.ts @@ -1,7 +1,9 @@ -import angular, { ILocationService } from 'angular'; +import angular, { ILocationService, IRootScopeService } from 'angular'; import _ from 'lodash'; import { DashboardModel } from '../state/DashboardModel'; import { ContextSrv } from 'app/core/services/context_srv'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; +import { CoreEvents, AppEventConsumer } from 'app/types'; export class ChangeTracker { current: any; @@ -14,13 +16,13 @@ export class ChangeTracker { /** @ngInject */ constructor( dashboard: DashboardModel, - scope: any, + scope: IRootScopeService & AppEventConsumer, originalCopyDelay: any, private $location: ILocationService, $window: any, private $timeout: any, private contextSrv: ContextSrv, - private $rootScope: any + private $rootScope: GrafanaRootScope ) { this.$location = $location; this.$window = $window; @@ -30,7 +32,7 @@ export class ChangeTracker { this.scope = scope; // register events - scope.onAppEvent('dashboard-saved', () => { + scope.onAppEvent(CoreEvents.dashboardSaved, () => { this.original = this.current.getSaveModelClone(); this.originalPath = $location.path(); }); @@ -161,7 +163,7 @@ export class ChangeTracker { } open_modal() { - this.$rootScope.appEvent('show-modal', { + this.$rootScope.appEvent(CoreEvents.showModal, { templateHtml: '', modalClass: 'modal--narrow confirm-modal', }); @@ -176,7 +178,7 @@ export class ChangeTracker { }); }); - this.$rootScope.appEvent('save-dashboard'); + this.$rootScope.appEvent(CoreEvents.saveDashboard); } gotoNext() { diff --git a/public/app/features/dashboard/services/DashboardLoaderSrv.ts b/public/app/features/dashboard/services/DashboardLoaderSrv.ts index e6a9517bc2a..c982faea136 100644 --- a/public/app/features/dashboard/services/DashboardLoaderSrv.ts +++ b/public/app/features/dashboard/services/DashboardLoaderSrv.ts @@ -4,12 +4,13 @@ import moment from 'moment'; import _ from 'lodash'; import $ from 'jquery'; import kbn from 'app/core/utils/kbn'; -import { dateMath } from '@grafana/data'; +import { dateMath, AppEvents } from '@grafana/data'; import impressionSrv from 'app/core/services/impression_srv'; import { BackendSrv } from 'app/core/services/backend_srv'; import { DashboardSrv } from './DashboardSrv'; import DatasourceSrv from 'app/features/plugins/datasource_srv'; import { UrlQueryValue } from '@grafana/runtime'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; export class DashboardLoaderSrv { /** @ngInject */ @@ -22,7 +23,7 @@ export class DashboardLoaderSrv { private $timeout: any, contextSrv: any, private $routeParams: any, - private $rootScope: any + private $rootScope: GrafanaRootScope ) {} _dashboardLoadFailed(title: string, snapshot?: boolean) { @@ -54,7 +55,7 @@ export class DashboardLoaderSrv { .getDashboardByUid(uid) .then((result: any) => { if (result.meta.isFolder) { - this.$rootScope.appEvent('alert-error', ['Dashboard not found']); + this.$rootScope.appEvent(AppEvents.alertError, ['Dashboard not found']); throw new Error('Dashboard not found'); } return result; @@ -94,7 +95,7 @@ export class DashboardLoaderSrv { }, (err: any) => { console.log('Script dashboard error ' + err); - this.$rootScope.appEvent('alert-error', [ + this.$rootScope.appEvent(AppEvents.alertError, [ 'Script Error', 'Please make sure it exists and returns a valid dashboard', ]); diff --git a/public/app/features/dashboard/services/DashboardSrv.ts b/public/app/features/dashboard/services/DashboardSrv.ts index f44327eeeb8..faf4efefcd5 100644 --- a/public/app/features/dashboard/services/DashboardSrv.ts +++ b/public/app/features/dashboard/services/DashboardSrv.ts @@ -3,9 +3,12 @@ import { appEvents } from 'app/core/app_events'; import locationUtil from 'app/core/utils/location_util'; import { DashboardModel } from '../state/DashboardModel'; import { removePanel } from '../utils/panel'; -import { DashboardMeta } from 'app/types'; +import { DashboardMeta, CoreEvents } from 'app/types'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; import { BackendSrv } from 'app/core/services/backend_srv'; import { ILocationService } from 'angular'; +import { AppEvents } from '@grafana/data'; +import { PanelEvents } from '@grafana/ui'; interface DashboardSaveOptions { folderId?: number; @@ -18,10 +21,14 @@ export class DashboardSrv { dashboard: DashboardModel; /** @ngInject */ - constructor(private backendSrv: BackendSrv, private $rootScope: any, private $location: ILocationService) { - appEvents.on('save-dashboard', this.saveDashboard.bind(this), $rootScope); - appEvents.on('panel-change-view', this.onPanelChangeView); - appEvents.on('remove-panel', this.onRemovePanel); + constructor( + private backendSrv: BackendSrv, + private $rootScope: GrafanaRootScope, + private $location: ILocationService + ) { + appEvents.on(CoreEvents.saveDashboard, this.saveDashboard.bind(this), $rootScope); + appEvents.on(PanelEvents.panelChangeView, this.onPanelChangeView); + appEvents.on(CoreEvents.removePanel, this.onRemovePanel); // Export to react setDashboardSrv(this); @@ -96,7 +103,7 @@ export class DashboardSrv { if (err.data && err.data.status === 'version-mismatch') { err.isHandled = true; - this.$rootScope.appEvent('confirm-modal', { + this.$rootScope.appEvent(CoreEvents.showConfirmModal, { title: 'Conflict', text: 'Someone else has updated this dashboard.', text2: 'Would you still like to save this dashboard?', @@ -111,7 +118,7 @@ export class DashboardSrv { if (err.data && err.data.status === 'name-exists') { err.isHandled = true; - this.$rootScope.appEvent('confirm-modal', { + this.$rootScope.appEvent(CoreEvents.showConfirmModal, { title: 'Conflict', text: 'A dashboard with the same name in selected folder already exists.', text2: 'Would you still like to save this dashboard?', @@ -126,7 +133,7 @@ export class DashboardSrv { if (err.data && err.data.status === 'plugin-dashboard') { err.isHandled = true; - this.$rootScope.appEvent('confirm-modal', { + this.$rootScope.appEvent(CoreEvents.showConfirmModal, { title: 'Plugin Dashboard', text: err.data.message, text2: 'Your changes will be lost when you update the plugin. Use Save As to create custom version.', @@ -147,8 +154,8 @@ export class DashboardSrv { this.dashboard.version = data.version; // important that these happen before location redirect below - this.$rootScope.appEvent('dashboard-saved', this.dashboard); - this.$rootScope.appEvent('alert-success', ['Dashboard saved']); + this.$rootScope.appEvent(CoreEvents.dashboardSaved, this.dashboard); + this.$rootScope.appEvent(AppEvents.alertSuccess, ['Dashboard saved']); const newUrl = locationUtil.stripBaseFromUrl(data.url); const currentPath = this.$location.path(); @@ -201,20 +208,20 @@ export class DashboardSrv { } showDashboardProvisionedModal() { - this.$rootScope.appEvent('show-modal', { + this.$rootScope.appEvent(CoreEvents.showModal, { templateHtml: '', }); } showSaveAsModal() { - this.$rootScope.appEvent('show-modal', { + this.$rootScope.appEvent(CoreEvents.showModal, { templateHtml: '', modalClass: 'modal--narrow', }); } showSaveModal() { - this.$rootScope.appEvent('show-modal', { + this.$rootScope.appEvent(CoreEvents.showModal, { templateHtml: '', modalClass: 'modal--narrow', }); diff --git a/public/app/features/dashboard/services/TimeSrv.ts b/public/app/features/dashboard/services/TimeSrv.ts index c9854816920..c4c4e385a9d 100644 --- a/public/app/features/dashboard/services/TimeSrv.ts +++ b/public/app/features/dashboard/services/TimeSrv.ts @@ -17,6 +17,7 @@ import { import { ITimeoutService, ILocationService } from 'angular'; import { ContextSrv } from 'app/core/services/context_srv'; import { DashboardModel } from '../state/DashboardModel'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; import { getZoomedTimeRange, getShiftedTimeRange } from 'app/core/utils/timePicker'; export class TimeSrv { @@ -30,7 +31,7 @@ export class TimeSrv { /** @ngInject */ constructor( - $rootScope: any, + $rootScope: GrafanaRootScope, private $timeout: ITimeoutService, private $location: ILocationService, private timer: any, diff --git a/public/app/features/dashboard/services/UnsavedChangesSrv.ts b/public/app/features/dashboard/services/UnsavedChangesSrv.ts index 69a11f5be78..60f215e2dbd 100644 --- a/public/app/features/dashboard/services/UnsavedChangesSrv.ts +++ b/public/app/features/dashboard/services/UnsavedChangesSrv.ts @@ -2,11 +2,12 @@ import angular, { IQService, ILocationService } from 'angular'; import { ChangeTracker } from './ChangeTracker'; import { ContextSrv } from 'app/core/services/context_srv'; import { DashboardSrv } from './DashboardSrv'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; /** @ngInject */ export function unsavedChangesSrv( this: any, - $rootScope: any, + $rootScope: GrafanaRootScope, $q: IQService, $location: ILocationService, $timeout: any, diff --git a/public/app/features/dashboard/state/DashboardModel.ts b/public/app/features/dashboard/state/DashboardModel.ts index cc66317f3e6..85b804cc31b 100644 --- a/public/app/features/dashboard/state/DashboardModel.ts +++ b/public/app/features/dashboard/state/DashboardModel.ts @@ -11,11 +11,12 @@ import { contextSrv } from 'app/core/services/context_srv'; import sortByKeys from 'app/core/utils/sort_by_keys'; // Types -import { PanelModel, GridPos } from './PanelModel'; +import { PanelModel, GridPos, panelAdded, panelRemoved } from './PanelModel'; import { DashboardMigrator } from './DashboardMigrator'; -import { TimeRange, TimeZone } from '@grafana/data'; +import { TimeRange, TimeZone, AppEvent } from '@grafana/data'; import { UrlQueryValue } from '@grafana/runtime'; -import { KIOSK_MODE_TV, DashboardMeta } from 'app/types'; +import { PanelEvents } from '@grafana/ui'; +import { KIOSK_MODE_TV, DashboardMeta, CoreEvents } from 'app/types'; import { toUtc, DateTimeInput, dateTime, isDateTime } from '@grafana/data'; export interface CloneOptions { @@ -215,15 +216,15 @@ export class DashboardModel { panel.setViewMode(fullscreen, this.meta.isEditing); - this.events.emit('view-mode-changed', panel); + this.events.emit(PanelEvents.viewModeChanged, panel); } timeRangeUpdated(timeRange: TimeRange) { - this.events.emit('time-range-updated', timeRange); + this.events.emit(CoreEvents.timeRangeUpdated, timeRange); } startRefresh() { - this.events.emit('refresh'); + this.events.emit(PanelEvents.refresh); for (const panel of this.panels) { if (!this.otherPanelInFullscreen(panel)) { @@ -233,7 +234,7 @@ export class DashboardModel { } render() { - this.events.emit('render'); + this.events.emit(PanelEvents.render); for (const panel of this.panels) { panel.render(); @@ -306,7 +307,7 @@ export class DashboardModel { this.sortPanelsByGridPos(); - this.events.emit('panel-added', panel); + this.events.emit(panelAdded, panel); } sortPanelsByGridPos() { @@ -343,7 +344,7 @@ export class DashboardModel { _.pull(this.panels, ...panelsToRemove); panelsToRemove.map(p => p.destroy()); this.sortPanelsByGridPos(); - this.events.emit('repeats-processed'); + this.events.emit(CoreEvents.repeatsProcessed); } processRepeats() { @@ -363,7 +364,7 @@ export class DashboardModel { } this.sortPanelsByGridPos(); - this.events.emit('repeats-processed'); + this.events.emit(CoreEvents.repeatsProcessed); } cleanUpRowRepeats(rowPanels: PanelModel[]) { @@ -596,7 +597,7 @@ export class DashboardModel { removePanel(panel: PanelModel) { const index = _.indexOf(this.panels, panel); this.panels.splice(index, 1); - this.events.emit('panel-removed', panel); + this.events.emit(panelRemoved, panel); } removeRow(row: PanelModel, removePanels: boolean) { @@ -761,7 +762,7 @@ export class DashboardModel { this.sortPanelsByGridPos(); // emit change event - this.events.emit('row-expanded'); + this.events.emit(CoreEvents.rowExpanded); return; } @@ -774,7 +775,7 @@ export class DashboardModel { row.collapsed = true; // emit change event - this.events.emit('row-collapsed'); + this.events.emit(CoreEvents.rowCollapsed); } /** @@ -798,12 +799,12 @@ export class DashboardModel { return rowPanels; } - on(eventName: string, callback: (payload?: any) => void) { - this.events.on(eventName, callback); + on(event: AppEvent, callback: (payload?: T) => void) { + this.events.on(event, callback); } - off(eventName: string, callback?: (payload?: any) => void) { - this.events.off(eventName, callback); + off(event: AppEvent, callback?: (payload?: T) => void) { + this.events.off(event, callback); } cycleGraphTooltip() { @@ -911,7 +912,7 @@ export class DashboardModel { templateVariableValueUpdated() { this.processRepeats(); - this.events.emit('template-variable-value-updated'); + this.events.emit(CoreEvents.templateVariableValueUpdated); } expandParentRowFor(panelId: number) { diff --git a/public/app/features/dashboard/state/PanelModel.test.ts b/public/app/features/dashboard/state/PanelModel.test.ts index fd47ecc3013..f3d8d1497df 100644 --- a/public/app/features/dashboard/state/PanelModel.test.ts +++ b/public/app/features/dashboard/state/PanelModel.test.ts @@ -1,5 +1,6 @@ import { PanelModel } from './PanelModel'; import { getPanelPlugin } from '../../plugins/__mocks__/pluginMocks'; +import { PanelEvents } from '@grafana/ui'; class TablePanelCtrl {} @@ -144,7 +145,7 @@ describe('PanelModel', () => { let tearDownPublished = false; beforeEach(() => { - model.events.on('panel-teardown', () => { + model.events.on(PanelEvents.panelTeardown, () => { tearDownPublished = true; }); model.changePlugin(getPanelPlugin({ id: 'graph' })); diff --git a/public/app/features/dashboard/state/PanelModel.ts b/public/app/features/dashboard/state/PanelModel.ts index c390ae0edee..67035e46f15 100644 --- a/public/app/features/dashboard/state/PanelModel.ts +++ b/public/app/features/dashboard/state/PanelModel.ts @@ -4,12 +4,16 @@ import _ from 'lodash'; import { Emitter } from 'app/core/utils/emitter'; import { getNextRefIdChar } from 'app/core/utils/query'; // Types -import { DataQuery, DataQueryResponseData, PanelPlugin } from '@grafana/ui'; +import { DataQuery, DataQueryResponseData, PanelPlugin, PanelEvents } from '@grafana/ui'; import { DataLink, DataTransformerConfig, ScopedVars } from '@grafana/data'; import config from 'app/core/config'; import { PanelQueryRunner } from './PanelQueryRunner'; +import { eventFactory } from '@grafana/data'; + +export const panelAdded = eventFactory('panel-added'); +export const panelRemoved = eventFactory('panel-removed'); export interface GridPos { x: number; @@ -179,7 +183,7 @@ export class PanelModel { setViewMode(fullscreen: boolean, isEditing: boolean) { this.fullscreen = fullscreen; this.isEditing = isEditing; - this.events.emit('view-mode-changed'); + this.events.emit(PanelEvents.viewModeChanged); } updateGridPos(newPos: GridPos) { @@ -195,29 +199,29 @@ export class PanelModel { this.gridPos.h = newPos.h; if (sizeChanged) { - this.events.emit('panel-size-changed'); + this.events.emit(PanelEvents.panelSizeChanged); } } resizeDone() { - this.events.emit('panel-size-changed'); + this.events.emit(PanelEvents.panelSizeChanged); } refresh() { this.hasRefreshed = true; - this.events.emit('refresh'); + this.events.emit(PanelEvents.refresh); } render() { if (!this.hasRefreshed) { this.refresh(); } else { - this.events.emit('render'); + this.events.emit(PanelEvents.render); } } initialized() { - this.events.emit('panel-initialized'); + this.events.emit(PanelEvents.panelInitialized); } private getOptionsToRemember() { @@ -343,7 +347,7 @@ export class PanelModel { } destroy() { - this.events.emit('panel-teardown'); + this.events.emit(PanelEvents.panelTeardown); this.events.removeAllListeners(); if (this.queryRunner) { diff --git a/public/app/features/dashboard/utils/panel.ts b/public/app/features/dashboard/utils/panel.ts index 5e319c37f31..a641f490d83 100644 --- a/public/app/features/dashboard/utils/panel.ts +++ b/public/app/features/dashboard/utils/panel.ts @@ -3,8 +3,8 @@ import store from 'app/core/store'; // Models import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; -import { PanelModel } from 'app/features/dashboard/state/PanelModel'; -import { TimeRange } from '@grafana/data'; +import { PanelModel, panelRemoved, panelAdded } from 'app/features/dashboard/state/PanelModel'; +import { TimeRange, AppEvents } from '@grafana/data'; // Utils import { isString as _isString } from 'lodash'; @@ -18,6 +18,7 @@ import templateSrv from 'app/features/templating/template_srv'; // Constants import { LS_PANEL_COPY_KEY, PANEL_BORDER } from 'app/core/constants'; +import { CoreEvents } from 'app/types'; export const removePanel = (dashboard: DashboardModel, panel: PanelModel, ask: boolean) => { // confirm deletion @@ -25,7 +26,7 @@ export const removePanel = (dashboard: DashboardModel, panel: PanelModel, ask: b 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', { + appEvents.emit(CoreEvents.showConfirmModal, { title: 'Remove Panel', text: 'Are you sure you want to remove this panel?', text2: text2, @@ -45,7 +46,7 @@ export const duplicatePanel = (dashboard: DashboardModel, panel: PanelModel) => 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']); + appEvents.emit(AppEvents.alertSuccess, ['Panel copied. Open Add Panel to paste']); }; const replacePanel = (dashboard: DashboardModel, newPanel: PanelModel, oldPanel: PanelModel) => { @@ -53,15 +54,15 @@ const replacePanel = (dashboard: DashboardModel, newPanel: PanelModel, oldPanel: return panel.id === oldPanel.id; }); - const deletedPanel = dashboard.panels.splice(index, 1); - dashboard.events.emit('panel-removed', deletedPanel); + const deletedPanel = dashboard.panels.splice(index, 1)[0]; + dashboard.events.emit(panelRemoved, deletedPanel); newPanel = new PanelModel(newPanel); newPanel.id = oldPanel.id; dashboard.panels.splice(index, 0, newPanel); dashboard.sortPanelsByGridPos(); - dashboard.events.emit('panel-added', newPanel); + dashboard.events.emit(panelAdded, newPanel); }; export const editPanelJson = (dashboard: DashboardModel, panel: PanelModel) => { @@ -74,14 +75,14 @@ export const editPanelJson = (dashboard: DashboardModel, panel: PanelModel) => { enableCopy: true, }; - appEvents.emit('show-modal', { + appEvents.emit(CoreEvents.showModal, { src: 'public/app/partials/edit_json.html', model: model, }); }; export const sharePanel = (dashboard: DashboardModel, panel: PanelModel) => { - appEvents.emit('show-modal', { + appEvents.emit(CoreEvents.showModal, { src: 'public/app/features/dashboard/components/ShareModal/template.html', model: { dashboard: dashboard, diff --git a/public/app/features/datasources/settings/DataSourceSettingsPage.tsx b/public/app/features/datasources/settings/DataSourceSettingsPage.tsx index 610e118898c..c55e78eceef 100644 --- a/public/app/features/datasources/settings/DataSourceSettingsPage.tsx +++ b/public/app/features/datasources/settings/DataSourceSettingsPage.tsx @@ -29,7 +29,7 @@ import { getNavModel } from 'app/core/selectors/navModel'; import { getRouteParamsId } from 'app/core/selectors/location'; // Types -import { StoreState } from 'app/types/'; +import { StoreState, CoreEvents } from 'app/types/'; import { UrlQueryMap } from '@grafana/runtime'; import { DataSourceSettings, DataSourcePluginMeta } from '@grafana/ui'; import { NavModel } from '@grafana/data'; @@ -114,7 +114,7 @@ export class DataSourceSettingsPage extends PureComponent { }; onDelete = () => { - appEvents.emit('confirm-modal', { + appEvents.emit(CoreEvents.showConfirmModal, { title: 'Delete', text: 'Are you sure you want to delete this data source?', yesText: 'Delete', diff --git a/public/app/features/explore/state/reducers.ts b/public/app/features/explore/state/reducers.ts index b6dd7bc82fc..cdcdd4de207 100644 --- a/public/app/features/explore/state/reducers.ts +++ b/public/app/features/explore/state/reducers.ts @@ -11,7 +11,7 @@ import { } from 'app/core/utils/explore'; import { ExploreItemState, ExploreState, ExploreId, ExploreUpdateState, ExploreMode } from 'app/types/explore'; import { LoadingState, toLegacyResponseData, DefaultTimeRange } from '@grafana/data'; -import { DataQuery, DataSourceApi, PanelData, DataQueryRequest, RefreshPicker } from '@grafana/ui'; +import { DataQuery, DataSourceApi, PanelData, DataQueryRequest, RefreshPicker, PanelEvents } from '@grafana/ui'; import { HigherOrderAction, ActionTypes, @@ -600,7 +600,7 @@ export const processQueryResponse = ( } // For Angular editors - state.eventBridge.emit('data-error', error); + state.eventBridge.emit(PanelEvents.dataError, error); return { ...state, @@ -624,7 +624,7 @@ export const processQueryResponse = ( if (state.datasourceInstance.components.QueryCtrl) { const legacy = series.map(v => toLegacyResponseData(v)); - state.eventBridge.emit('data-received', legacy); + state.eventBridge.emit(PanelEvents.dataReceived, legacy); } return { diff --git a/public/app/features/folders/CreateFolderCtrl.ts b/public/app/features/folders/CreateFolderCtrl.ts index 5b3872c2532..14aaeb26b3d 100644 --- a/public/app/features/folders/CreateFolderCtrl.ts +++ b/public/app/features/folders/CreateFolderCtrl.ts @@ -4,6 +4,7 @@ import { BackendSrv } from 'app/core/services/backend_srv'; import { ILocationService } from 'angular'; import { ValidationSrv } from 'app/features/manage-dashboards'; import { NavModelSrv } from 'app/core/nav_model_srv'; +import { AppEvents } from '@grafana/data'; export default class CreateFolderCtrl { title = ''; @@ -28,7 +29,7 @@ export default class CreateFolderCtrl { } return this.backendSrv.createFolder({ title: this.title }).then((result: any) => { - appEvents.emit('alert-success', ['Folder Created', 'OK']); + appEvents.emit(AppEvents.alertSuccess, ['Folder Created', 'OK']); this.$location.url(locationUtil.stripBaseFromUrl(result.url)); }); } diff --git a/public/app/features/folders/FolderSettingsPage.tsx b/public/app/features/folders/FolderSettingsPage.tsx index 04fa00ba91c..37feaf84c7c 100644 --- a/public/app/features/folders/FolderSettingsPage.tsx +++ b/public/app/features/folders/FolderSettingsPage.tsx @@ -6,7 +6,7 @@ import { Input } from '@grafana/ui'; import Page from 'app/core/components/Page/Page'; import appEvents from 'app/core/app_events'; import { getNavModel } from 'app/core/selectors/navModel'; -import { StoreState, FolderState } from 'app/types'; +import { StoreState, FolderState, CoreEvents } from 'app/types'; import { getFolderByUid, setFolderTitle, saveFolder, deleteFolder } from './state/actions'; import { getLoadingNav } from './state/navModel'; @@ -52,7 +52,7 @@ export class FolderSettingsPage extends PureComponent { evt.stopPropagation(); evt.preventDefault(); - appEvents.emit('confirm-modal', { + appEvents.emit(CoreEvents.showConfirmModal, { title: 'Delete', text: `Do you want to delete this folder and all its dashboards?`, icon: 'fa-trash', diff --git a/public/app/features/folders/state/actions.ts b/public/app/features/folders/state/actions.ts index a7adc71e2d8..af391e084f9 100644 --- a/public/app/features/folders/state/actions.ts +++ b/public/app/features/folders/state/actions.ts @@ -13,6 +13,7 @@ import { import { updateNavIndex, updateLocation } from 'app/core/actions'; import { buildNavModel } from './navModel'; import appEvents from 'app/core/app_events'; +import { AppEvents } from '@grafana/data'; export enum ActionTypes { LoadFolder = 'LOAD_FOLDER', @@ -71,7 +72,7 @@ export function saveFolder(folder: FolderState): ThunkResult { }); // this should be redux action at some point - appEvents.emit('alert-success', ['Folder saved']); + appEvents.emit(AppEvents.alertSuccess, ['Folder saved']); dispatch(updateLocation({ path: `${res.url}/settings` })); }; diff --git a/public/app/features/manage-dashboards/SnapshotListCtrl.ts b/public/app/features/manage-dashboards/SnapshotListCtrl.ts index 4ccb629412c..67f9d38255f 100644 --- a/public/app/features/manage-dashboards/SnapshotListCtrl.ts +++ b/public/app/features/manage-dashboards/SnapshotListCtrl.ts @@ -2,6 +2,8 @@ import _ from 'lodash'; import { NavModelSrv } from 'app/core/core'; import { ILocationService } from 'angular'; import { BackendSrv } from '@grafana/runtime'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; +import { CoreEvents } from 'app/types'; export class SnapshotListCtrl { navModel: any; @@ -9,7 +11,7 @@ export class SnapshotListCtrl { /** @ngInject */ constructor( - private $rootScope: any, + private $rootScope: GrafanaRootScope, private backendSrv: BackendSrv, navModelSrv: NavModelSrv, private $location: ILocationService @@ -35,7 +37,7 @@ export class SnapshotListCtrl { } removeSnapshot(snapshot: any) { - this.$rootScope.appEvent('confirm-modal', { + this.$rootScope.appEvent(CoreEvents.showConfirmModal, { title: 'Delete', text: 'Are you sure you want to delete snapshot ' + snapshot.name + '?', yesText: 'Delete', diff --git a/public/app/features/manage-dashboards/components/MoveToFolderModal/MoveToFolderCtrl.ts b/public/app/features/manage-dashboards/components/MoveToFolderModal/MoveToFolderCtrl.ts index 543a58b848d..f4c24e28100 100644 --- a/public/app/features/manage-dashboards/components/MoveToFolderModal/MoveToFolderCtrl.ts +++ b/public/app/features/manage-dashboards/components/MoveToFolderModal/MoveToFolderCtrl.ts @@ -1,6 +1,7 @@ import coreModule from 'app/core/core_module'; import appEvents from 'app/core/app_events'; import { BackendSrv } from 'app/core/services/backend_srv'; +import { AppEvents } from '@grafana/data'; export class MoveToFolderCtrl { dashboards: any; @@ -23,11 +24,11 @@ export class MoveToFolderCtrl { const msg = `${result.successCount} dashboard${result.successCount === 1 ? '' : 's'} moved to ${ this.folder.title }`; - appEvents.emit('alert-success', [header, msg]); + appEvents.emit(AppEvents.alertSuccess, [header, msg]); } if (result.totalCount === result.alreadyInFolderCount) { - appEvents.emit('alert-error', ['Error', `Dashboards already belongs to folder ${this.folder.title}`]); + appEvents.emit(AppEvents.alertError, ['Error', `Dashboards already belongs to folder ${this.folder.title}`]); } this.dismiss(); diff --git a/public/app/features/manage-dashboards/components/UploadDashboard/uploadDashboardDirective.ts b/public/app/features/manage-dashboards/components/UploadDashboard/uploadDashboardDirective.ts index 6dae44598a2..b6bff8c94f2 100644 --- a/public/app/features/manage-dashboards/components/UploadDashboard/uploadDashboardDirective.ts +++ b/public/app/features/manage-dashboards/components/UploadDashboard/uploadDashboardDirective.ts @@ -1,6 +1,7 @@ import coreModule from 'app/core/core_module'; import appEvents from 'app/core/app_events'; import angular, { ILocationService } from 'angular'; +import { AppEvents } from '@grafana/data'; const template = ` @@ -30,7 +31,10 @@ export function uploadDashboardDirective(timer: any, $location: ILocationService dash = JSON.parse(e.target.result); } catch (err) { console.log(err); - appEvents.emit('alert-error', ['Import failed', 'JSON -> JS Serialization failed: ' + err.message]); + appEvents.emit(AppEvents.alertError, [ + 'Import failed', + 'JSON -> JS Serialization failed: ' + err.message, + ]); return; } @@ -58,7 +62,7 @@ export function uploadDashboardDirective(timer: any, $location: ILocationService // Something elem[0].addEventListener('change', file_selected, false); } else { - appEvents.emit('alert-error', ['Oops', 'The HTML5 File APIs are not fully supported in this browser']); + appEvents.emit(AppEvents.alertError, ['Oops', 'The HTML5 File APIs are not fully supported in this browser']); } }, }; diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts index 5dc612f86c7..9c0a7640cf5 100644 --- a/public/app/features/panel/metrics_panel_ctrl.ts +++ b/public/app/features/panel/metrics_panel_ctrl.ts @@ -6,11 +6,12 @@ import { PanelCtrl } from 'app/features/panel/panel_ctrl'; import { getExploreUrl } from 'app/core/utils/explore'; import { applyPanelTimeOverrides, getResolution } from 'app/features/dashboard/utils/panel'; import { ContextSrv } from 'app/core/services/context_srv'; -import { toLegacyResponseData, TimeRange, LoadingState, DataFrame, toDataFrameDTO } from '@grafana/data'; +import { toLegacyResponseData, toDataFrameDTO, TimeRange, LoadingState, DataFrame } from '@grafana/data'; -import { LegacyResponseData, DataSourceApi, PanelData, DataQueryResponse } from '@grafana/ui'; +import { LegacyResponseData, DataSourceApi, PanelData, DataQueryResponse, PanelEvents } from '@grafana/ui'; import { Unsubscribable } from 'rxjs'; import { PanelModel } from 'app/features/dashboard/state'; +import { CoreEvents } from 'app/types'; class MetricsPanelCtrl extends PanelCtrl { scope: any; @@ -42,8 +43,8 @@ class MetricsPanelCtrl extends PanelCtrl { this.scope = $scope; this.panel.datasource = this.panel.datasource || null; - this.events.on('refresh', this.onMetricsPanelRefresh.bind(this)); - this.events.on('panel-teardown', this.onPanelTearDown.bind(this)); + this.events.on(PanelEvents.refresh, this.onMetricsPanelRefresh.bind(this)); + this.events.on(PanelEvents.panelTeardown, this.onPanelTearDown.bind(this)); } private onPanelTearDown() { @@ -71,7 +72,7 @@ class MetricsPanelCtrl extends PanelCtrl { // Defer panel rendering till the next digest cycle. // For some reason snapshot panels don't init at this time, so this helps to avoid rendering issues. return this.$timeout(() => { - this.events.emit('data-snapshot-load', data); + this.events.emit(PanelEvents.dataSnapshotLoad, data); }); } @@ -111,7 +112,7 @@ class MetricsPanelCtrl extends PanelCtrl { console.log('Panel data error:', err); return this.$timeout(() => { - this.events.emit('data-error', err); + this.events.emit(PanelEvents.dataError, err); }); } @@ -214,7 +215,7 @@ class MetricsPanelCtrl extends PanelCtrl { } try { - this.events.emit('data-frames-received', data); + this.events.emit(CoreEvents.dataFramesReceived, data); } catch (err) { this.processDataError(err); } @@ -233,7 +234,7 @@ class MetricsPanelCtrl extends PanelCtrl { } try { - this.events.emit('data-received', result.data); + this.events.emit(PanelEvents.dataReceived, result.data); } catch (err) { this.processDataError(err); } diff --git a/public/app/features/panel/panel_ctrl.ts b/public/app/features/panel/panel_ctrl.ts index 0a6a6fc06e7..68545c93d90 100644 --- a/public/app/features/panel/panel_ctrl.ts +++ b/public/app/features/panel/panel_ctrl.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; import { sanitize, escapeHtml } from 'app/core/utils/text'; -import { renderMarkdown } from '@grafana/data'; +import { renderMarkdown, AppEvent } from '@grafana/data'; import config from 'app/core/config'; import { profiler } from 'app/core/core'; @@ -14,12 +14,12 @@ import { sharePanel as sharePanelUtil, calculateInnerPanelHeight, } from 'app/features/dashboard/utils/panel'; - import { GRID_COLUMN_COUNT } from 'app/core/constants'; import { auto } from 'angular'; import { TemplateSrv } from '../templating/template_srv'; import { PanelPluginMeta } from '@grafana/ui/src/types/panel'; import { getPanelLinksSupplier } from './panellinks/linkSuppliers'; +import { PanelEvents } from '@grafana/ui'; export class PanelCtrl { panel: any; @@ -41,6 +41,7 @@ export class PanelCtrl { timing: any; maxPanelsPerRowOptions: number[]; + /** @ngInject */ constructor($scope: any, $injector: auto.IInjectorService) { this.$injector = $injector; this.$location = $injector.get('$location'); @@ -56,11 +57,11 @@ export class PanelCtrl { this.pluginName = plugin.name; } - $scope.$on('component-did-mount', () => this.panelDidMount()); + $scope.$on(PanelEvents.componentDidMount.name, () => this.panelDidMount()); } panelDidMount() { - this.events.emit('component-did-mount'); + this.events.emit(PanelEvents.componentDidMount); this.dashboard.panelInitialized(this.panel); } @@ -72,12 +73,12 @@ export class PanelCtrl { this.panel.refresh(); } - publishAppEvent(evtName: string, evt: any) { - this.$scope.$root.appEvent(evtName, evt); + publishAppEvent(event: AppEvent, payload?: T) { + this.$scope.$root.appEvent(event, payload); } changeView(fullscreen: boolean, edit: boolean) { - this.publishAppEvent('panel-change-view', { + this.publishAppEvent(PanelEvents.panelChangeView, { fullscreen, edit, panelId: this.panel.id, @@ -99,7 +100,7 @@ export class PanelCtrl { initEditMode() { if (!this.editModeInitiated) { this.editModeInitiated = true; - this.events.emit('init-edit-mode', null); + this.events.emit(PanelEvents.editModeInitialized); this.maxPanelsPerRowOptions = getFactors(GRID_COLUMN_COUNT); } } @@ -193,7 +194,7 @@ export class PanelCtrl { click: 'ctrl.editPanelJson(); dismiss();', }); - this.events.emit('init-panel-actions', menu); + this.events.emit(PanelEvents.initPanelActions, menu); return menu; } @@ -212,7 +213,7 @@ export class PanelCtrl { } render(payload?: any) { - this.events.emit('render', payload); + this.events.emit(PanelEvents.render, payload); } duplicate() { diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index 16126bb809f..ef6594a255d 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/public/app/features/panel/panel_directive.ts @@ -4,6 +4,7 @@ import $ from 'jquery'; import Drop from 'tether-drop'; // @ts-ignore import baron from 'baron'; +import { PanelEvents } from '@grafana/ui'; const module = angular.module('grafana.directives'); @@ -73,7 +74,7 @@ module.directive('grafanaPanel', ($rootScope, $document, $timeout) => { } // update scrollbar after mounting - ctrl.events.on('component-did-mount', () => { + ctrl.events.on(PanelEvents.componentDidMount, () => { if (ctrl.__proto__.constructor.scrollable) { const scrollRootClass = 'baron baron__root baron__clipper panel-content--scrollable'; const scrollerClass = 'baron__scroller'; @@ -102,7 +103,7 @@ module.directive('grafanaPanel', ($rootScope, $document, $timeout) => { } }); - ctrl.events.on('panel-size-changed', () => { + ctrl.events.on(PanelEvents.panelSizeChanged, () => { ctrl.calculatePanelHeight(panelContainer[0].offsetHeight); $timeout(() => { resizeScrollableContent(); @@ -110,7 +111,7 @@ module.directive('grafanaPanel', ($rootScope, $document, $timeout) => { }); }); - ctrl.events.on('view-mode-changed', () => { + ctrl.events.on(PanelEvents.viewModeChanged, () => { // first wait one pass for dashboard fullscreen view mode to take effect (classses being applied) setTimeout(() => { // then recalc style @@ -123,7 +124,7 @@ module.directive('grafanaPanel', ($rootScope, $document, $timeout) => { }, 10); }); - ctrl.events.on('render', () => { + ctrl.events.on(PanelEvents.render, () => { // set initial height if (!ctrl.height) { ctrl.calculatePanelHeight(panelContainer[0].offsetHeight); diff --git a/public/app/features/playlist/playlist_edit_ctrl.ts b/public/app/features/playlist/playlist_edit_ctrl.ts index 70ae3a29b62..de77a51648b 100644 --- a/public/app/features/playlist/playlist_edit_ctrl.ts +++ b/public/app/features/playlist/playlist_edit_ctrl.ts @@ -3,6 +3,8 @@ import coreModule from '../../core/core_module'; import { ILocationService } from 'angular'; import { BackendSrv } from 'app/core/services/backend_srv'; import { NavModelSrv } from 'app/core/nav_model_srv'; +import { AppEventEmitter } from 'app/types'; +import { AppEvents } from '@grafana/data'; export interface PlaylistItem { value: any; @@ -27,7 +29,7 @@ export class PlaylistEditCtrl { /** @ngInject */ constructor( - private $scope: any, + private $scope: AppEventEmitter, private backendSrv: BackendSrv, private $location: ILocationService, $route: any, @@ -102,11 +104,11 @@ export class PlaylistEditCtrl { savePromise.then( () => { - this.$scope.appEvent('alert-success', ['Playlist saved', '']); + this.$scope.appEvent(AppEvents.alertSuccess, ['Playlist saved']); this.$location.path('/playlists'); }, () => { - this.$scope.appEvent('alert-error', ['Unable to save playlist', '']); + this.$scope.appEvent(AppEvents.alertError, ['Unable to save playlist']); } ); } diff --git a/public/app/features/playlist/playlist_srv.ts b/public/app/features/playlist/playlist_srv.ts index 57252005cdc..01542823701 100644 --- a/public/app/features/playlist/playlist_srv.ts +++ b/public/app/features/playlist/playlist_srv.ts @@ -8,6 +8,7 @@ import appEvents from 'app/core/app_events'; import locationUtil from 'app/core/utils/location_util'; import kbn from 'app/core/utils/kbn'; import { store } from 'app/store/store'; +import { CoreEvents } from 'app/types'; export const queryParamsToPreserve: { [key: string]: boolean } = { kiosk: true, @@ -86,7 +87,7 @@ export class PlaylistSrv { this.storeUnsub = store.subscribe(() => this.storeUpdated()); this.validPlaylistUrl = this.$location.path(); - appEvents.emit('playlist-started'); + appEvents.emit(CoreEvents.playlistStarted); return this.backendSrv.get(`/api/playlists/${playlistId}`).then((playlist: any) => { return this.backendSrv.get(`/api/playlists/${playlistId}/dashboards`).then((dashboards: any) => { @@ -101,7 +102,7 @@ export class PlaylistSrv { if (this.isPlaying) { const queryParams = this.$location.search(); if (queryParams.kiosk) { - appEvents.emit('toggle-kiosk-mode', { exit: true }); + appEvents.emit(CoreEvents.toggleKioskMode, { exit: true }); } } @@ -116,7 +117,7 @@ export class PlaylistSrv { this.$timeout.cancel(this.cancelPromise); } - appEvents.emit('playlist-stopped'); + appEvents.emit(CoreEvents.playlistStopped); } } diff --git a/public/app/features/playlist/playlists_ctrl.ts b/public/app/features/playlist/playlists_ctrl.ts index 4b3c09894a7..19da03b2614 100644 --- a/public/app/features/playlist/playlists_ctrl.ts +++ b/public/app/features/playlist/playlists_ctrl.ts @@ -2,6 +2,8 @@ import _ from 'lodash'; import coreModule from '../../core/core_module'; import { BackendSrv } from '@grafana/runtime'; import { NavModelSrv } from 'app/core/nav_model_srv'; +import { CoreEvents } from 'app/types'; +import { AppEvents } from '@grafana/data'; export class PlaylistsCtrl { playlists: any; @@ -24,17 +26,17 @@ export class PlaylistsCtrl { this.backendSrv.delete('/api/playlists/' + playlist.id).then( () => { - this.$scope.appEvent('alert-success', ['Playlist deleted', '']); + this.$scope.appEvent(AppEvents.alertSuccess, ['Playlist deleted']); }, () => { - this.$scope.appEvent('alert-error', ['Unable to delete playlist', '']); + this.$scope.appEvent(AppEvents.alertError, ['Unable to delete playlist']); this.playlists.push(playlist); } ); } removePlaylist(playlist: any) { - this.$scope.appEvent('confirm-modal', { + this.$scope.appEvent(CoreEvents.showConfirmModal, { title: 'Delete', text: 'Are you sure you want to delete playlist ' + playlist.name + '?', yesText: 'Delete', diff --git a/public/app/features/plugins/AppRootPage.tsx b/public/app/features/plugins/AppRootPage.tsx index 8a841dd0f50..9702dab99b0 100644 --- a/public/app/features/plugins/AppRootPage.tsx +++ b/public/app/features/plugins/AppRootPage.tsx @@ -11,7 +11,7 @@ import Page from 'app/core/components/Page/Page'; import { getPluginSettings } from './PluginSettingsCache'; import { importAppPlugin } from './plugin_loader'; import { AppPlugin, AppPluginMeta, PluginType } from '@grafana/ui'; -import { NavModel } from '@grafana/data'; +import { NavModel, AppEvents } from '@grafana/data'; import { getLoadingNav } from './PluginPage'; import { getNotFoundNav, getWarningNav } from 'app/core/nav_model_srv'; import { appEvents } from 'app/core/core'; @@ -58,7 +58,7 @@ class AppRootPage extends Component { const app = await getPluginSettings(pluginId).then(info => { const error = getAppPluginPageError(info); if (error) { - appEvents.emit('alert-error', [error]); + appEvents.emit(AppEvents.alertError, [error]); this.setState({ nav: getWarningNav(error) }); return null; } diff --git a/public/app/features/plugins/PluginDashboards.tsx b/public/app/features/plugins/PluginDashboards.tsx index 0a8b5563f00..84a110b1ce8 100644 --- a/public/app/features/plugins/PluginDashboards.tsx +++ b/public/app/features/plugins/PluginDashboards.tsx @@ -7,6 +7,7 @@ import { PluginDashboard } from 'app/types'; import { getBackendSrv } from 'app/core/services/backend_srv'; import { appEvents } from 'app/core/core'; import DashboardsTable from 'app/features/datasources/DashboardsTable'; +import { AppEvents } from '@grafana/data'; interface Props { plugin: PluginMeta; @@ -79,7 +80,7 @@ export class PluginDashboards extends PureComponent { return getBackendSrv() .post(`/api/dashboards/import`, installCmd) .then((res: PluginDashboard) => { - appEvents.emit('alert-success', ['Dashboard Imported', dash.title]); + appEvents.emit(AppEvents.alertSuccess, ['Dashboard Imported', dash.title]); extend(dash, res); this.setState({ dashboards: [...this.state.dashboards] }); }); diff --git a/public/app/features/plugins/PluginPage.tsx b/public/app/features/plugins/PluginPage.tsx index ab1e85c1d32..bc79a835ac4 100644 --- a/public/app/features/plugins/PluginPage.tsx +++ b/public/app/features/plugins/PluginPage.tsx @@ -5,7 +5,7 @@ import { connect } from 'react-redux'; import find from 'lodash/find'; // Types import { UrlQueryMap } from '@grafana/runtime'; -import { AppNotificationSeverity, StoreState } from 'app/types'; +import { StoreState, AppNotificationSeverity, CoreEvents } from 'app/types'; import { Alert, AppPlugin, @@ -173,7 +173,7 @@ class PluginPage extends PureComponent { } showUpdateInfo = () => { - appEvents.emit('show-modal', { + appEvents.emit(CoreEvents.showModal, { src: 'public/app/features/plugins/partials/update_instructions.html', model: this.state.plugin.meta, }); diff --git a/public/app/features/plugins/datasource_srv.ts b/public/app/features/plugins/datasource_srv.ts index 133d2ee9ba7..18d23de7060 100644 --- a/public/app/features/plugins/datasource_srv.ts +++ b/public/app/features/plugins/datasource_srv.ts @@ -9,9 +9,10 @@ import { DataSourceSrv as DataSourceService, getDataSourceSrv as getDataSourceSe // Types import { DataSourceApi, DataSourceSelectItem } from '@grafana/ui'; -import { ScopedVars } from '@grafana/data'; +import { ScopedVars, AppEvents } from '@grafana/data'; import { auto } from 'angular'; import { TemplateSrv } from '../templating/template_srv'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; export class DatasourceSrv implements DataSourceService { datasources: { [name: string]: DataSourceApi }; @@ -20,7 +21,7 @@ export class DatasourceSrv implements DataSourceService { constructor( private $q: any, private $injector: auto.IInjectorService, - private $rootScope: any, + private $rootScope: GrafanaRootScope, private templateSrv: TemplateSrv ) { this.init(); @@ -86,7 +87,7 @@ export class DatasourceSrv implements DataSourceService { deferred.resolve(instance); }) .catch(err => { - this.$rootScope.appEvent('alert-error', [dsConfig.name + ' plugin failed', err.toString()]); + this.$rootScope.appEvent(AppEvents.alertError, [dsConfig.name + ' plugin failed', err.toString()]); }); return deferred.promise; diff --git a/public/app/features/plugins/plugin_component.ts b/public/app/features/plugins/plugin_component.ts index 92fb54a8ee2..4e008e343ef 100644 --- a/public/app/features/plugins/plugin_component.ts +++ b/public/app/features/plugins/plugin_component.ts @@ -7,12 +7,13 @@ import coreModule from 'app/core/core_module'; import { DataSourceApi } from '@grafana/ui'; import { importPanelPlugin, importDataSourcePlugin, importAppPlugin } from './plugin_loader'; import DatasourceSrv from './datasource_srv'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; /** @ngInject */ function pluginDirectiveLoader( $compile: any, datasourceSrv: DatasourceSrv, - $rootScope: any, + $rootScope: GrafanaRootScope, $q: IQService, $http: any, $templateCache: any, diff --git a/public/app/features/plugins/plugin_page_ctrl.ts b/public/app/features/plugins/plugin_page_ctrl.ts index 4a3d74ef47e..83d0b221b19 100644 --- a/public/app/features/plugins/plugin_page_ctrl.ts +++ b/public/app/features/plugins/plugin_page_ctrl.ts @@ -4,6 +4,8 @@ import _ from 'lodash'; import { getPluginSettings } from './PluginSettingsCache'; import { PluginMeta } from '@grafana/ui'; import { NavModelSrv } from 'app/core/core'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; +import { AppEvents } from '@grafana/data'; export class AppPageCtrl { page: any; @@ -14,7 +16,7 @@ export class AppPageCtrl { /** @ngInject */ constructor( private $routeParams: any, - private $rootScope: any, + private $rootScope: GrafanaRootScope, private navModelSrv: NavModelSrv, private $q: IQService ) { @@ -26,7 +28,7 @@ export class AppPageCtrl { this.initPage(settings); }) .catch(err => { - this.$rootScope.appEvent('alert-error', ['Unknown Plugin', '']); + this.$rootScope.appEvent(AppEvents.alertError, ['Unknown Plugin']); this.navModel = this.navModelSrv.getNotFoundNav(); }); } @@ -36,12 +38,12 @@ export class AppPageCtrl { this.page = _.find(app.includes, { slug: this.$routeParams.slug }); if (!this.page) { - this.$rootScope.appEvent('alert-error', ['App Page Not Found', '']); + this.$rootScope.appEvent(AppEvents.alertError, ['App Page Not Found']); this.navModel = this.navModelSrv.getNotFoundNav(); return; } if (app.type !== 'app' || !app.enabled) { - this.$rootScope.appEvent('alert-error', ['Application Not Enabled', '']); + this.$rootScope.appEvent(AppEvents.alertError, ['Application Not Enabled']); this.navModel = this.navModelSrv.getNotFoundNav(); return; } diff --git a/public/app/features/plugins/specs/datasource_srv.test.ts b/public/app/features/plugins/specs/datasource_srv.test.ts index 2245e3a34db..46b469a93a5 100644 --- a/public/app/features/plugins/specs/datasource_srv.test.ts +++ b/public/app/features/plugins/specs/datasource_srv.test.ts @@ -17,7 +17,7 @@ const templateSrv: any = { }; describe('datasource_srv', () => { - const _datasourceSrv = new DatasourceSrv({}, {} as any, {}, templateSrv); + const _datasourceSrv = new DatasourceSrv({}, {} as any, {} as any, templateSrv); describe('when loading external datasources', () => { beforeEach(() => { diff --git a/public/app/features/templating/editor_ctrl.ts b/public/app/features/templating/editor_ctrl.ts index 24586334162..3b1ba6e1dab 100644 --- a/public/app/features/templating/editor_ctrl.ts +++ b/public/app/features/templating/editor_ctrl.ts @@ -5,6 +5,7 @@ import appEvents from 'app/core/app_events'; import DatasourceSrv from '../plugins/datasource_srv'; import { VariableSrv } from './all'; import { TemplateSrv } from './template_srv'; +import { AppEvents } from '@grafana/data'; export class VariableEditorCtrl { /** @ngInject */ @@ -85,13 +86,16 @@ export class VariableEditorCtrl { } if (!$scope.current.name.match(/^\w+$/)) { - appEvents.emit('alert-warning', ['Validation', 'Only word and digit characters are allowed in variable names']); + appEvents.emit(AppEvents.alertWarning, [ + 'Validation', + 'Only word and digit characters are allowed in variable names', + ]); return false; } const sameName: any = _.find($scope.variables, { name: $scope.current.name }); if (sameName && sameName !== $scope.current) { - appEvents.emit('alert-warning', ['Validation', 'Variable with the same name already exists']); + appEvents.emit(AppEvents.alertWarning, ['Validation', 'Variable with the same name already exists']); return false; } @@ -100,7 +104,7 @@ export class VariableEditorCtrl { _.isString($scope.current.query) && $scope.current.query.match(new RegExp('\\$' + $scope.current.name + '(/| |$)')) ) { - appEvents.emit('alert-warning', [ + appEvents.emit(AppEvents.alertWarning, [ 'Validation', 'Query cannot contain a reference to itself. Variable: $' + $scope.current.name, ]); @@ -128,7 +132,10 @@ export class VariableEditorCtrl { if (err.data && err.data.message) { err.message = err.data.message; } - appEvents.emit('alert-error', ['Templating', 'Template variables could not be initialized: ' + err.message]); + appEvents.emit(AppEvents.alertError, [ + 'Templating', + 'Template variables could not be initialized: ' + err.message, + ]); }); }; diff --git a/public/app/features/templating/specs/editor_ctrl.test.ts b/public/app/features/templating/specs/editor_ctrl.test.ts index c04bb19d9e0..9823d1d9cea 100644 --- a/public/app/features/templating/specs/editor_ctrl.test.ts +++ b/public/app/features/templating/specs/editor_ctrl.test.ts @@ -1,5 +1,6 @@ import { VariableEditorCtrl } from '../editor_ctrl'; import { TemplateSrv } from '../template_srv'; +import { AppEvents } from '@grafana/data'; let mockEmit: any; jest.mock('app/core/app_events', () => { @@ -32,7 +33,7 @@ describe('VariableEditorCtrl', () => { it('should emit an error', () => { return scope.runQuery().then(res => { expect(mockEmit).toBeCalled(); - expect(mockEmit.mock.calls[0][0]).toBe('alert-error'); + expect(mockEmit.mock.calls[0][0]).toBe(AppEvents.alertError); expect(mockEmit.mock.calls[0][1][0]).toBe('Templating'); expect(mockEmit.mock.calls[0][1][1]).toBe('Template variables could not be initialized: error'); }); diff --git a/public/app/features/templating/variable_srv.ts b/public/app/features/templating/variable_srv.ts index 9ee8773e761..a4d28569c5f 100644 --- a/public/app/features/templating/variable_srv.ts +++ b/public/app/features/templating/variable_srv.ts @@ -12,6 +12,7 @@ import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; // Types import { TimeRange } from '@grafana/data'; +import { CoreEvents } from 'app/types'; export class VariableSrv { dashboard: DashboardModel; @@ -28,8 +29,11 @@ export class VariableSrv { init(dashboard: DashboardModel) { this.dashboard = dashboard; - this.dashboard.events.on('time-range-updated', this.onTimeRangeUpdated.bind(this)); - this.dashboard.events.on('template-variable-value-updated', this.updateUrlParamsWithCurrentVariables.bind(this)); + this.dashboard.events.on(CoreEvents.timeRangeUpdated, this.onTimeRangeUpdated.bind(this)); + this.dashboard.events.on( + CoreEvents.templateVariableValueUpdated, + this.updateUrlParamsWithCurrentVariables.bind(this) + ); // create working class models representing variables this.variables = dashboard.templating.list = dashboard.templating.list.map(this.createVariableFromModel.bind(this)); diff --git a/public/app/features/users/UsersListPage.tsx b/public/app/features/users/UsersListPage.tsx index cd40bd3c8bb..f6dc772ae58 100644 --- a/public/app/features/users/UsersListPage.tsx +++ b/public/app/features/users/UsersListPage.tsx @@ -6,7 +6,7 @@ import Page from 'app/core/components/Page/Page'; import UsersActionBar from './UsersActionBar'; import UsersTable from './UsersTable'; import InviteesTable from './InviteesTable'; -import { Invitee, OrgUser } from 'app/types'; +import { Invitee, OrgUser, CoreEvents } from 'app/types'; import appEvents from 'app/core/app_events'; import { loadUsers, loadInvitees, setUsersSearchQuery, updateUser, removeUser } from './state/actions'; import { getNavModel } from 'app/core/selectors/navModel'; @@ -66,7 +66,7 @@ export class UsersListPage extends PureComponent { }; onRemoveUser = (user: OrgUser) => { - appEvents.emit('confirm-modal', { + appEvents.emit(CoreEvents.showConfirmModal, { title: 'Delete', text: 'Are you sure you want to delete user ' + user.login + '?', yesText: 'Delete', diff --git a/public/app/plugins/app/example-app/legacy/angular_example_page.ts b/public/app/plugins/app/example-app/legacy/angular_example_page.ts index b5ebe173ad2..525e58c0ae7 100644 --- a/public/app/plugins/app/example-app/legacy/angular_example_page.ts +++ b/public/app/plugins/app/example-app/legacy/angular_example_page.ts @@ -1,8 +1,10 @@ +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; + export class AngularExamplePageCtrl { static templateUrl = 'legacy/angular_example_page.html'; /** @ngInject */ - constructor($scope: any, $rootScope: any) { + constructor($scope: any, $rootScope: GrafanaRootScope) { console.log('AngularExamplePageCtrl:', this); } } diff --git a/public/app/plugins/datasource/elasticsearch/bucket_agg.ts b/public/app/plugins/datasource/elasticsearch/bucket_agg.ts index 83f10c0da38..cfd25d7ab55 100644 --- a/public/app/plugins/datasource/elasticsearch/bucket_agg.ts +++ b/public/app/plugins/datasource/elasticsearch/bucket_agg.ts @@ -2,10 +2,12 @@ import coreModule from 'app/core/core_module'; import _ from 'lodash'; import * as queryDef from './query_def'; import { IQService } from 'angular'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; +import { CoreEvents } from 'app/types'; export class ElasticBucketAggCtrl { /** @ngInject */ - constructor($scope: any, uiSegmentSrv: any, $q: IQService, $rootScope: any) { + constructor($scope: any, uiSegmentSrv: any, $q: IQService, $rootScope: GrafanaRootScope) { const bucketAggs = $scope.target.bucketAggs; $scope.orderByOptions = []; @@ -23,7 +25,7 @@ export class ElasticBucketAggCtrl { }; $rootScope.onAppEvent( - 'elastic-query-updated', + CoreEvents.elasticQueryUpdated, () => { $scope.validateModel(); }, diff --git a/public/app/plugins/datasource/elasticsearch/metric_agg.ts b/public/app/plugins/datasource/elasticsearch/metric_agg.ts index 0b711bf8921..7dbf22fe5a1 100644 --- a/public/app/plugins/datasource/elasticsearch/metric_agg.ts +++ b/public/app/plugins/datasource/elasticsearch/metric_agg.ts @@ -3,10 +3,12 @@ import _ from 'lodash'; import * as queryDef from './query_def'; import { ElasticsearchAggregation } from './types'; import { IQService } from 'angular'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; +import { CoreEvents } from 'app/types'; export class ElasticMetricAggCtrl { /** @ngInject */ - constructor($scope: any, uiSegmentSrv: any, $q: IQService, $rootScope: any) { + constructor($scope: any, uiSegmentSrv: any, $q: IQService, $rootScope: GrafanaRootScope) { const metricAggs: ElasticsearchAggregation[] = $scope.target.metrics; $scope.metricAggTypes = queryDef.getMetricAggTypes($scope.esVersion); $scope.extendedStats = queryDef.extendedStats; @@ -24,7 +26,7 @@ export class ElasticMetricAggCtrl { }; $rootScope.onAppEvent( - 'elastic-query-updated', + CoreEvents.elasticQueryUpdated, () => { $scope.index = _.indexOf(metricAggs, $scope.agg); $scope.updatePipelineAggOptions(); diff --git a/public/app/plugins/datasource/elasticsearch/query_ctrl.ts b/public/app/plugins/datasource/elasticsearch/query_ctrl.ts index f8df4e0b689..85e8158734f 100644 --- a/public/app/plugins/datasource/elasticsearch/query_ctrl.ts +++ b/public/app/plugins/datasource/elasticsearch/query_ctrl.ts @@ -7,6 +7,8 @@ import _ from 'lodash'; import * as queryDef from './query_def'; import { QueryCtrl } from 'app/plugins/sdk'; import { ElasticsearchAggregation } from './types'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; +import { CoreEvents } from 'app/types'; export class ElasticQueryCtrl extends QueryCtrl { static templateUrl = 'partials/query.editor.html'; @@ -15,7 +17,12 @@ export class ElasticQueryCtrl extends QueryCtrl { rawQueryOld: string; /** @ngInject */ - constructor($scope: any, $injector: auto.IInjectorService, private $rootScope: any, private uiSegmentSrv: any) { + constructor( + $scope: any, + $injector: auto.IInjectorService, + private $rootScope: GrafanaRootScope, + private uiSegmentSrv: any + ) { super($scope, $injector); this.esVersion = this.datasource.esVersion; @@ -50,7 +57,7 @@ export class ElasticQueryCtrl extends QueryCtrl { } this.rawQueryOld = newJson; - this.$rootScope.appEvent('elastic-query-updated'); + this.$rootScope.appEvent(CoreEvents.elasticQueryUpdated); } getCollapsedText() { diff --git a/public/app/plugins/datasource/elasticsearch/specs/datasource.test.ts b/public/app/plugins/datasource/elasticsearch/specs/datasource.test.ts index bfeb1645ded..005f490b9a6 100644 --- a/public/app/plugins/datasource/elasticsearch/specs/datasource.test.ts +++ b/public/app/plugins/datasource/elasticsearch/specs/datasource.test.ts @@ -14,7 +14,7 @@ describe('ElasticDatasource', function(this: any) { datasourceRequest: jest.fn(), }; - const $rootScope: any = { + const $rootScope = { $on: jest.fn(), appEvent: jest.fn(), }; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.ts index feb46b3585b..0b9fd39b2b2 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.ts @@ -8,6 +8,7 @@ import kbn from 'app/core/utils/kbn'; import { TemplateSrv } from 'app/features/templating/template_srv'; import { auto, IPromise } from 'angular'; import { DataFrame } from '@grafana/data'; +import { PanelEvents } from '@grafana/ui'; export interface ResultFormat { text: string; @@ -136,8 +137,8 @@ export class AzureMonitorQueryCtrl extends QueryCtrl { this.migrateApplicationInsightsKeys(); - this.panelCtrl.events.on('data-received', this.onDataReceived.bind(this), $scope); - this.panelCtrl.events.on('data-error', this.onDataError.bind(this), $scope); + this.panelCtrl.events.on(PanelEvents.dataReceived, this.onDataReceived.bind(this), $scope); + this.panelCtrl.events.on(PanelEvents.dataError, this.onDataError.bind(this), $scope); this.resultFormats = [{ text: 'Time series', value: 'time_series' }, { text: 'Table', value: 'table' }]; this.getSubscriptions(); if (this.target.queryType === 'Azure Log Analytics') { diff --git a/public/app/plugins/datasource/graphite/query_ctrl.ts b/public/app/plugins/datasource/graphite/query_ctrl.ts index f2b2e88a35b..24a203df8ec 100644 --- a/public/app/plugins/datasource/graphite/query_ctrl.ts +++ b/public/app/plugins/datasource/graphite/query_ctrl.ts @@ -7,6 +7,7 @@ import { QueryCtrl } from 'app/plugins/sdk'; import appEvents from 'app/core/app_events'; import { auto } from 'angular'; import { TemplateSrv } from 'app/features/templating/template_srv'; +import { AppEvents } from '@grafana/data'; const GRAPHITE_TAG_OPERATORS = ['=', '!=', '=~', '!=~']; const TAG_PREFIX = 'tag: '; @@ -103,7 +104,7 @@ export class GraphiteQueryCtrl extends QueryCtrl { } }) .catch((err: any) => { - appEvents.emit('alert-error', ['Error', err]); + appEvents.emit(AppEvents.alertError, ['Error', err]); }); } diff --git a/public/app/plugins/datasource/mssql/query_ctrl.ts b/public/app/plugins/datasource/mssql/query_ctrl.ts index 14a5637d9e4..cd485ab9104 100644 --- a/public/app/plugins/datasource/mssql/query_ctrl.ts +++ b/public/app/plugins/datasource/mssql/query_ctrl.ts @@ -1,6 +1,7 @@ import _ from 'lodash'; import { QueryCtrl } from 'app/plugins/sdk'; import { auto } from 'angular'; +import { PanelEvents } from '@grafana/ui'; export interface MssqlQuery { refId: string; @@ -52,8 +53,8 @@ export class MssqlQueryCtrl extends QueryCtrl { } } - this.panelCtrl.events.on('data-received', this.onDataReceived.bind(this), $scope); - this.panelCtrl.events.on('data-error', this.onDataError.bind(this), $scope); + this.panelCtrl.events.on(PanelEvents.dataReceived, this.onDataReceived.bind(this), $scope); + this.panelCtrl.events.on(PanelEvents.dataError, this.onDataError.bind(this), $scope); } onDataReceived(dataList: any) { diff --git a/public/app/plugins/datasource/mysql/query_ctrl.ts b/public/app/plugins/datasource/mysql/query_ctrl.ts index b9a51cfb486..e762a7e7fc8 100644 --- a/public/app/plugins/datasource/mysql/query_ctrl.ts +++ b/public/app/plugins/datasource/mysql/query_ctrl.ts @@ -7,6 +7,8 @@ import MysqlQuery from './mysql_query'; import sqlPart from './sql_part'; import { auto, IQService } from 'angular'; import { TemplateSrv } from 'app/features/templating/template_srv'; +import { CoreEvents } from 'app/types'; +import { PanelEvents } from '@grafana/ui'; export interface QueryMeta { sql: string; @@ -101,8 +103,8 @@ export class MysqlQueryCtrl extends QueryCtrl { this.whereAdd = this.uiSegmentSrv.newPlusButton(); this.groupAdd = this.uiSegmentSrv.newPlusButton(); - this.panelCtrl.events.on('data-received', this.onDataReceived.bind(this), $scope); - this.panelCtrl.events.on('data-error', this.onDataError.bind(this), $scope); + this.panelCtrl.events.on(PanelEvents.dataReceived, this.onDataReceived.bind(this), $scope); + this.panelCtrl.events.on(PanelEvents.dataError, this.onDataError.bind(this), $scope); } updateRawSqlAndRefresh() { @@ -158,7 +160,7 @@ export class MysqlQueryCtrl extends QueryCtrl { toggleEditorMode() { if (this.target.rawQuery) { - appEvents.emit('confirm-modal', { + appEvents.emit(CoreEvents.showConfirmModal, { title: 'Warning', text2: 'Switching to query builder may overwrite your raw SQL.', icon: 'fa-exclamation', diff --git a/public/app/plugins/datasource/postgres/query_ctrl.ts b/public/app/plugins/datasource/postgres/query_ctrl.ts index b4f0902d95b..3ebef534c91 100644 --- a/public/app/plugins/datasource/postgres/query_ctrl.ts +++ b/public/app/plugins/datasource/postgres/query_ctrl.ts @@ -7,6 +7,8 @@ import PostgresQuery from './postgres_query'; import sqlPart from './sql_part'; import { auto, IQService } from 'angular'; import { TemplateSrv } from 'app/features/templating/template_srv'; +import { CoreEvents } from 'app/types'; +import { PanelEvents } from '@grafana/ui'; export interface QueryMeta { sql: string; @@ -99,8 +101,8 @@ export class PostgresQueryCtrl extends QueryCtrl { this.whereAdd = this.uiSegmentSrv.newPlusButton(); this.groupAdd = this.uiSegmentSrv.newPlusButton(); - this.panelCtrl.events.on('data-received', this.onDataReceived.bind(this), $scope); - this.panelCtrl.events.on('data-error', this.onDataError.bind(this), $scope); + this.panelCtrl.events.on(PanelEvents.dataReceived, this.onDataReceived.bind(this), $scope); + this.panelCtrl.events.on(PanelEvents.dataError, this.onDataError.bind(this), $scope); } updateRawSqlAndRefresh() { @@ -189,7 +191,7 @@ export class PostgresQueryCtrl extends QueryCtrl { toggleEditorMode() { if (this.target.rawQuery) { - appEvents.emit('confirm-modal', { + appEvents.emit(CoreEvents.showConfirmModal, { title: 'Warning', text2: 'Switching to query builder may overwrite your raw SQL.', icon: 'fa-exclamation', diff --git a/public/app/plugins/datasource/stackdriver/components/Filter.tsx b/public/app/plugins/datasource/stackdriver/components/Filter.tsx index 73d75b06462..ff155a95bdb 100644 --- a/public/app/plugins/datasource/stackdriver/components/Filter.tsx +++ b/public/app/plugins/datasource/stackdriver/components/Filter.tsx @@ -7,6 +7,7 @@ import { getAngularLoader, AngularComponent } from '@grafana/runtime'; import { TemplateSrv } from 'app/features/templating/template_srv'; import StackdriverDatasource from '../datasource'; import '../query_filter_ctrl'; +import { AppEvents } from '@grafana/data'; export interface Props { filtersChanged: (filters: string[]) => void; @@ -102,7 +103,7 @@ export class Filter extends React.Component { } resolve(); } catch (error) { - appEvents.emit('alert-error', ['Error', 'Error loading metric labels for ' + this.props.metricType]); + appEvents.emit(AppEvents.alertError, ['Error', 'Error loading metric labels for ' + this.props.metricType]); scope.labelData = labelData; resolve(); } diff --git a/public/app/plugins/datasource/stackdriver/components/Metrics.tsx b/public/app/plugins/datasource/stackdriver/components/Metrics.tsx index 269b50d60ff..f1cb3c07f27 100644 --- a/public/app/plugins/datasource/stackdriver/components/Metrics.tsx +++ b/public/app/plugins/datasource/stackdriver/components/Metrics.tsx @@ -6,6 +6,7 @@ import appEvents from 'app/core/app_events'; import { MetricDescriptor } from '../types'; import { MetricSelect } from 'app/core/components/Select/MetricSelect'; import { TemplateSrv } from 'app/features/templating/template_srv'; +import { CoreEvents } from 'app/types'; export interface Props { onChange: (metricDescriptor: MetricDescriptor) => void; @@ -58,7 +59,7 @@ export class Metrics extends React.Component { } resolve(this.state.defaultProject); } catch (error) { - appEvents.emit('ds-request-error', error); + appEvents.emit(CoreEvents.dsRequestError, error); reject(); } }); diff --git a/public/app/plugins/datasource/stackdriver/components/QueryEditor.tsx b/public/app/plugins/datasource/stackdriver/components/QueryEditor.tsx index a36a9f42631..1b32dcfebe1 100644 --- a/public/app/plugins/datasource/stackdriver/components/QueryEditor.tsx +++ b/public/app/plugins/datasource/stackdriver/components/QueryEditor.tsx @@ -14,6 +14,7 @@ import { StackdriverQuery, MetricDescriptor } from '../types'; import { getAlignmentPickerData } from '../functions'; import StackdriverDatasource from '../datasource'; import { TimeSeries, SelectableValue } from '@grafana/data'; +import { PanelEvents } from '@grafana/ui'; export interface Props { onQueryChange: (target: StackdriverQuery) => void; @@ -56,8 +57,8 @@ export class QueryEditor extends React.Component { componentDidMount() { const { events, target, templateSrv } = this.props; - events.on('data-received', this.onDataReceived.bind(this)); - events.on('data-error', this.onDataError.bind(this)); + events.on(PanelEvents.dataReceived, this.onDataReceived.bind(this)); + events.on(PanelEvents.dataError, this.onDataError.bind(this)); const { perSeriesAligner, alignOptions } = getAlignmentPickerData(target, templateSrv); this.setState({ ...this.props.target, @@ -67,8 +68,8 @@ export class QueryEditor extends React.Component { } componentWillUnmount() { - this.props.events.off('data-received', this.onDataReceived); - this.props.events.off('data-error', this.onDataError); + this.props.events.off(PanelEvents.dataReceived, this.onDataReceived); + this.props.events.off(PanelEvents.dataError, this.onDataError); } onDataReceived(dataList: TimeSeries[]) { diff --git a/public/app/plugins/datasource/stackdriver/datasource.ts b/public/app/plugins/datasource/stackdriver/datasource.ts index a803d717d6a..5a4f3a78554 100644 --- a/public/app/plugins/datasource/stackdriver/datasource.ts +++ b/public/app/plugins/datasource/stackdriver/datasource.ts @@ -8,6 +8,7 @@ import { ScopedVars } from '@grafana/data'; import { BackendSrv } from 'app/core/services/backend_srv'; import { TemplateSrv } from 'app/features/templating/template_srv'; import { TimeSrv } from 'app/features/dashboard/services/TimeSrv'; +import { CoreEvents } from 'app/types'; export default class StackdriverDatasource extends DataSourceApi { url: string; @@ -287,7 +288,7 @@ export default class StackdriverDatasource extends DataSourceApi { ); return; } - appEvents.emit('alert-warning', ['Unknown Dashboard: ' + anno.dashboardId]); + appEvents.emit(AppEvents.alertWarning, ['Unknown Dashboard: ' + anno.dashboardId]); }); }; diff --git a/public/app/plugins/panel/dashlist/module.ts b/public/app/plugins/panel/dashlist/module.ts index 933f8f95a9b..2f5deea7ace 100644 --- a/public/app/plugins/panel/dashlist/module.ts +++ b/public/app/plugins/panel/dashlist/module.ts @@ -4,6 +4,7 @@ import impressionSrv from 'app/core/services/impression_srv'; import { auto } from 'angular'; import { BackendSrv } from 'app/core/services/backend_srv'; import { DashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; +import { PanelEvents } from '@grafana/ui'; class DashListCtrl extends PanelCtrl { static templateUrl = 'module.html'; @@ -38,8 +39,8 @@ class DashListCtrl extends PanelCtrl { delete this.panel.tag; } - this.events.on('refresh', this.onRefresh.bind(this)); - this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); + this.events.on(PanelEvents.refresh, this.onRefresh.bind(this)); + this.events.on(PanelEvents.editModeInitialized, this.onInitEditMode.bind(this)); this.groups = [ { list: [], show: false, header: 'Starred dashboards' }, diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 412346e2f85..dd3ea38883a 100644 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -31,6 +31,7 @@ import { ContextMenuItem, getDisplayProcessor, getFlotPairsConstant, + PanelEvents, } from '@grafana/ui'; import { provideTheme, getCurrentTheme } from 'app/core/utils/ConfigProvider'; import { toUtc, LinkModelSupplier, DataFrameView } from '@grafana/data'; @@ -38,6 +39,7 @@ import { GraphContextMenuCtrl } from './GraphContextMenuCtrl'; import { TimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { ContextSrv } from 'app/core/services/context_srv'; import { getFieldLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers'; +import { CoreEvents } from 'app/types'; const LegendWithThemeProvider = provideTheme(Legend); @@ -74,12 +76,12 @@ class GraphElement { }); // panel events - this.ctrl.events.on('panel-teardown', this.onPanelTeardown.bind(this)); - this.ctrl.events.on('render', this.onRender.bind(this)); + this.ctrl.events.on(PanelEvents.panelTeardown, this.onPanelTeardown.bind(this)); + this.ctrl.events.on(PanelEvents.render, this.onRender.bind(this)); // global events - appEvents.on('graph-hover', this.onGraphHover.bind(this), scope); - appEvents.on('graph-hover-clear', this.onGraphHoverClear.bind(this), scope); + appEvents.on(CoreEvents.graphHover, this.onGraphHover.bind(this), scope); + appEvents.on(CoreEvents.graphHoverClear, this.onGraphHoverClear.bind(this), scope); this.elem.bind('plotselected', this.onPlotSelected.bind(this)); this.elem.bind('plotclick', this.onPlotClick.bind(this)); diff --git a/public/app/plugins/panel/graph/graph_tooltip.ts b/public/app/plugins/panel/graph/graph_tooltip.ts index ebf694b693c..0c2dec69c1e 100644 --- a/public/app/plugins/panel/graph/graph_tooltip.ts +++ b/public/app/plugins/panel/graph/graph_tooltip.ts @@ -1,5 +1,6 @@ import $ from 'jquery'; import { appEvents } from 'app/core/core'; +import { CoreEvents } from 'app/types'; export default function GraphTooltip(this: any, elem: any, dashboard: any, scope: any, getSeriesFn: any) { const self = this; @@ -155,7 +156,7 @@ export default function GraphTooltip(this: any, elem: any, dashboard: any, scope plot.unhighlight(); } } - appEvents.emit('graph-hover-clear'); + appEvents.emit(CoreEvents.graphHoverClear); }); elem.bind('plothover', (event: any, pos: { panelRelY: number; pageY: number }, item: any) => { @@ -163,11 +164,11 @@ export default function GraphTooltip(this: any, elem: any, dashboard: any, scope // broadcast to other graph panels that we are hovering! pos.panelRelY = (pos.pageY - elem.offset().top) / elem.height(); - appEvents.emit('graph-hover', { pos: pos, panel: panel }); + appEvents.emit(CoreEvents.graphHover, { pos: pos, panel: panel }); }); elem.bind('plotclick', (event: any, pos: any, item: any) => { - appEvents.emit('graph-click', { pos: pos, panel: panel, item: item }); + appEvents.emit(CoreEvents.graphClicked, { pos: pos, panel: panel, item: item }); }); this.clear = (plot: { clearCrosshair: () => void; unhighlight: () => void }) => { diff --git a/public/app/plugins/panel/graph/module.ts b/public/app/plugins/panel/graph/module.ts index 571d3573198..4c513161b33 100644 --- a/public/app/plugins/panel/graph/module.ts +++ b/public/app/plugins/panel/graph/module.ts @@ -11,14 +11,16 @@ import { DataProcessor } from './data_processor'; import { axesEditorComponent } from './axes_editor'; import config from 'app/core/config'; import TimeSeries from 'app/core/time_series2'; -import { DataFrame, DataLink, DateTimeInput } from '@grafana/data'; -import { getColorFromHexRgbOrName, VariableSuggestion } from '@grafana/ui'; +import { getColorFromHexRgbOrName, VariableSuggestion, PanelEvents } from '@grafana/ui'; import { getProcessedDataFrames } from 'app/features/dashboard/state/runRequest'; +import { DataFrame, DataLink, DateTimeInput } from '@grafana/data'; + import { GraphContextMenuCtrl } from './GraphContextMenuCtrl'; import { getDataLinksVariableSuggestions } from 'app/features/panel/panellinks/link_srv'; import { auto } from 'angular'; import { AnnotationsSrv } from 'app/features/annotations/all'; +import { CoreEvents } from 'app/types'; class GraphCtrl extends MetricsPanelCtrl { static template = template; @@ -146,12 +148,12 @@ class GraphCtrl extends MetricsPanelCtrl { this.processor = new DataProcessor(this.panel); this.contextMenuCtrl = new GraphContextMenuCtrl($scope); - this.events.on('render', this.onRender.bind(this)); - this.events.on('data-frames-received', this.onDataFramesReceived.bind(this)); - this.events.on('data-error', this.onDataError.bind(this)); - this.events.on('data-snapshot-load', this.onDataSnapshotLoad.bind(this)); - this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); - this.events.on('init-panel-actions', this.onInitPanelActions.bind(this)); + this.events.on(PanelEvents.render, this.onRender.bind(this)); + this.events.on(CoreEvents.dataFramesReceived, this.onDataFramesReceived.bind(this)); + this.events.on(PanelEvents.dataReceived, this.onDataError.bind(this)); + this.events.on(PanelEvents.dataSnapshotLoad, this.onDataSnapshotLoad.bind(this)); + this.events.on(PanelEvents.editModeInitialized, this.onInitEditMode.bind(this)); + this.events.on(PanelEvents.initPanelActions, this.onInitPanelActions.bind(this)); this.onDataLinksChange = this.onDataLinksChange.bind(this); } @@ -189,7 +191,7 @@ class GraphCtrl extends MetricsPanelCtrl { } zoomOut(evt: any) { - this.publishAppEvent('zoom-out', 2); + this.publishAppEvent(CoreEvents.zoomOut, 2); } onDataSnapshotLoad(snapshotData: any) { @@ -325,7 +327,7 @@ class GraphCtrl extends MetricsPanelCtrl { exportCsv() { const scope = this.$scope.$new(true); scope.seriesList = this.seriesList; - this.publishAppEvent('show-modal', { + this.publishAppEvent(CoreEvents.showModal, { templateHtml: '', scope, modalClass: 'modal--narrow', diff --git a/public/app/plugins/panel/graph/threshold_manager.ts b/public/app/plugins/panel/graph/threshold_manager.ts index 89adaa6dc2c..9c5634b05a3 100644 --- a/public/app/plugins/panel/graph/threshold_manager.ts +++ b/public/app/plugins/panel/graph/threshold_manager.ts @@ -2,6 +2,7 @@ import 'vendor/flot/jquery.flot'; import $ from 'jquery'; import _ from 'lodash'; import { getColorFromHexRgbOrName } from '@grafana/ui'; +import { CoreEvents } from 'app/types'; import { PanelCtrl } from 'app/features/panel/panel_ctrl'; export class ThresholdManager { @@ -65,7 +66,7 @@ export class ThresholdManager { // trigger digest and render panelCtrl.$scope.$apply(() => { panelCtrl.render(); - panelCtrl.events.emit('threshold-changed', { + panelCtrl.events.emit(CoreEvents.thresholdChanged, { threshold: model, handleIndex: handleIndex, }); diff --git a/public/app/plugins/panel/heatmap/color_legend.ts b/public/app/plugins/panel/heatmap/color_legend.ts index be1823807d5..d2bb775cbb6 100644 --- a/public/app/plugins/panel/heatmap/color_legend.ts +++ b/public/app/plugins/panel/heatmap/color_legend.ts @@ -5,7 +5,7 @@ import { contextSrv } from 'app/core/core'; import { tickStep } from 'app/core/utils/ticks'; import { getColorScale, getOpacityScale } from './color_scale'; import coreModule from 'app/core/core_module'; -import { GrafanaThemeType, getColorFromHexRgbOrName } from '@grafana/ui'; +import { PanelEvents, GrafanaThemeType, getColorFromHexRgbOrName } from '@grafana/ui'; const LEGEND_HEIGHT_PX = 6; const LEGEND_WIDTH_PX = 100; @@ -27,7 +27,7 @@ coreModule.directive('colorLegend', () => { render(); - ctrl.events.on('render', () => { + ctrl.events.on(PanelEvents.render, () => { render(); }); @@ -62,7 +62,7 @@ coreModule.directive('heatmapLegend', () => { const panel = scope.ctrl.panel; render(); - ctrl.events.on('render', () => { + ctrl.events.on(PanelEvents.render, () => { render(); }); diff --git a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts index 744988ba405..91238a0f805 100644 --- a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts +++ b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts @@ -13,10 +13,11 @@ import { sortSeriesByLabel, } from './heatmap_data_converter'; import { auto } from 'angular'; -import { LegacyResponseData } from '@grafana/ui'; +import { LegacyResponseData, PanelEvents } from '@grafana/ui'; import { getProcessedDataFrames } from 'app/features/dashboard/state/runRequest'; -import { DataFrame } from '@grafana/data'; import { DataProcessor } from '../graph/data_processor'; +import { DataFrame } from '@grafana/data'; +import { CoreEvents } from 'app/types'; const X_BUCKET_NUMBER_DEFAULT = 30; const Y_BUCKET_NUMBER_DEFAULT = 10; @@ -142,11 +143,11 @@ export class HeatmapCtrl extends MetricsPanelCtrl { }); // Bind grafana panel events - this.events.on('render', this.onRender.bind(this)); - this.events.on('data-frames-received', this.onDataFramesReceived.bind(this)); - this.events.on('data-error', this.onDataError.bind(this)); - this.events.on('data-snapshot-load', this.onSnapshotLoad.bind(this)); - this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); + this.events.on(PanelEvents.render, this.onRender.bind(this)); + this.events.on(CoreEvents.dataFramesReceived, this.onDataFramesReceived.bind(this)); + this.events.on(PanelEvents.dataError, this.onDataError.bind(this)); + this.events.on(PanelEvents.dataSnapshotLoad, this.onSnapshotLoad.bind(this)); + this.events.on(PanelEvents.editModeInitialized, this.onInitEditMode.bind(this)); this.onCardColorChange = this.onCardColorChange.bind(this); } @@ -158,7 +159,7 @@ export class HeatmapCtrl extends MetricsPanelCtrl { } zoomOut(evt: any) { - this.publishAppEvent('zoom-out', 2); + this.publishAppEvent(CoreEvents.zoomOut, 2); } onRender() { diff --git a/public/app/plugins/panel/heatmap/rendering.ts b/public/app/plugins/panel/heatmap/rendering.ts index 977dc4bec04..ba83a967f74 100644 --- a/public/app/plugins/panel/heatmap/rendering.ts +++ b/public/app/plugins/panel/heatmap/rendering.ts @@ -6,8 +6,9 @@ import * as ticksUtils from 'app/core/utils/ticks'; import { HeatmapTooltip } from './heatmap_tooltip'; import { mergeZeroBuckets } from './heatmap_data_converter'; import { getColorScale, getOpacityScale } from './color_scale'; -import { GrafanaThemeType, getColorFromHexRgbOrName, getValueFormat } from '@grafana/ui'; +import { PanelEvents, GrafanaThemeType, getColorFromHexRgbOrName, getValueFormat } from '@grafana/ui'; import { toUtc } from '@grafana/data'; +import { CoreEvents } from 'app/types'; const MIN_CARD_SIZE = 1, CARD_PADDING = 1, @@ -66,7 +67,7 @@ export class HeatmapRenderer { this.margin = { left: 25, right: 15, top: 10, bottom: 20 }; this.dataRangeWidingFactor = DATA_RANGE_WIDING_FACTOR; - this.ctrl.events.on('render', this.onRender.bind(this)); + this.ctrl.events.on(PanelEvents.render, this.onRender.bind(this)); this.ctrl.tickValueFormatter = this.tickValueFormatter.bind(this); @@ -75,9 +76,9 @@ export class HeatmapRenderer { ///////////////////////////// // Shared crosshair and tooltip - appEvents.on('graph-hover', this.onGraphHover.bind(this), this.scope); + appEvents.on(CoreEvents.graphHover, this.onGraphHover.bind(this), this.scope); - appEvents.on('graph-hover-clear', this.onGraphHoverClear.bind(this), this.scope); + appEvents.on(CoreEvents.graphHoverClear, this.onGraphHoverClear.bind(this), this.scope); // Register selection listeners this.$heatmap.on('mousedown', this.onMouseDown.bind(this)); @@ -722,7 +723,7 @@ export class HeatmapRenderer { } onMouseLeave() { - appEvents.emit('graph-hover-clear'); + appEvents.emit(CoreEvents.graphHoverClear); this.clearCrosshair(); } @@ -768,7 +769,7 @@ export class HeatmapRenderer { // Set minimum offset to prevent showing legend from another panel pos.panelRelY = Math.max(pos.offset.y / this.height, 0.001); // broadcast to other graph panels that we are hovering - appEvents.emit('graph-hover', { pos: pos, panel: this.panel }); + appEvents.emit(CoreEvents.graphHover, { pos: pos, panel: this.panel }); } limitSelection(x2: number) { diff --git a/public/app/plugins/panel/pluginlist/module.ts b/public/app/plugins/panel/pluginlist/module.ts index c6ca83f248a..6110c264f60 100644 --- a/public/app/plugins/panel/pluginlist/module.ts +++ b/public/app/plugins/panel/pluginlist/module.ts @@ -2,7 +2,9 @@ import _ from 'lodash'; import { PanelCtrl } from '../../../features/panel/panel_ctrl'; import { auto } from 'angular'; import { BackendSrv } from '@grafana/runtime'; +import { PanelEvents } from '@grafana/ui'; import { ContextSrv } from '../../../core/services/context_srv'; +import { CoreEvents } from 'app/types'; class PluginListCtrl extends PanelCtrl { static templateUrl = 'module.html'; @@ -22,7 +24,7 @@ class PluginListCtrl extends PanelCtrl { _.defaults(this.panel, this.panelDefaults); this.isAdmin = contextSrv.hasRole('Admin'); - this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); + this.events.on(PanelEvents.editModeInitialized, this.onInitEditMode.bind(this)); this.pluginList = []; this.viewModel = [ { header: 'Installed Apps', list: [], type: 'app' }, @@ -51,7 +53,7 @@ class PluginListCtrl extends PanelCtrl { const modalScope = this.$scope.$new(true); modalScope.plugin = plugin; - this.publishAppEvent('show-modal', { + this.publishAppEvent(CoreEvents.showModal, { src: 'public/app/features/plugins/partials/update_instructions.html', scope: modalScope, }); diff --git a/public/app/plugins/panel/singlestat/module.ts b/public/app/plugins/panel/singlestat/module.ts index 62f6ef17ee9..eab1f74349d 100644 --- a/public/app/plugins/panel/singlestat/module.ts +++ b/public/app/plugins/panel/singlestat/module.ts @@ -1,19 +1,10 @@ import _ from 'lodash'; +import { auto } from 'angular'; import $ from 'jquery'; import 'vendor/flot/jquery.flot'; import 'vendor/flot/jquery.flot.gauge'; import 'app/features/panel/panellinks/link_srv'; -import { - convertOldAngulrValueMapping, - getColorFromHexRgbOrName, - getDisplayProcessor, - getFlotPairs, - LegacyResponseData, -} from '@grafana/ui'; -import kbn from 'app/core/utils/kbn'; -import config from 'app/core/config'; -import { MetricsPanelCtrl } from 'app/plugins/sdk'; import { DataFrame, DisplayValue, @@ -26,7 +17,20 @@ import { reduceField, ReducerID, } from '@grafana/data'; -import { auto } from 'angular'; + +import { + LegacyResponseData, + getFlotPairs, + getDisplayProcessor, + convertOldAngulrValueMapping, + getColorFromHexRgbOrName, + PanelEvents, +} from '@grafana/ui'; + +import { CoreEvents } from 'app/types'; +import kbn from 'app/core/utils/kbn'; +import config from 'app/core/config'; +import { MetricsPanelCtrl } from 'app/plugins/sdk'; import { LinkSrv } from 'app/features/panel/panellinks/link_srv'; import { getProcessedDataFrames } from 'app/features/dashboard/state/runRequest'; @@ -118,10 +122,10 @@ class SingleStatCtrl extends MetricsPanelCtrl { super($scope, $injector); _.defaults(this.panel, this.panelDefaults); - this.events.on('data-frames-received', this.onFramesReceived.bind(this)); - this.events.on('data-error', this.onDataError.bind(this)); - this.events.on('data-snapshot-load', this.onSnapshotLoad.bind(this)); - this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); + this.events.on(CoreEvents.dataFramesReceived, this.onFramesReceived.bind(this)); + this.events.on(PanelEvents.dataError, this.onDataError.bind(this)); + this.events.on(PanelEvents.dataSnapshotLoad, this.onSnapshotLoad.bind(this)); + this.events.on(PanelEvents.editModeInitialized, this.onInitEditMode.bind(this)); this.useDataFrames = true; @@ -658,7 +662,7 @@ class SingleStatCtrl extends MetricsPanelCtrl { hookupDrilldownLinkTooltip(); - this.events.on('render', () => { + this.events.on(PanelEvents.render, () => { render(); ctrl.renderingCompleted(); }); diff --git a/public/app/plugins/panel/table/module.ts b/public/app/plugins/panel/table/module.ts index b8d2b1e951a..f09ffbadf9a 100644 --- a/public/app/plugins/panel/table/module.ts +++ b/public/app/plugins/panel/table/module.ts @@ -8,6 +8,8 @@ import { columnOptionsTab } from './column_options'; import { TableRenderer } from './renderer'; import { isTableData } from '@grafana/data'; import { TemplateSrv } from 'app/features/templating/template_srv'; +import { PanelEvents } from '@grafana/ui'; +import { CoreEvents } from 'app/types'; class TablePanelCtrl extends MetricsPanelCtrl { static templateUrl = 'module.html'; @@ -68,11 +70,11 @@ class TablePanelCtrl extends MetricsPanelCtrl { _.defaults(this.panel, this.panelDefaults); - this.events.on('data-received', this.onDataReceived.bind(this)); - this.events.on('data-error', this.onDataError.bind(this)); - this.events.on('data-snapshot-load', this.onDataReceived.bind(this)); - this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); - this.events.on('init-panel-actions', this.onInitPanelActions.bind(this)); + this.events.on(PanelEvents.dataReceived, this.onDataReceived.bind(this)); + this.events.on(PanelEvents.dataError, this.onDataError.bind(this)); + this.events.on(PanelEvents.dataSnapshotLoad, this.onDataReceived.bind(this)); + this.events.on(PanelEvents.editModeInitialized, this.onInitEditMode.bind(this)); + this.events.on(PanelEvents.initPanelActions, this.onInitPanelActions.bind(this)); } onInitEditMode() { @@ -172,7 +174,7 @@ class TablePanelCtrl extends MetricsPanelCtrl { const scope = this.$scope.$new(true); scope.tableData = this.renderer.render_values(); scope.panel = 'table'; - this.publishAppEvent('show-modal', { + this.publishAppEvent(CoreEvents.showModal, { templateHtml: '', scope, modalClass: 'modal--narrow', @@ -272,7 +274,7 @@ class TablePanelCtrl extends MetricsPanelCtrl { unbindDestroy(); }); - ctrl.events.on('render', (renderData: any) => { + ctrl.events.on(PanelEvents.render, (renderData: any) => { data = renderData || data; if (data) { renderPanel(); diff --git a/public/app/plugins/panel/text/module.ts b/public/app/plugins/panel/text/module.ts index 5a7dba332f2..9aba417a8cb 100644 --- a/public/app/plugins/panel/text/module.ts +++ b/public/app/plugins/panel/text/module.ts @@ -5,6 +5,7 @@ import { sanitize, escapeHtml } from 'app/core/utils/text'; import config from 'app/core/config'; import { auto, ISCEService } from 'angular'; import { TemplateSrv } from 'app/features/templating/template_srv'; +import { PanelEvents } from '@grafana/ui'; import { renderMarkdown } from '@grafana/data'; const defaultContent = ` @@ -38,9 +39,9 @@ export class TextPanelCtrl extends PanelCtrl { _.defaults(this.panel, this.panelDefaults); - this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); - this.events.on('refresh', this.onRefresh.bind(this)); - this.events.on('render', this.onRender.bind(this)); + this.events.on(PanelEvents.editModeInitialized, this.onInitEditMode.bind(this)); + this.events.on(PanelEvents.refresh, this.onRefresh.bind(this)); + this.events.on(PanelEvents.render, this.onRender.bind(this)); const renderWhenChanged = (scope: any) => { const { panel } = scope.ctrl; diff --git a/public/app/routes/GrafanaCtrl.ts b/public/app/routes/GrafanaCtrl.ts index 6dd9f8d3875..997c9359915 100644 --- a/public/app/routes/GrafanaCtrl.ts +++ b/public/app/routes/GrafanaCtrl.ts @@ -21,20 +21,23 @@ import { LocationUpdate, setLocationSrv } from '@grafana/runtime'; import { updateLocation } from 'app/core/actions'; // Types -import { KioskUrlValue } from 'app/types'; +import { KioskUrlValue, CoreEvents, AppEventEmitter, AppEventConsumer } from 'app/types'; import { setLinkSrv, LinkSrv } from 'app/features/panel/panellinks/link_srv'; import { UtilSrv } from 'app/core/services/util_srv'; import { ContextSrv } from 'app/core/services/context_srv'; import { BridgeSrv } from 'app/core/services/bridge_srv'; import { PlaylistSrv } from 'app/features/playlist/playlist_srv'; -import { ILocationService, ITimeoutService, IRootScopeService } from 'angular'; +import { ILocationService, ITimeoutService, IRootScopeService, IAngularEvent } from 'angular'; +import { AppEvent, AppEvents } from '@grafana/data'; + +export type GrafanaRootScope = IRootScopeService & AppEventEmitter & AppEventConsumer & { colors: string[] }; export class GrafanaCtrl { /** @ngInject */ constructor( $scope: any, utilSrv: UtilSrv, - $rootScope: any, + $rootScope: GrafanaRootScope, contextSrv: ContextSrv, bridgeSrv: BridgeSrv, backendSrv: BackendSrv, @@ -70,8 +73,18 @@ export class GrafanaCtrl { $rootScope.colors = colors; - $rootScope.onAppEvent = function(name: string, callback: () => void, localScope: any) { - const unbind = $rootScope.$on(name, callback); + $rootScope.onAppEvent = function( + event: AppEvent | string, + callback: (event: IAngularEvent, ...args: any[]) => void, + localScope?: any + ) { + let unbind; + if (typeof event === 'string') { + unbind = $rootScope.$on(event, callback); + } else { + unbind = $rootScope.$on(event.name, callback); + } + let callerScope = this; if (callerScope.$id === 1 && !localScope) { console.log('warning rootScope onAppEvent called without localscope'); @@ -82,9 +95,14 @@ export class GrafanaCtrl { callerScope.$on('$destroy', unbind); }; - $rootScope.appEvent = (name: string, payload: any) => { - $rootScope.$emit(name, payload); - appEvents.emit(name, payload); + $rootScope.appEvent = (event: AppEvent | string, payload?: T | any) => { + if (typeof event === 'string') { + $rootScope.$emit(event, payload); + appEvents.emit(event, payload); + } else { + $rootScope.$emit(event.name, payload); + appEvents.emit(event, payload); + } }; $scope.init(); @@ -121,7 +139,7 @@ export function grafanaAppDirective( return { restrict: 'E', controller: GrafanaCtrl, - link: (scope: any, elem: JQuery) => { + link: (scope: IRootScopeService & AppEventEmitter, elem: JQuery) => { const body = $('body'); // see https://github.com/zenorocha/clipboard.js/issues/155 @@ -129,19 +147,19 @@ export function grafanaAppDirective( $('.preloader').remove(); - appEvents.on('toggle-sidemenu-mobile', () => { + appEvents.on(CoreEvents.toggleSidemenuMobile, () => { body.toggleClass('sidemenu-open--xs'); }); - appEvents.on('toggle-sidemenu-hidden', () => { + appEvents.on(CoreEvents.toggleSidemenuHidden, () => { body.toggleClass('sidemenu-hidden'); }); - appEvents.on('playlist-started', () => { + appEvents.on(CoreEvents.playlistStarted, () => { elem.toggleClass('view-mode--playlist', true); }); - appEvents.on('playlist-stopped', () => { + appEvents.on(CoreEvents.playlistStopped, () => { elem.toggleClass('view-mode--playlist', false); }); @@ -178,11 +196,11 @@ export function grafanaAppDirective( drop.destroy(); } - appEvents.emit('hide-dash-search'); + appEvents.emit(CoreEvents.hideDashSearch); }); // handle kiosk mode - appEvents.on('toggle-kiosk-mode', (options: { exit?: boolean }) => { + appEvents.on(CoreEvents.toggleKioskMode, (options: { exit?: boolean }) => { const search: { kiosk?: KioskUrlValue } = $location.search(); if (options && options.exit) { @@ -192,7 +210,7 @@ export function grafanaAppDirective( switch (search.kiosk) { case 'tv': { search.kiosk = true; - appEvents.emit('alert-success', ['Press ESC to exit Kiosk mode']); + appEvents.emit(AppEvents.alertSuccess, ['Press ESC to exit Kiosk mode']); break; } case '1': @@ -248,7 +266,7 @@ export function grafanaAppDirective( // check every 2 seconds setInterval(checkForInActiveUser, 2000); - appEvents.on('toggle-view-mode', () => { + appEvents.on(CoreEvents.toggleViewMode, () => { lastActivity = 0; checkForInActiveUser(); }); @@ -278,7 +296,7 @@ export function grafanaAppDirective( if (body.find('.search-container').length > 0) { if (target.parents('.search-results-container, .search-field-wrapper').length === 0) { scope.$apply(() => { - scope.appEvent('hide-dash-search'); + scope.appEvent(CoreEvents.hideDashSearch); }); } } diff --git a/public/app/routes/ReactContainer.tsx b/public/app/routes/ReactContainer.tsx index 7cad6918133..a6eb2227510 100644 --- a/public/app/routes/ReactContainer.tsx +++ b/public/app/routes/ReactContainer.tsx @@ -9,6 +9,7 @@ import { store } from 'app/store/store'; import { ContextSrv } from 'app/core/services/context_srv'; import { provideTheme } from 'app/core/utils/ConfigProvider'; import { ErrorBoundaryAlert } from '@grafana/ui'; +import { GrafanaRootScope } from './GrafanaCtrl'; function WrapInProvider(store: any, Component: any, props: any) { return ( @@ -21,7 +22,13 @@ function WrapInProvider(store: any, Component: any, props: any) { } /** @ngInject */ -export function reactContainer($route: any, $location: any, $injector: any, $rootScope: any, contextSrv: ContextSrv) { +export function reactContainer( + $route: any, + $location: any, + $injector: any, + $rootScope: GrafanaRootScope, + contextSrv: ContextSrv +) { return { restrict: 'E', template: '', diff --git a/public/app/routes/dashboard_loaders.ts b/public/app/routes/dashboard_loaders.ts index 7eaec7c560d..d44ee1a6217 100644 --- a/public/app/routes/dashboard_loaders.ts +++ b/public/app/routes/dashboard_loaders.ts @@ -4,18 +4,19 @@ import { UrlQueryMap } from '@grafana/runtime'; import { DashboardLoaderSrv } from 'app/features/dashboard/services/DashboardLoaderSrv'; import { BackendSrv } from 'app/core/services/backend_srv'; import { ILocationService } from 'angular'; +import { Scope, CoreEvents, AppEventEmitter } from 'app/types'; export class LoadDashboardCtrl { /** @ngInject */ constructor( - $scope: any, + $scope: Scope & AppEventEmitter, $routeParams: UrlQueryMap, dashboardLoaderSrv: DashboardLoaderSrv, backendSrv: BackendSrv, $location: ILocationService, $browser: any ) { - $scope.appEvent('dashboard-fetch-start'); + $scope.appEvent(CoreEvents.dashboardFetchStart); if (!$routeParams.uid && !$routeParams.slug) { backendSrv.get('/api/dashboards/home').then((homeDash: { redirectUri: string; meta: any }) => { diff --git a/public/app/types/angular.ts b/public/app/types/angular.ts new file mode 100644 index 00000000000..ad20a89773c --- /dev/null +++ b/public/app/types/angular.ts @@ -0,0 +1 @@ +export type Scope = { [key: string]: any }; diff --git a/public/app/types/appEvent.ts b/public/app/types/appEvent.ts new file mode 100644 index 00000000000..1dc8433a53b --- /dev/null +++ b/public/app/types/appEvent.ts @@ -0,0 +1,19 @@ +import { IAngularEvent } from 'angular'; +import { AppEvent } from '@grafana/data'; + +export interface AppEventEmitter { + /** + * DEPRECATED. + */ + appEvent(name: string, data?: any): void; + + appEvent(event: AppEvent): void; + // This overload allows for omitting the appEvent payload if the payload's type only contains optional properties + appEvent extends T ? Partial : never>(event: AppEvent): void; + appEvent(event: AppEvent, payload: T): void; +} + +export interface AppEventConsumer { + onAppEvent(name: string, callback: (event: IAngularEvent, ...args: any[]) => void, localScope?: any): void; + onAppEvent(event: AppEvent, callback: (event: IAngularEvent, ...args: any[]) => void, localScope?: any): void; +} diff --git a/public/app/types/events.ts b/public/app/types/events.ts new file mode 100644 index 00000000000..7020ecff0de --- /dev/null +++ b/public/app/types/events.ts @@ -0,0 +1,143 @@ +import { DataFrame, TimeRange } from '@grafana/data'; +import { IHttpResponse } from 'angular'; +import { DashboardModel } from 'app/features/dashboard/state'; +import { eventFactory } from '@grafana/data'; + +/** + * Event Payloads + */ + +export interface ShowDashSearchPayload { + query?: string; +} + +export interface LocationChangePayload { + href: string; +} + +export interface ShowModalPayload { + model?: any; + modalClass?: string; + src?: string; + templateHtml?: string; + backdrop?: any; + scope?: any; +} + +export interface ShowConfirmModalPayload { + title?: string; + text?: string; + text2?: string; + text2htmlBind?: boolean; + confirmText?: string; + altActionText?: string; + yesText?: string; + noText?: string; + icon?: string; + + onConfirm?: () => void; + onAltAction?: () => void; +} + +type DataSourceResponsePayload = IHttpResponse; + +export interface SaveDashboardPayload { + overwrite?: boolean; + folderId?: number; + makeEditable?: boolean; +} + +export interface GraphHoverPayload { + pos: any; + panel: { + id: number; + }; +} + +export interface ToggleKioskModePayload { + exit?: boolean; +} + +export interface GraphClickedPayload { + pos: any; + panel: any; + item: any; +} + +export interface ThresholdChangedPayload { + threshold: any; + handleIndex: any; +} + +export interface DashScrollPayload { + restore?: boolean; + animate?: boolean; + pos?: number; +} + +/** + * Events + */ + +export const showDashSearch = eventFactory('show-dash-search'); +export const hideDashSearch = eventFactory('hide-dash-search'); +export const hideDashEditor = eventFactory('hide-dash-editor'); +export const dashScroll = eventFactory('dash-scroll'); +export const dashLinksUpdated = eventFactory('dash-links-updated'); +export const saveDashboard = eventFactory('save-dashboard'); +export const dashboardFetchStart = eventFactory('dashboard-fetch-start'); +export const dashboardSaved = eventFactory('dashboard-saved'); +export const removePanel = eventFactory('remove-panel'); + +export const searchQuery = eventFactory('search-query'); + +export const locationChange = eventFactory('location-change'); + +export const timepickerOpen = eventFactory('timepickerOpen'); +export const timepickerClosed = eventFactory('timepickerClosed'); + +export const showModal = eventFactory('show-modal'); +export const showConfirmModal = eventFactory('confirm-modal'); +export const hideModal = eventFactory('hide-modal'); + +export const dsRequestResponse = eventFactory('ds-request-response'); +export const dsRequestError = eventFactory('ds-request-error'); + +export const graphHover = eventFactory('graph-hover'); +export const graphHoverClear = eventFactory('graph-hover-clear'); + +export const toggleSidemenuMobile = eventFactory('toggle-sidemenu-mobile'); +export const toggleSidemenuHidden = eventFactory('toggle-sidemenu-hidden'); + +export const playlistStarted = eventFactory('playlist-started'); +export const playlistStopped = eventFactory('playlist-stopped'); + +export const toggleKioskMode = eventFactory('toggle-kiosk-mode'); +export const toggleViewMode = eventFactory('toggle-view-mode'); + +export const timeRangeUpdated = eventFactory('time-range-updated'); + +export const repeatsProcessed = eventFactory('repeats-processed'); +export const rowExpanded = eventFactory('row-expanded'); +export const rowCollapsed = eventFactory('row-collapsed'); +export const templateVariableValueUpdated = eventFactory('template-variable-value-updated'); + +export const dataFramesReceived = eventFactory('data-frames-received'); + +export const graphClicked = eventFactory('graph-click'); + +export const thresholdChanged = eventFactory('threshold-changed'); + +export const zoomOut = eventFactory('zoom-out'); + +export const shiftTime = eventFactory('shift-time'); + +export const elasticQueryUpdated = eventFactory('elastic-query-updated'); + +export const layoutModeChanged = eventFactory('layout-mode-changed'); + +export const jsonDiffReady = eventFactory('json-diff-ready'); + +export const closeTimepicker = eventFactory('closeTimepicker'); + +export const routeUpdated = eventFactory('$routeUpdate'); diff --git a/public/app/types/index.ts b/public/app/types/index.ts index a47f1123024..da4a563a1b9 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -14,3 +14,8 @@ export * from './search'; export * from './explore'; export * from './store'; export * from './ldap'; +export * from './appEvent'; +export * from './angular'; + +import * as CoreEvents from './events'; +export { CoreEvents }; diff --git a/public/test/specs/helpers.ts b/public/test/specs/helpers.ts index 1112b1f6b06..b064fff51da 100644 --- a/public/test/specs/helpers.ts +++ b/public/test/specs/helpers.ts @@ -7,6 +7,7 @@ import { angularMocks, sinon } from '../lib/common'; import { PanelModel } from 'app/features/dashboard/state/PanelModel'; import { RawTimeRange } from '@grafana/data'; import { PanelPluginMeta } from '@grafana/ui'; +import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; export function ControllerTestContext(this: any) { const self = this; @@ -46,63 +47,67 @@ export function ControllerTestContext(this: any) { }; this.createPanelController = (Ctrl: any) => { - return angularMocks.inject(($controller: any, $rootScope: any, $q: any, $location: any, $browser: any) => { - self.scope = $rootScope.$new(); - self.$location = $location; - self.$browser = $browser; - self.$q = $q; - self.panel = new PanelModel({ type: 'test' }); - self.dashboard = { meta: {} }; - self.isUtc = false; - self.dashboard.isTimezoneUtc = () => { - return self.isUtc; - }; + return angularMocks.inject( + ($controller: any, $rootScope: GrafanaRootScope, $q: any, $location: any, $browser: any) => { + self.scope = $rootScope.$new(); + self.$location = $location; + self.$browser = $browser; + self.$q = $q; + self.panel = new PanelModel({ type: 'test' }); + self.dashboard = { meta: {} }; + self.isUtc = false; + self.dashboard.isTimezoneUtc = () => { + return self.isUtc; + }; - $rootScope.appEvent = sinon.spy(); - $rootScope.onAppEvent = sinon.spy(); - $rootScope.colors = []; + $rootScope.appEvent = sinon.spy(); + $rootScope.onAppEvent = sinon.spy(); + $rootScope.colors = []; - for (let i = 0; i < 50; i++) { - $rootScope.colors.push('#' + i); - } - - config.panels['test'] = { info: {} } as PanelPluginMeta; - self.ctrl = $controller( - Ctrl, - { $scope: self.scope }, - { - panel: self.panel, - dashboard: self.dashboard, + for (let i = 0; i < 50; i++) { + $rootScope.colors.push('#' + i); } - ); - }); + + config.panels['test'] = { info: {} } as PanelPluginMeta; + self.ctrl = $controller( + Ctrl, + { $scope: self.scope }, + { + panel: self.panel, + dashboard: self.dashboard, + } + ); + } + ); }; this.createControllerPhase = (controllerName: string) => { - return angularMocks.inject(($controller: any, $rootScope: any, $q: any, $location: any, $browser: any) => { - self.scope = $rootScope.$new(); - self.$location = $location; - self.$browser = $browser; - self.scope.contextSrv = {}; - self.scope.panel = {}; - self.scope.dashboard = { meta: {} }; - self.scope.dashboardMeta = {}; - self.scope.dashboardViewState = DashboardViewStateStub(); - self.scope.appEvent = sinon.spy(); - self.scope.onAppEvent = sinon.spy(); + return angularMocks.inject( + ($controller: any, $rootScope: GrafanaRootScope, $q: any, $location: any, $browser: any) => { + self.scope = $rootScope.$new(); + self.$location = $location; + self.$browser = $browser; + self.scope.contextSrv = {}; + self.scope.panel = {}; + self.scope.dashboard = { meta: {} }; + self.scope.dashboardMeta = {}; + self.scope.dashboardViewState = DashboardViewStateStub(); + self.scope.appEvent = sinon.spy(); + self.scope.onAppEvent = sinon.spy(); - $rootScope.colors = []; - for (let i = 0; i < 50; i++) { - $rootScope.colors.push('#' + i); + $rootScope.colors = []; + for (let i = 0; i < 50; i++) { + $rootScope.colors.push('#' + i); + } + + self.$q = $q; + self.scope.skipDataOnInit = true; + self.scope.skipAutoInit = true; + self.controller = $controller(controllerName, { + $scope: self.scope, + }); } - - self.$q = $q; - self.scope.skipDataOnInit = true; - self.scope.skipAutoInit = true; - self.controller = $controller(controllerName, { - $scope: self.scope, - }); - }); + ); }; this.setIsUtc = (isUtc: any = false) => { @@ -129,7 +134,7 @@ export function ServiceTestContext(this: any) { this.createService = (name: string) => { // @ts-ignore return angularMocks.inject( - ($q: any, $rootScope: any, $httpBackend: any, $injector: any, $location: any, $timeout: any) => { + ($q: any, $rootScope: GrafanaRootScope, $httpBackend: any, $injector: any, $location: any, $timeout: any) => { self.$q = $q; self.$rootScope = $rootScope; self.$httpBackend = $httpBackend;