Commit 74c65eca by Torkel Ödegaard Committed by GitHub

EventBus: Introduces new event bus with emitter backward compatible interface (#27564)

* updated

* Experimenting with event bus with legacy support

* Before switch to emitter

* EventBus & Emitter unification

* Everything using new EventBus

* Making progress

* Fixing merge issues

* Final merge issues

* Updated

* Updates

* Fix

* Updated

* Update

* Update

* Rename methods to publish and subscribe

* Ts fixes

* Updated

* updated

* fixing doc warnigns

* removed unused file
parent d84d8a13
......@@ -25,6 +25,7 @@
"@braintree/sanitize-url": "4.0.0",
"@types/d3-interpolate": "^1.3.1",
"apache-arrow": "0.16.0",
"eventemitter3": "4.0.7",
"lodash": "4.17.19",
"rxjs": "6.6.3",
"xss": "1.0.6"
......
import { EventBusSrv } from './EventBus';
import { BusEventWithPayload } from './types';
import { eventFactory } from './eventFactory';
interface LoginEventPayload {
logins: number;
}
interface HelloEventPayload {
hellos: number;
}
class LoginEvent extends BusEventWithPayload<LoginEventPayload> {
static type = 'login-event';
}
class HelloEvent extends BusEventWithPayload<HelloEventPayload> {
static type = 'hello-event';
}
type LegacyEventPayload = [string, string];
export const legacyEvent = eventFactory<LegacyEventPayload>('legacy-event');
class AlertSuccessEvent extends BusEventWithPayload<LegacyEventPayload> {
static type = 'legacy-event';
}
describe('EventBus', () => {
it('Can create events', () => {
expect(new LoginEvent({ logins: 1 }).type).toBe('login-event');
});
it('Can subscribe specific event', () => {
const bus = new EventBusSrv();
const events: LoginEvent[] = [];
bus.subscribe(LoginEvent, event => {
events.push(event);
});
bus.publish(new LoginEvent({ logins: 10 }));
bus.publish(new HelloEvent({ hellos: 10 }));
expect(events[0].payload.logins).toBe(10);
expect(events.length).toBe(1);
});
describe('Legacy emitter behavior', () => {
it('Supports legacy events', () => {
const bus = new EventBusSrv();
const events: any = [];
const handler = (event: LegacyEventPayload) => {
events.push(event);
};
bus.on(legacyEvent, handler);
bus.emit(legacyEvent, ['hello', 'hello2']);
bus.off(legacyEvent, handler);
bus.emit(legacyEvent, ['hello', 'hello2']);
expect(events.length).toEqual(1);
expect(events[0]).toEqual(['hello', 'hello2']);
});
it('Interoperability with legacy events', () => {
const bus = new EventBusSrv();
const legacyEvents: any = [];
const newEvents: any = [];
bus.on(legacyEvent, event => {
legacyEvents.push(event);
});
bus.subscribe(AlertSuccessEvent, event => {
newEvents.push(event);
});
bus.emit(legacyEvent, ['legacy', 'params']);
bus.publish(new AlertSuccessEvent(['new', 'event']));
expect(legacyEvents).toEqual([
['legacy', 'params'],
['new', 'event'],
]);
expect(newEvents).toEqual([
{
type: 'legacy-event',
payload: ['legacy', 'params'],
},
{
type: 'legacy-event',
payload: ['new', 'event'],
},
]);
});
it('should notfiy subscribers', () => {
const bus = new EventBusSrv();
let sub1Called = false;
let sub2Called = false;
bus.on(legacyEvent, () => {
sub1Called = true;
});
bus.on(legacyEvent, () => {
sub2Called = true;
});
bus.emit(legacyEvent, null);
expect(sub1Called).toBe(true);
expect(sub2Called).toBe(true);
});
it('when subscribing twice', () => {
const bus = new EventBusSrv();
let sub1Called = 0;
function handler() {
sub1Called += 1;
}
bus.on(legacyEvent, handler);
bus.on(legacyEvent, handler);
bus.emit(legacyEvent, null);
expect(sub1Called).toBe(2);
});
it('should handle errors', () => {
const bus = new EventBusSrv();
let sub1Called = 0;
let sub2Called = 0;
bus.on(legacyEvent, () => {
sub1Called++;
throw { message: 'hello' };
});
bus.on(legacyEvent, () => {
sub2Called++;
});
try {
bus.emit(legacyEvent, null);
} catch (_) {}
try {
bus.emit(legacyEvent, null);
} catch (_) {}
expect(sub1Called).toBe(2);
expect(sub2Called).toBe(0);
});
it('removeAllListeners should unsubscribe to all', () => {
const bus = new EventBusSrv();
const events: LoginEvent[] = [];
bus.subscribe(LoginEvent, event => {
events.push(event);
});
bus.removeAllListeners();
bus.publish(new LoginEvent({ logins: 10 }));
expect(events.length).toBe(0);
});
});
});
import EventEmitter from 'eventemitter3';
import { Unsubscribable, Observable } from 'rxjs';
import { AppEvent } from './types';
import { EventBus, LegacyEmitter, BusEventHandler, BusEventType, LegacyEventHandler, BusEvent } from './types';
/**
* @alpha
*/
export class EventBusSrv implements EventBus, LegacyEmitter {
private emitter: EventEmitter;
constructor() {
this.emitter = new EventEmitter();
}
publish<T extends BusEvent>(event: T): void {
this.emitter.emit(event.type, event);
}
subscribe<T extends BusEvent>(typeFilter: BusEventType<T>, handler: BusEventHandler<T>): Unsubscribable {
return this.getStream(typeFilter).subscribe({ next: handler });
}
getStream<T extends BusEvent>(eventType: BusEventType<T>): Observable<T> {
return new Observable<T>(observer => {
const handler = (event: T) => {
observer.next(event);
};
this.emitter.on(eventType.type, handler);
return () => {
this.emitter.off(eventType.type, handler);
};
});
}
/**
* Legacy functions
*/
emit<T>(event: AppEvent<T> | string, payload?: T | any): void {
// console.log(`Deprecated emitter function used (emit), use $emit`);
if (typeof event === 'string') {
this.emitter.emit(event, { type: event, payload });
} else {
this.emitter.emit(event.name, { type: event.name, payload });
}
}
on<T>(event: AppEvent<T> | string, handler: LegacyEventHandler<T>, scope?: any) {
// console.log(`Deprecated emitter function used (on), use $on`);
// need this wrapper to make old events compatible with old handlers
handler.wrapper = (emittedEvent: BusEvent) => {
handler(emittedEvent.payload);
};
if (typeof event === 'string') {
this.emitter.on(event, handler.wrapper);
} else {
this.emitter.on(event.name, handler.wrapper);
}
if (scope) {
const unbind = scope.$on('$destroy', () => {
this.off(event, handler);
unbind();
});
}
}
off<T>(event: AppEvent<T> | string, handler: LegacyEventHandler<T>) {
if (typeof event === 'string') {
this.emitter.off(event, handler.wrapper);
return;
}
this.emitter.off(event.name, handler.wrapper);
}
removeAllListeners() {
this.emitter.removeAllListeners();
}
}
import { AppEvent } from './appEvents';
export type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
export type Subtract<T, K> = Omit<T, keyof K>;
import { AppEvent } from './types';
const typeList: Set<string> = new Set();
export function eventFactory<T = undefined>(name: string): AppEvent<T> {
if (typeList.has(name)) {
throw new Error(`There is already an event defined with type '${name}'`);
......
export * from './eventFactory';
export * from './types';
export * from './EventBus';
import { Unsubscribable, Observable } from 'rxjs';
/**
* @alpha
* internal interface
*/
export interface BusEvent {
readonly type: string;
readonly payload?: any;
}
/**
* @alpha
* Base event type
*/
export abstract class BusEventBase implements BusEvent {
readonly type: string;
readonly payload?: any;
constructor() {
//@ts-ignore
this.type = this.__proto__.constructor.type;
}
}
/**
* @alpha
* Base event type with payload
*/
export abstract class BusEventWithPayload<T> extends BusEventBase {
readonly payload: T;
constructor(payload: T) {
super();
this.payload = payload;
}
}
/*
* Interface for an event type constructor
*/
export interface BusEventType<T extends BusEvent> {
type: string;
new (...args: any[]): T;
}
/**
* @alpha
* Event callback/handler type
*/
export interface BusEventHandler<T extends BusEvent> {
(event: T): void;
}
/**
* @alpha
* Main minimal interface
*/
export interface EventBus {
/**
* Publish single vent
*/
publish<T extends BusEvent>(event: T): void;
/**
* Subscribe to single event
*/
subscribe<T extends BusEvent>(eventType: BusEventType<T>, handler: BusEventHandler<T>): Unsubscribable;
/**
* Get observable of events
*/
getStream<T extends BusEvent>(eventType: BusEventType<T>): Observable<T>;
/**
* Remove all event subscriptions
*/
removeAllListeners(): void;
}
/**
* @public
* @deprecated event type
*/
export interface AppEvent<T> {
readonly name: string;
payload?: T;
}
/** @public */
export interface LegacyEmitter {
/**
* @deprecated use $emit
*/
emit<T>(event: AppEvent<T> | string, payload?: T): void;
/**
* @deprecated use $on
*/
on<T>(event: AppEvent<T> | string, handler: LegacyEventHandler<T>, scope?: any): void;
/**
* @deprecated use $on
*/
off<T>(event: AppEvent<T> | string, handler: (payload?: T | any) => void): void;
}
/** @public */
export interface LegacyEventHandler<T> {
(payload: T): void;
wrapper?: (event: BusEvent) => void;
}
/** @alpha */
export interface EventBusExtended extends EventBus, LegacyEmitter {}
......@@ -12,4 +12,5 @@ export * from './datetime';
export * from './text';
export * from './valueFormats';
export * from './field';
export * from './events';
export { PanelPlugin } from './panel/PanelPlugin';
import { eventFactory } from './utils';
export interface AppEvent<T> {
readonly name: string;
payload?: T;
}
export type AlertPayload = [string, string?];
export type AlertErrorPayload = [string, (string | Error)?];
export const alertSuccess = eventFactory<AlertPayload>('alert-success');
export const alertWarning = eventFactory<AlertPayload>('alert-warning');
export const alertError = eventFactory<AlertErrorPayload>('alert-error');
......@@ -7,7 +7,6 @@ export * from './navModel';
export * from './select';
export * from './time';
export * from './thresholds';
export * from './utils';
export * from './valueMapping';
export * from './displayValue';
export * from './graph';
......@@ -27,12 +26,7 @@ export * from './orgs';
export * from './flot';
export * from './trace';
export * from './explore';
export * from './legacyEvents';
export * from './live';
import * as AppEvents from './appEvents';
import { AppEvent } from './appEvents';
export { AppEvent, AppEvents };
import * as PanelEvents from './panelEvents';
export { PanelEvents };
export { GrafanaConfig, BuildInfo, FeatureToggles, LicenseInfo } from './config';
import { DataQueryError, DataQueryResponseData } from './datasource';
import { AngularPanelMenuItem } from './panel';
import { DataFrame } from './dataFrame';
import { eventFactory } from '../events/eventFactory';
import { BusEventBase, BusEventWithPayload } from '../events/types';
export type AlertPayload = [string, string?];
export type AlertErrorPayload = [string, (string | Error)?];
export const AppEvents = {
alertSuccess: eventFactory<AlertPayload>('alert-success'),
alertWarning: eventFactory<AlertPayload>('alert-warning'),
alertError: eventFactory<AlertErrorPayload>('alert-error'),
};
export const PanelEvents = {
refresh: eventFactory('refresh'),
componentDidMount: eventFactory('component-did-mount'),
dataReceived: eventFactory<DataQueryResponseData[]>('data-received'),
dataError: eventFactory<DataQueryError>('data-error'),
dataFramesReceived: eventFactory<DataFrame[]>('data-frames-received'),
dataSnapshotLoad: eventFactory<DataQueryResponseData[]>('data-snapshot-load'),
editModeInitialized: eventFactory('init-edit-mode'),
initPanelActions: eventFactory<AngularPanelMenuItem[]>('init-panel-actions'),
panelInitialized: eventFactory('panel-initialized'),
panelSizeChanged: eventFactory('panel-size-changed'),
panelTeardown: eventFactory('panel-teardown'),
render: eventFactory<any>('render'),
};
/** @public */
export interface LegacyGraphHoverEventPayload {
pos: any;
panel: {
id: number;
};
}
/** @alpha */
export class LegacyGraphHoverEvent extends BusEventWithPayload<LegacyGraphHoverEventPayload> {
static type = 'graph-hover';
}
/** @alpha */
export class LegacyGraphHoverClearEvent extends BusEventBase {
static type = 'graph-hover-clear';
}
......@@ -4,6 +4,7 @@ import { ScopedVars } from './ScopedVars';
import { LoadingState } from './data';
import { DataFrame } from './dataFrame';
import { AbsoluteTimeRange, TimeRange, TimeZone } from './time';
import { EventBus } from '../events';
import { FieldConfigSource } from './fieldOverrides';
import { Registry } from '../utils';
import { StandardEditorProps } from '../field';
......@@ -77,6 +78,9 @@ export interface PanelProps<T = any> {
/** Panel title */
title: string;
/** EventBus */
eventBus: EventBus;
/** Panel options change handler */
onOptionsChange: (options: T) => void;
......
import { eventFactory } from './utils';
import { DataQueryError, DataQueryResponseData } from './datasource';
import { AngularPanelMenuItem } from './panel';
import { DataFrame } from './dataFrame';
/** Payloads */
export interface PanelChangeViewPayload {
fullscreen?: boolean;
edit?: boolean;
panelId?: number;
toggle?: boolean;
}
/** Events */
export const refresh = eventFactory('refresh');
export const componentDidMount = eventFactory('component-did-mount');
export const dataError = eventFactory<DataQueryError>('data-error');
export const dataReceived = eventFactory<DataQueryResponseData[]>('data-received');
export const dataFramesReceived = eventFactory<DataFrame[]>('data-frames-received');
export const dataSnapshotLoad = eventFactory<DataQueryResponseData[]>('data-snapshot-load');
export const editModeInitialized = eventFactory('init-edit-mode');
export const initPanelActions = eventFactory<AngularPanelMenuItem[]>('init-panel-actions');
export const panelChangeView = eventFactory<PanelChangeViewPayload>('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<any>('render');
export const viewModeChanged = eventFactory('view-mode-changed');
import { Emitter } from './utils/emitter';
import { EventBusSrv, EventBusExtended } from '@grafana/data';
export const appEvents = new Emitter();
export const appEvents: EventBusExtended = new EventBusSrv();
export default appEvents;
......@@ -19,7 +19,6 @@ import { colors, JsonExplorer } from '@grafana/ui/';
import { infoPopover } from './components/info_popover';
import { arrayJoin } from './directives/array_join';
import { Emitter } from './utils/emitter';
import { switchDirective } from './components/switch';
import { dashboardSelector } from './components/dashboard_selector';
import { queryPartEditorDirective } from './components/query_part/query_part_editor';
......@@ -47,7 +46,6 @@ export {
coreModule,
switchDirective,
infoPopover,
Emitter,
appEvents,
dashboardSelector,
queryPartEditorDirective,
......
......@@ -11,7 +11,6 @@ import { DashboardSearchHit } from 'app/features/search/types';
import { FolderDTO } from 'app/types';
import { coreModule } from 'app/core/core_module';
import { ContextSrv, contextSrv } from './context_srv';
import { Emitter } from '../utils/emitter';
import { parseInitFromOptions, parseUrlFromOptions } from '../utils/fetch';
import { isDataQuery, isLocalUrl } from '../utils/query';
import { FetchQueue } from './FetchQueue';
......@@ -22,7 +21,7 @@ const CANCEL_ALL_REQUESTS_REQUEST_ID = 'cancel_all_requests_request_id';
export interface BackendSrvDependencies {
fromFetch: (input: string | Request, init?: RequestInit) => Observable<Response>;
appEvents: Emitter;
appEvents: typeof appEvents;
contextSrv: ContextSrv;
logout: () => void;
}
......
......@@ -2,7 +2,7 @@ import _ from 'lodash';
import Mousetrap from 'mousetrap';
import 'mousetrap-global-bind';
import { ILocationService, IRootScopeService, ITimeoutService } from 'angular';
import { locationUtil } from '@grafana/data';
import { LegacyGraphHoverClearEvent, locationUtil } from '@grafana/data';
import coreModule from 'app/core/core_module';
import appEvents from 'app/core/app_events';
......@@ -197,7 +197,7 @@ export class KeybindingSrv {
setupDashboardBindings(scope: IRootScopeService & AppEventEmitter, dashboard: DashboardModel) {
this.bind('mod+o', () => {
dashboard.graphTooltip = (dashboard.graphTooltip + 1) % 3;
appEvents.emit(CoreEvents.graphHoverClear);
dashboard.events.publish(new LegacyGraphHoverClearEvent());
dashboard.startRefresh();
});
......
import 'whatwg-fetch'; // fetch polyfill needed for PhantomJs rendering
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';
import { AppEvents, DataQueryErrorType } from '@grafana/data';
import { AppEvents, DataQueryErrorType, EventBusExtended } from '@grafana/data';
import { BackendSrv } from '../services/backend_srv';
import { Emitter } from '../utils/emitter';
import { ContextSrv, User } from '../services/context_srv';
import { describe, expect } from '../../../test/lib/common';
import { BackendSrvRequest, FetchError } from '@grafana/runtime';
......@@ -35,9 +34,11 @@ const getTestContext = (overides?: object) => {
};
return of(mockedResponse);
});
const appEventsMock: Emitter = ({
const appEventsMock: EventBusExtended = ({
emit: jest.fn(),
} as any) as Emitter;
} as any) as EventBusExtended;
const user: User = ({
isSignedIn: props.isSignedIn,
orgId: props.orgId,
......
import { Emitter } from '../utils/emitter';
import { eventFactory } from '@grafana/data';
const testEvent = eventFactory('test');
describe('Emitter', () => {
describe('given 2 subscribers', () => {
it('should notfiy subscribers', () => {
const events = new Emitter();
let sub1Called = false;
let sub2Called = false;
events.on(testEvent, () => {
sub1Called = true;
});
events.on(testEvent, () => {
sub2Called = true;
});
events.emit(testEvent, null);
expect(sub1Called).toBe(true);
expect(sub2Called).toBe(true);
});
it('when subscribing twice', () => {
const events = new Emitter();
let sub1Called = 0;
function handler() {
sub1Called += 1;
}
events.on(testEvent, handler);
events.on(testEvent, handler);
events.emit(testEvent, null);
expect(sub1Called).toBe(2);
});
it('should handle errors', () => {
const events = new Emitter();
let sub1Called = 0;
let sub2Called = 0;
events.on(testEvent, () => {
sub1Called++;
throw { message: 'hello' };
});
events.on(testEvent, () => {
sub2Called++;
});
try {
events.emit(testEvent, null);
} catch (_) {}
try {
events.emit(testEvent, null);
} catch (_) {}
expect(sub1Called).toBe(2);
expect(sub2Called).toBe(0);
});
});
});
import EventEmitter3, { EventEmitter } from 'eventemitter3';
import { AppEvent } from '@grafana/data';
export class Emitter {
private emitter: EventEmitter3;
constructor() {
this.emitter = new EventEmitter();
}
/**
* DEPRECATED.
*/
emit(name: string, data?: any): void;
/**
* Emits an `event` with `payload`.
*/
emit<T extends undefined>(event: AppEvent<T>): void;
emit<T extends (U extends any ? Partial<T> : unknown) extends T ? Partial<T> : never, U = any>(
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.warn(`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);
}
}
/**
* 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 (U extends any ? Partial<T> : unknown) extends T ? Partial<T> : never, U = any>(
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.warn(`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(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 (U extends any ? Partial<T> : unknown) extends T ? Partial<T> : never, U = any>(
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.warn(`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);
}
getEventCount(): number {
return (this.emitter as any)._eventsCount;
}
}
......@@ -39,8 +39,8 @@ export class SettingsCtrl {
this.$scope.$on('$destroy', () => {
this.dashboard.updateSubmenuVisibility();
setTimeout(() => {
this.$rootScope.appEvent(CoreEvents.dashScroll, { restore: true });
this.dashboard.startRefresh();
});
});
......@@ -53,7 +53,6 @@ export class SettingsCtrl {
this.onRouteUpdated();
this.$rootScope.onAppEvent(CoreEvents.routeUpdated, this.onRouteUpdated.bind(this), $scope);
this.$rootScope.appEvent(CoreEvents.dashScroll, { animate: false, pos: 0 });
appEvents.on(CoreEvents.dashboardSaved, this.onPostSave.bind(this), $scope);
......
......@@ -57,9 +57,7 @@ export class HistoryListCtrl {
}
}
dismiss() {
this.$rootScope.appEvent(CoreEvents.hideDashEditor);
}
dismiss() {}
addToLog() {
this.start = this.start + this.limit;
......
......@@ -24,7 +24,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
"autoUpdate": undefined,
"description": undefined,
"editable": true,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -53,7 +53,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -138,7 +138,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
"autoUpdate": undefined,
"description": undefined,
"editable": true,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -167,7 +167,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -233,7 +233,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
"autoUpdate": undefined,
"description": undefined,
"editable": true,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -262,7 +262,7 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -384,7 +384,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
"autoUpdate": undefined,
"description": undefined,
"editable": true,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -413,7 +413,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -498,7 +498,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
"autoUpdate": undefined,
"description": undefined,
"editable": true,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -527,7 +527,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -593,7 +593,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
"autoUpdate": undefined,
"description": undefined,
"editable": true,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -622,7 +622,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -693,7 +693,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
"autoUpdate": undefined,
"description": undefined,
"editable": true,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -722,7 +722,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......
......@@ -52,8 +52,9 @@ export interface State {
}
export class PanelChrome extends PureComponent<Props, State> {
timeSrv: TimeSrv = getTimeSrv();
querySubscription: Unsubscribable;
readonly timeSrv: TimeSrv = getTimeSrv();
querySubscription?: Unsubscribable;
constructor(props: Props) {
super(props);
......@@ -73,6 +74,7 @@ export class PanelChrome extends PureComponent<Props, State> {
componentDidMount() {
const { panel, dashboard } = this.props;
// Subscribe to panel events
panel.events.on(PanelEvents.refresh, this.onRefresh);
panel.events.on(PanelEvents.render, this.onRender);
......@@ -244,7 +246,7 @@ export class PanelChrome extends PureComponent<Props, State> {
}
renderPanel(width: number, height: number) {
const { panel, plugin } = this.props;
const { panel, plugin, dashboard } = this.props;
const { renderCounter, data, isFirstLoad } = this.state;
const { theme } = config;
const { state: loadingState } = data;
......@@ -291,6 +293,7 @@ export class PanelChrome extends PureComponent<Props, State> {
onOptionsChange={this.onOptionsChange}
onFieldConfigChange={this.onFieldConfigChange}
onChangeTimeRange={this.onChangeTimeRange}
eventBus={dashboard.events}
/>
</div>
</>
......
......@@ -68,7 +68,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"autoUpdate": undefined,
"description": undefined,
"editable": true,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {
"panel-added": EE {
......@@ -123,7 +123,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -151,7 +151,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -179,7 +179,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -207,7 +207,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -258,7 +258,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -310,7 +310,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"autoUpdate": undefined,
"description": undefined,
"editable": true,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {
"panel-added": EE {
......@@ -365,7 +365,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -393,7 +393,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -421,7 +421,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -449,7 +449,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -500,7 +500,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -552,7 +552,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"autoUpdate": undefined,
"description": undefined,
"editable": true,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {
"panel-added": EE {
......@@ -607,7 +607,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -635,7 +635,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -663,7 +663,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -691,7 +691,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -742,7 +742,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -794,7 +794,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"autoUpdate": undefined,
"description": undefined,
"editable": true,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {
"panel-added": EE {
......@@ -849,7 +849,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -877,7 +877,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -905,7 +905,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -933,7 +933,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......@@ -984,7 +984,7 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
PanelModel {
"cachedPluginOptions": Object {},
"datasource": null,
"events": Emitter {
"events": EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......
......@@ -5,7 +5,6 @@ import _ from 'lodash';
// Utils & Services
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { AngularComponent, getAngularLoader } from '@grafana/runtime';
import { Emitter } from 'app/core/utils/emitter';
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
// Types
import { PanelModel } from '../state/PanelModel';
......@@ -19,6 +18,7 @@ import {
PanelEvents,
TimeRange,
toLegacyResponseData,
EventBusExtended,
} from '@grafana/data';
import { QueryEditorRowTitle } from './QueryEditorRowTitle';
import { QueryOperationRow } from 'app/core/components/QueryOperationRow/QueryOperationRow';
......@@ -331,7 +331,7 @@ export interface AngularQueryComponentScope {
target: DataQuery;
panel: PanelModel;
dashboard: DashboardModel;
events: Emitter;
events: EventBusExtended;
refresh: () => void;
render: () => void;
datasource: DataSourceApi | null;
......
......@@ -4,7 +4,6 @@ import _ from 'lodash';
import { DEFAULT_ANNOTATION_COLOR } from '@grafana/ui';
import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, GRID_COLUMN_COUNT, REPEAT_DIR_VERTICAL } from 'app/core/constants';
// Utils & Services
import { Emitter } from 'app/core/utils/emitter';
import { contextSrv } from 'app/core/services/context_srv';
import sortByKeys from 'app/core/utils/sort_by_keys';
// Types
......@@ -19,6 +18,8 @@ import {
TimeRange,
TimeZone,
UrlQueryValue,
EventBusSrv,
EventBusExtended,
} from '@grafana/data';
import { CoreEvents, DashboardMeta, KIOSK_MODE_TV } from 'app/types';
import { GetVariables, getVariables } from 'app/features/variables/state/selectors';
......@@ -82,7 +83,7 @@ export class DashboardModel {
// repeat process cycles
iteration?: number;
meta: DashboardMeta;
events: Emitter;
events: EventBusExtended;
static nonPersistedProperties: { [str: string]: boolean } = {
events: true,
......@@ -101,7 +102,7 @@ export class DashboardModel {
data = {};
}
this.events = new Emitter();
this.events = new EventBusSrv();
this.id = data.id || null;
this.uid = data.uid || null;
this.revision = data.revision;
......
......@@ -2,7 +2,6 @@
import _ from 'lodash';
// Utils
import { getTemplateSrv } from '@grafana/runtime';
import { Emitter } from 'app/core/utils/emitter';
import { getNextRefIdChar } from 'app/core/utils/query';
// Types
import {
......@@ -19,6 +18,8 @@ import {
ScopedVars,
ThresholdsConfig,
ThresholdsMode,
EventBusExtended,
EventBusSrv,
} from '@grafana/data';
import { EDIT_PANEL_ID } from 'app/core/constants';
import config from 'app/core/config';
......@@ -145,7 +146,7 @@ export class PanelModel implements DataConfigSource {
isInView: boolean;
hasRefreshed: boolean;
events: Emitter;
events: EventBusExtended;
cacheTimeout?: any;
cachedPluginOptions?: any;
legend?: { show: boolean; sort?: string; sortDesc?: boolean };
......@@ -154,7 +155,7 @@ export class PanelModel implements DataConfigSource {
private queryRunner?: PanelQueryRunner;
constructor(model: any) {
this.events = new Emitter();
this.events = new EventBusSrv();
this.restoreModel(model);
this.replaceVariables = this.replaceVariables.bind(this);
}
......
......@@ -197,7 +197,7 @@ export class PanelQueryRunner {
}
this.subscription = observable.subscribe({
next: (data: PanelData) => {
next: data => {
this.lastResult = preProcessPanelData(data, this.lastResult);
// Store preprocessed query results for applying overrides later on in the pipeline
this.subject.next(this.lastResult);
......
......@@ -20,6 +20,8 @@ import {
TimeZone,
ExploreUrlState,
LogsModel,
EventBusExtended,
EventBusSrv,
} from '@grafana/data';
import store from 'app/core/store';
......@@ -50,7 +52,6 @@ import {
getTimeRangeFromUrl,
lastUsedDatasourceKeyForOrgId,
} from 'app/core/utils/explore';
import { Emitter } from 'app/core/utils/emitter';
import { ExploreToolbar } from './ExploreToolbar';
import { NoDataSourceCallToAction } from './NoDataSourceCallToAction';
import { getTimeZone } from '../profile/state/selectors';
......@@ -159,11 +160,11 @@ interface ExploreState {
*/
export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
el: any;
exploreEvents: Emitter;
exploreEvents: EventBusExtended;
constructor(props: ExploreProps) {
super(props);
this.exploreEvents = new Emitter();
this.exploreEvents = new EventBusSrv();
this.state = {
openDrawer: undefined,
};
......
......@@ -5,9 +5,7 @@ import React, { PureComponent } from 'react';
import { getAngularLoader, AngularComponent } from '@grafana/runtime';
// Types
import { Emitter } from 'app/core/utils/emitter';
import { DataQuery } from '@grafana/data';
import { TimeRange } from '@grafana/data';
import { DataQuery, TimeRange, EventBusExtended } from '@grafana/data';
import 'app/features/plugins/plugin_loader';
interface QueryEditorProps {
......@@ -16,7 +14,7 @@ interface QueryEditorProps {
onExecuteQuery?: () => void;
onQueryChange?: (value: DataQuery) => void;
initialQuery: DataQuery;
exploreEvents: Emitter;
exploreEvents: EventBusExtended;
range: TimeRange;
textEditModeEnabled?: boolean;
}
......
......@@ -2,14 +2,14 @@ import React from 'react';
import { QueryRow, QueryRowProps } from './QueryRow';
import { shallow } from 'enzyme';
import { ExploreId } from 'app/types/explore';
import { Emitter } from 'app/core/utils/emitter';
import { EventBusExtended } from '@grafana/data';
import { DataSourceApi, TimeRange, AbsoluteTimeRange, PanelData } from '@grafana/data';
const setup = (propOverrides?: object) => {
const props: QueryRowProps = {
exploreId: ExploreId.left,
index: 1,
exploreEvents: {} as Emitter,
exploreEvents: {} as EventBusExtended,
changeQuery: jest.fn(),
datasourceInstance: {} as DataSourceApi,
highlightLogsExpressionAction: jest.fn() as any,
......
......@@ -20,18 +20,18 @@ import {
TimeRange,
AbsoluteTimeRange,
LoadingState,
EventBusExtended,
} from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { ExploreItemState, ExploreId } from 'app/types/explore';
import { Emitter } from 'app/core/utils/emitter';
import { highlightLogsExpressionAction, removeQueryRowAction } from './state/actionTypes';
import { ErrorContainer } from './ErrorContainer';
interface PropsFromParent {
exploreId: ExploreId;
index: number;
exploreEvents: Emitter;
exploreEvents: EventBusExtended;
}
export interface QueryRowProps extends PropsFromParent {
......
......@@ -5,12 +5,12 @@ import React, { PureComponent } from 'react';
import QueryRow from './QueryRow';
// Types
import { Emitter } from 'app/core/utils/emitter';
import { EventBusExtended } from '@grafana/data';
import { ExploreId } from 'app/types/explore';
interface QueryRowsProps {
className?: string;
exploreEvents: Emitter;
exploreEvents: EventBusExtended;
exploreId: ExploreId;
queryKeys: string[];
}
......
......@@ -17,7 +17,7 @@ exports[`Explore should render component 1`] = `
>
<QueryRows
exploreEvents={
Emitter {
EventBusSrv {
"emitter": EventEmitter {
"_events": Object {},
"_eventsCount": 0,
......
......@@ -2,7 +2,6 @@
import { Unsubscribable } from 'rxjs';
import { createAction } from '@reduxjs/toolkit';
import { Emitter } from 'app/core/core';
import {
AbsoluteTimeRange,
DataQuery,
......@@ -13,6 +12,7 @@ import {
LogsDedupStrategy,
QueryFixAction,
TimeRange,
EventBusExtended,
} from '@grafana/data';
import { ExploreId, ExploreItemState, ExplorePanelData } from 'app/types/explore';
......@@ -52,7 +52,7 @@ export interface HighlightLogsExpressionPayload {
export interface InitializeExplorePayload {
exploreId: ExploreId;
containerWidth: number;
eventBridge: Emitter;
eventBridge: EventBusExtended;
queries: DataQuery[];
range: TimeRange;
originPanelId?: number | null;
......
import { PayloadAction } from '@reduxjs/toolkit';
import { DataQuery, DefaultTimeZone, toUtc, ExploreUrlState } from '@grafana/data';
import { DataQuery, DefaultTimeZone, toUtc, ExploreUrlState, EventBusExtended } from '@grafana/data';
import { cancelQueries, loadDatasource, navigateToExplore, refreshExplore } from './actions';
import { ExploreId, ExploreUpdateState } from 'app/types';
......@@ -13,7 +13,6 @@ import {
scanStopAction,
setQueriesAction,
} from './actionTypes';
import { Emitter } from 'app/core/core';
import { makeInitialUpdateState } from './reducers';
import { PanelModel } from 'app/features/dashboard/state';
import { updateLocation } from '../../../core/actions';
......@@ -61,7 +60,7 @@ jest.mock('app/core/utils/explore', () => ({
const setup = (updateOverides?: Partial<ExploreUpdateState>) => {
const exploreId = ExploreId.left;
const containerWidth = 1920;
const eventBridge = {} as Emitter;
const eventBridge = {} as EventBusExtended;
const timeZone = DefaultTimeZone;
const range = testRange;
const urlState: ExploreUrlState = {
......
......@@ -14,6 +14,7 @@ import {
LoadingState,
LogsDedupStrategy,
PanelData,
EventBusExtended,
QueryFixAction,
RawTimeRange,
TimeRange,
......@@ -21,7 +22,6 @@ import {
// Services & Utils
import store from 'app/core/store';
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { Emitter } from 'app/core/core';
import {
buildQueryTransaction,
clearQueryKeys,
......@@ -280,7 +280,7 @@ export function initializeExplore(
queries: DataQuery[],
range: TimeRange,
containerWidth: number,
eventBridge: Emitter,
eventBridge: EventBusExtended,
originPanelId?: number | null
): ThunkResult<void> {
return async (dispatch, getState) => {
......
......@@ -11,6 +11,7 @@ import {
PanelEvents,
sortLogsResult,
toLegacyResponseData,
EventBusExtended,
} from '@grafana/data';
import { RefreshPicker } from '@grafana/ui';
import { LocationUpdate } from '@grafana/runtime';
......@@ -62,7 +63,6 @@ import {
updateDatasourceInstanceAction,
} from './actionTypes';
import { updateLocation } from '../../../core/actions';
import { Emitter } from 'app/core/core';
export const DEFAULT_RANGE = {
from: 'now-6h',
......@@ -111,7 +111,7 @@ export const makeExploreItemState = (): ExploreItemState => ({
graphResult: null,
logsResult: null,
dedupStrategy: LogsDedupStrategy.none,
eventBridge: (null as unknown) as Emitter,
eventBridge: (null as unknown) as EventBusExtended,
});
export const createEmptyQueryResponse = (): PanelData => ({
......
import _ from 'lodash';
import config from 'app/core/config';
import { profiler } from 'app/core/core';
import { Emitter } from 'app/core/utils/emitter';
import { auto } from 'angular';
import { AppEvent, PanelEvents, PanelPluginMeta, AngularPanelMenuItem } from '@grafana/data';
import { AppEvent, PanelEvents, PanelPluginMeta, AngularPanelMenuItem, EventBusExtended } from '@grafana/data';
import { DashboardModel } from '../dashboard/state';
export class PanelCtrl {
......@@ -21,7 +20,7 @@ export class PanelCtrl {
height: number;
width: number;
containerHeight: any;
events: Emitter;
events: EventBusExtended;
loading: boolean;
timing: any;
......
......@@ -97,6 +97,7 @@ function createBarGaugePanelWithData(data: PanelData): ReactWrapper<PanelProps<B
width={532}
transparent={false}
height={250}
eventBus={{} as any}
/>
);
}
......@@ -11,7 +11,7 @@ import './jquery.flot.events';
import $ from 'jquery';
import _ from 'lodash';
import { tickStep } from 'app/core/utils/ticks';
import { appEvents, coreModule, updateLegendValues } from 'app/core/core';
import { coreModule, updateLegendValues } from 'app/core/core';
import GraphTooltip from './graph_tooltip';
import { ThresholdManager } from './threshold_manager';
import { TimeRegionManager } from './time_region_manager';
......@@ -37,6 +37,8 @@ import {
getTimeField,
getValueFormat,
hasLinks,
LegacyGraphHoverClearEvent,
LegacyGraphHoverEvent,
LinkModelSupplier,
PanelEvents,
toUtc,
......@@ -45,7 +47,6 @@ 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);
......@@ -86,8 +87,11 @@ class GraphElement {
this.ctrl.events.on(PanelEvents.render, this.onRender.bind(this));
// global events
appEvents.on(CoreEvents.graphHover, this.onGraphHover.bind(this), scope);
appEvents.on(CoreEvents.graphHoverClear, this.onGraphHoverClear.bind(this), scope);
// Using old way here to use the scope unsubscribe model as the new $on function does not take scope
this.ctrl.dashboard.events.on(LegacyGraphHoverEvent.type, this.onGraphHover.bind(this), this.scope);
this.ctrl.dashboard.events.on(LegacyGraphHoverClearEvent.type, this.onGraphHoverClear.bind(this), this.scope);
// plot events
this.elem.bind('plotselected', this.onPlotSelected.bind(this));
this.elem.bind('plotclick', this.onPlotClick.bind(this));
......
import $ from 'jquery';
import { appEvents } from 'app/core/core';
import { CoreEvents } from 'app/types';
import { textUtil, systemDateFormats } from '@grafana/data';
import { textUtil, systemDateFormats, LegacyGraphHoverClearEvent, LegacyGraphHoverEvent } from '@grafana/data';
export default function GraphTooltip(this: any, elem: any, dashboard: any, scope: any, getSeriesFn: any) {
const self = this;
......@@ -157,7 +157,7 @@ export default function GraphTooltip(this: any, elem: any, dashboard: any, scope
plot.unhighlight();
}
}
appEvents.emit(CoreEvents.graphHoverClear);
dashboard.events.$emit(new LegacyGraphHoverClearEvent());
});
elem.bind('plothover', (event: any, pos: { panelRelY: number; pageY: number }, item: any) => {
......@@ -165,7 +165,7 @@ 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(CoreEvents.graphHover, { pos: pos, panel: panel });
dashboard.events.$emit(new LegacyGraphHoverEvent({ pos: pos, panel: panel }));
});
elem.bind('plotclick', (event: any, pos: any, item: any) => {
......
......@@ -20,13 +20,12 @@ import '../module';
import { GraphCtrl } from '../module';
import { MetricsPanelCtrl } from 'app/features/panel/metrics_panel_ctrl';
import { PanelCtrl } from 'app/features/panel/panel_ctrl';
import config from 'app/core/config';
import TimeSeries from 'app/core/time_series2';
import $ from 'jquery';
import { graphDirective } from '../graph';
import { dateTime } from '@grafana/data';
import { dateTime, EventBusSrv } from '@grafana/data';
const ctx = {} as any;
let ctrl: any;
......@@ -84,6 +83,7 @@ describe('grafanaGraph', () => {
hiddenSeries: {},
dashboard: {
getTimezone: () => 'browser',
events: new EventBusSrv(),
},
range: {
from: dateTime([2015, 1, 1, 10]),
......
import _ from 'lodash';
import $ from 'jquery';
import * as d3 from 'd3';
import { appEvents, contextSrv } from 'app/core/core';
import { contextSrv } from 'app/core/core';
import * as ticksUtils from 'app/core/utils/ticks';
import { HeatmapTooltip } from './heatmap_tooltip';
import { mergeZeroBuckets } from './heatmap_data_converter';
......@@ -12,10 +12,11 @@ import {
getValueFormat,
formattedValueToString,
dateTimeFormat,
LegacyGraphHoverEvent,
LegacyGraphHoverClearEvent,
getColorForTheme,
} from '@grafana/data';
import { graphTimeFormat } from '@grafana/ui';
import { CoreEvents } from 'app/types';
import { config } from 'app/core/config';
const MIN_CARD_SIZE = 1,
......@@ -84,9 +85,8 @@ export class HeatmapRenderer {
/////////////////////////////
// Shared crosshair and tooltip
appEvents.on(CoreEvents.graphHover, this.onGraphHover.bind(this), this.scope);
appEvents.on(CoreEvents.graphHoverClear, this.onGraphHoverClear.bind(this), this.scope);
this.ctrl.dashboard.events.on(LegacyGraphHoverEvent.type, this.onGraphHover.bind(this), this.scope);
this.ctrl.dashboard.events.on(LegacyGraphHoverClearEvent.type, this.onGraphHoverClear.bind(this), this.scope);
// Register selection listeners
this.$heatmap.on('mousedown', this.onMouseDown.bind(this));
......@@ -735,7 +735,7 @@ export class HeatmapRenderer {
}
onMouseLeave() {
appEvents.emit(CoreEvents.graphHoverClear);
this.ctrl.dashboard.$emit(new LegacyGraphHoverClearEvent());
this.clearCrosshair();
}
......@@ -781,7 +781,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(CoreEvents.graphHover, { pos: pos, panel: this.panel });
this.ctrl.dashboard.events.emit$(new LegacyGraphHoverEvent({ pos: pos, panel: this.panel }));
}
limitSelection(x2: number) {
......
......@@ -9,6 +9,7 @@ import { TextOptions } from './types';
import { CustomScrollbar, stylesFactory } from '@grafana/ui';
import { css, cx } from 'emotion';
import DangerouslySetHtmlContent from 'dangerously-set-html-content';
import { Unsubscribable } from 'rxjs';
interface Props extends PanelProps<TextOptions> {}
......@@ -17,6 +18,8 @@ interface State {
}
export class TextPanel extends PureComponent<Props, State> {
eventSub?: Unsubscribable;
constructor(props: Props) {
super(props);
......
......@@ -209,8 +209,6 @@ export function grafanaAppDirective(
for (const drop of Drop.drops) {
drop.destroy();
}
appEvents.emit(CoreEvents.hideDashSearch);
});
// handle kiosk mode
......
......@@ -90,14 +90,13 @@ export interface DashScrollPayload {
pos?: number;
}
export interface PanelChangeViewPayload {}
/**
* Events
*/
export const showDashSearch = eventFactory<ShowDashSearchPayload>('show-dash-search');
export const hideDashSearch = eventFactory('hide-dash-search');
export const hideDashEditor = eventFactory('hide-dash-editor');
export const dashScroll = eventFactory<DashScrollPayload>('dash-scroll');
export const panelChangeView = eventFactory<PanelChangeViewPayload>('panel-change-view');
export const dashLinksUpdated = eventFactory('dash-links-updated');
export const saveDashboard = eventFactory<SaveDashboardPayload>('save-dashboard');
export const dashboardFetchStart = eventFactory('dashboard-fetch-start');
......@@ -119,9 +118,6 @@ export const showModalReact = eventFactory<ShowModalReactPayload>('show-modal-re
export const dsRequestResponse = eventFactory<DataSourceResponsePayload>('ds-request-response');
export const dsRequestError = eventFactory<any>('ds-request-error');
export const graphHover = eventFactory<GraphHoverPayload>('graph-hover');
export const graphHoverClear = eventFactory('graph-hover-clear');
export const toggleSidemenuMobile = eventFactory('toggle-sidemenu-mobile');
export const toggleSidemenuHidden = eventFactory('toggle-sidemenu-hidden');
......
......@@ -15,10 +15,9 @@ import {
QueryHint,
RawTimeRange,
TimeRange,
EventBusExtended,
} from '@grafana/data';
import { Emitter } from 'app/core/core';
export enum ExploreId {
left = 'left',
right = 'right',
......@@ -74,7 +73,7 @@ export interface ExploreItemState {
/**
* Emitter to send events to the rest of Grafana.
*/
eventBridge: Emitter;
eventBridge: EventBusExtended;
/**
* List of timeseries to be shown in the Explore graph result viewer.
*/
......
......@@ -12752,6 +12752,11 @@ eventemitter3@4.0.0:
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb"
integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==
eventemitter3@4.0.7:
version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
eventemitter3@^3.1.0:
version "3.1.2"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment