Types: Adds type safety to appEvents (#19418)

* Types: Add type safety to appEvents
This commit is contained in:
kay delaney
2019-10-14 09:27:47 +01:00
committed by GitHub
parent e7c37cc316
commit 99411bf37a
138 changed files with 991 additions and 508 deletions

View File

@@ -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<Props> {
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) => {

View File

@@ -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<Props, State> {
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']);
}
};

View File

@@ -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<Props, State> {
};
if (config.loginError) {
appEvents.emit('alert-warning', ['Login Failed', config.loginError]);
appEvents.emit(AppEvents.alertWarning, ['Login Failed', config.loginError]);
}
}

View File

@@ -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 (

View File

@@ -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 = `
<div class="layout-selector">
@@ -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;

View File

@@ -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 {
'<move-to-folder-modal dismiss="dismiss()" ' +
'dashboards="model.dashboards" after-save="model.afterSave()">' +
'</move-to-folder-modal>';
appEvents.emit('show-modal', {
appEvents.emit(CoreEvents.showModal, {
templateHtml: template,
modalClass: 'modal--narrow',
model: {

View File

@@ -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 = {}) {

View File

@@ -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);
}
}

View File

@@ -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: '<help-modal></help-modal>' });
expect(appEvents.emit).toHaveBeenCalledWith(CoreEvents.showModal, { templateHtml: '<help-modal></help-modal>' });
});
});
});

View File

@@ -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<Props> {
itemClicked = (event: React.SyntheticEvent, child: NavModelItem) => {
if (child.url === '/shortcuts') {
event.preventDefault();
appEvents.emit('show-modal', {
appEvents.emit(CoreEvents.showModal, {
templateHtml: '<help-modal></help-modal>',
});
}
};
switchOrg = () => {
appEvents.emit('show-modal', {
appEvents.emit(CoreEvents.showModal, {
templateHtml: '<org-switcher dismiss="dismiss()"></org-switcher>',
});
};

View File

@@ -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);
});
});
});

View File

@@ -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() {

View File

@@ -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);
}
});
}

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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;

View File

@@ -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', () => {

View File

@@ -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',

View File

@@ -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;

View File

@@ -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();

View File

@@ -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({

View File

@@ -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<angular.IDeferred<any>> } = {};
@@ -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;
})

View File

@@ -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;

View File

@@ -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: '<help-modal></help-modal>' });
appEvents.emit(CoreEvents.showModal, { templateHtml: '<help-modal></help-modal>' });
}
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

View File

@@ -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 = () => {

View File

@@ -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',

View File

@@ -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);

View File

@@ -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);
});
});

View File

@@ -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<T extends undefined>(event: AppEvent<T>): void;
emit<T extends Partial<T> extends T ? Partial<T> : never>(event: AppEvent<T>): void;
emit<T>(event: AppEvent<T>, payload: T): void;
emit<T>(event: AppEvent<T> | 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<T extends undefined>(event: AppEvent<T>, handler: () => void, scope?: any): void;
on<T extends Partial<T> extends T ? Partial<T> : never>(event: AppEvent<T>, handler: () => void, scope?: any): void;
on<T>(event: AppEvent<T>, handler: (payload: T) => void, scope?: any): void;
on<T>(event: AppEvent<T> | 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<T extends undefined>(event: AppEvent<T>, handler: () => void): void;
off<T extends Partial<T> extends T ? Partial<T> : never>(event: AppEvent<T>, handler: () => void, scope?: any): void;
off<T>(event: AppEvent<T>, handler: (payload: T) => void): void;
off<T>(event: AppEvent<T> | 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;
}