or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

data-transformations.mddataframe-operations.mddatetime-operations.mdevent-system.mdfield-processing.mdindex.mdplugin-system.mdtheme-system.mdutility-functions.mdvalue-formatting.md
tile.json

event-system.mddocs/

Event System

Type-safe event bus system for application-wide communication with built-in events, custom event support, and React integration for decoupled component communication.

Capabilities

Event Bus Service

Core event bus implementation for publishing and subscribing to events.

/**
 * Event bus service implementation
 */
class EventBusSrv implements EventBus {
  /**
   * Publishes an event to all subscribers
   * @param event - Event instance to publish
   */
  publish<T extends BusEvent>(event: T): void;
  
  /**
   * Subscribes to events of a specific type
   * @param typeInfo - Event type information
   * @param handler - Event handler function
   * @returns Unsubscribable for cleanup
   */
  subscribe<T extends BusEvent>(
    typeInfo: BusEventType<T>, 
    handler: BusEventHandler<T>
  ): Unsubscribable;
  
  /**
   * Gets all subscribers for event type (for debugging)
   * @param typeInfo - Event type information
   * @returns Array of event handlers
   */
  getEventCount<T extends BusEvent>(typeInfo: BusEventType<T>): number;
  
  /**
   * Removes all subscribers
   */
  removeAllListeners(): void;
}

Usage Examples:

import { EventBusSrv, DataHoverEvent } from "@grafana/data";

// Create event bus instance
const eventBus = new EventBusSrv();

// Subscribe to events
const unsubscribe = eventBus.subscribe(DataHoverEvent, (event) => {
  console.log('Data hover:', event.payload);
});

// Publish events
eventBus.publish(new DataHoverEvent({
  point: { x: 100, y: 200 },
  data: someDataFrame
}));

// Cleanup subscription
unsubscribe.unsubscribe();

Event Factory

Factory function for creating custom event types.

/**
 * Creates event type factory for custom events
 * @param name - Event name identifier
 * @returns Event type factory function
 */
function eventFactory<TPayload = undefined>(name: string): BusEventType<BusEventWithPayload<TPayload>>;

Usage Examples:

import { eventFactory } from "@grafana/data";

// Create custom event type
interface UserLoginPayload {
  userId: string;
  timestamp: number;
}

const UserLoginEvent = eventFactory<UserLoginPayload>('user-login');

// Use custom event
eventBus.subscribe(UserLoginEvent, (event) => {
  console.log(`User ${event.payload.userId} logged in at ${event.payload.timestamp}`);
});

eventBus.publish(new UserLoginEvent({
  userId: 'user123',
  timestamp: Date.now()
}));

Base Event Classes

Foundation classes for creating events with and without payloads.

/**
 * Base class for events without payload
 */
class BusEventBase implements BusEvent {
  /** Event type information */
  readonly type: string;
  /** Event origin information */
  readonly origin?: any;
  
  constructor(origin?: any);
}

/**
 * Base class for events with payload
 */
class BusEventWithPayload<T> extends BusEventBase {
  /** Event payload data */
  readonly payload: T;
  
  constructor(payload: T, origin?: any);
}

Built-in Events

Pre-defined events for common Grafana operations.

/**
 * Data hover event for chart interactions
 */
class DataHoverEvent extends BusEventWithPayload<DataHoverPayload> {
  static type = 'data-hover';
}

/**
 * Data hover clear event
 */
class DataHoverClearEvent extends BusEventBase {
  static type = 'data-hover-clear';
}

/**
 * Data selection event for chart interactions
 */
class DataSelectEvent extends BusEventWithPayload<any> {
  static type = 'data-select';
}

/**
 * Annotation change event
 */
class AnnotationChangeEvent extends BusEventWithPayload<any> {
  static type = 'annotation-changed';
}

/**
 * Dashboard loaded event
 */
class DashboardLoadedEvent extends BusEventWithPayload<DashboardLoadedEventPayload> {
  static type = 'dashboard-loaded';
}

/**
 * Data source update success event
 */
class DataSourceUpdatedSuccessfully extends BusEventWithPayload<any> {
  static type = 'ds-updated-successfully';
}

/**
 * Data source test success event
 */
class DataSourceTestSucceeded extends BusEventWithPayload<any> {
  static type = 'ds-test-succeeded';
}

/**
 * Data source test failure event
 */
class DataSourceTestFailed extends BusEventWithPayload<any> {
  static type = 'ds-test-failed';
}

/**
 * Panel attention event
 */
class SetPanelAttentionEvent extends BusEventWithPayload<any> {
  static type = 'set-panel-attention';
}

Type Definitions

/**
 * Base event interface
 */
interface BusEvent {
  /** Event type identifier */
  readonly type: string;
  /** Event origin information */
  readonly origin?: any;
}

/**
 * Event type information
 */
interface BusEventType<T extends BusEvent> {
  /** Event type identifier */
  readonly type: string;
  /** Event constructor */
  new (...args: any[]): T;
}

/**
 * Event handler function type
 */
type BusEventHandler<T extends BusEvent> = (event: T) => void;

/**
 * Event filtering options
 */
interface EventFilterOptions {
  /** Only handle events from specific origin */
  onlyLocal?: boolean;
}

/**
 * Event bus interface
 */
interface EventBus {
  /** Publish event */
  publish<T extends BusEvent>(event: T): void;
  /** Subscribe to events */
  subscribe<T extends BusEvent>(typeInfo: BusEventType<T>, handler: BusEventHandler<T>): Unsubscribable;
  /** Get subscriber count */
  getEventCount?<T extends BusEvent>(typeInfo: BusEventType<T>): number;
  /** Remove all listeners */
  removeAllListeners?(): void;
}

/**
 * Extended event bus with additional features
 */
interface EventBusExtended extends EventBus {
  /** Publish with options */
  publishWithOptions<T extends BusEvent>(event: T, options?: EventFilterOptions): void;
  /** Subscribe with filtering */
  subscribeWithFilter<T extends BusEvent>(
    typeInfo: BusEventType<T>, 
    handler: BusEventHandler<T>,
    filter?: EventFilterOptions
  ): Unsubscribable;
}

/**
 * Application event type
 */
type AppEvent<T = any> = BusEventType<BusEventWithPayload<T>>;

/**
 * Legacy emitter interface for backward compatibility
 */
interface LegacyEmitter {
  emit(name: string, ...args: any[]): void;
  on(name: string, handler: LegacyEventHandler<any>): void;
  off(name: string, handler?: LegacyEventHandler<any>): void;
}

/**
 * Legacy event handler type
 */
type LegacyEventHandler<T> = (payload?: T, ...args: any[]) => void;

/**
 * Unsubscribable interface for cleanup
 */
interface Unsubscribable {
  unsubscribe(): void;
}

Event Payload Types

/**
 * Data hover event payload
 */
interface DataHoverPayload {
  /** Hover point coordinates */
  point: {
    x: number;
    y: number;
  };
  /** Related data frame */
  data?: DataFrame;
  /** Row index */
  rowIndex?: number;
  /** Field index */
  columnIndex?: number;
}

/**
 * Dashboard loaded event payload
 */
interface DashboardLoadedEventPayload {
  /** Dashboard UID */
  dashboardId: string;
  /** Dashboard title */
  title: string;
  /** Loading time in milliseconds */
  loadTime?: number;
  /** Dashboard metadata */
  meta?: any;
}

Event Usage Patterns

Publisher-Subscriber Pattern

import { EventBusSrv, eventFactory } from "@grafana/data";

// Create global event bus
const globalEventBus = new EventBusSrv();

// Define custom event
interface NotificationPayload {
  message: string;
  type: 'success' | 'error' | 'warning' | 'info';
}

const NotificationEvent = eventFactory<NotificationPayload>('notification');

// Publisher component
class NotificationService {
  constructor(private eventBus: EventBus) {}
  
  showSuccess(message: string) {
    this.eventBus.publish(new NotificationEvent({
      message,
      type: 'success'
    }));
  }
  
  showError(message: string) {
    this.eventBus.publish(new NotificationEvent({
      message,
      type: 'error'
    }));
  }
}

// Subscriber component
class NotificationDisplay {
  constructor(private eventBus: EventBus) {
    this.eventBus.subscribe(NotificationEvent, this.handleNotification);
  }
  
  private handleNotification = (event: BusEventWithPayload<NotificationPayload>) => {
    const { message, type } = event.payload;
    this.displayNotification(message, type);
  };
  
  private displayNotification(message: string, type: string) {
    // Display logic here
    console.log(`[${type.toUpperCase()}] ${message}`);
  }
}

React Integration

import React, { useEffect, useState } from 'react';
import { EventBus, DataHoverEvent } from "@grafana/data";

interface Props {
  eventBus: EventBus;
}

function DataHoverIndicator({ eventBus }: Props) {
  const [hoverData, setHoverData] = useState<DataHoverPayload | null>(null);
  
  useEffect(() => {
    const subscription = eventBus.subscribe(DataHoverEvent, (event) => {
      setHoverData(event.payload);
    });
    
    return () => subscription.unsubscribe();
  }, [eventBus]);
  
  if (!hoverData) {
    return null;
  }
  
  return (
    <div>
      Hovering at: ({hoverData.point.x}, {hoverData.point.y})
    </div>
  );
}

Error Handling

import { EventBusSrv, eventFactory } from "@grafana/data";

interface ErrorPayload {
  error: Error;
  context?: string;
  recoverable?: boolean;
}

const ErrorEvent = eventFactory<ErrorPayload>('error');

class ErrorHandler {
  constructor(private eventBus: EventBus) {
    this.eventBus.subscribe(ErrorEvent, this.handleError);
  }
  
  private handleError = (event: BusEventWithPayload<ErrorPayload>) => {
    const { error, context, recoverable } = event.payload;
    
    console.error(`Error in ${context || 'unknown context'}:`, error);
    
    if (!recoverable) {
      // Handle critical errors
      this.handleCriticalError(error);
    } else {
      // Handle recoverable errors
      this.handleRecoverableError(error);
    }
  };
  
  private handleCriticalError(error: Error) {
    // Critical error handling logic
  }
  
  private handleRecoverableError(error: Error) {
    // Recoverable error handling logic
  }
}

// Usage
const errorHandler = new ErrorHandler(globalEventBus);

// Publish error
try {
  // Some operation
} catch (error) {
  globalEventBus.publish(new ErrorEvent({
    error,
    context: 'data processing',
    recoverable: true
  }));
}