How to subscribe to Grafana application events

If you’re building a panel plugin, Grafana already provides the data you need to configure and render your panel. If not, you can retrieve the information you need from any of the runtime services, like BackendSrv and TemplateSrv. In some cases though, you want your plugin to react to changes outside of your plugin. For example, when the user hovers their cursor over data in another panel. In this post, you’ll learn how to make your plugin react to events in Grafana.

Note: If you’ve previously built plugins using Angular, note that you don’t need to subscribe to render events to update your visualization in React. React components automatically re-renders whenever any of its dependencies has changed.

Grafana uses an event bus to publish application events to notify different parts of Grafana when the user performs an action. Your plugin can react to these actions by subscribing to one or more events.

Events are identified by a unique string, and can have an optional payload. In the following example the ZoomOutEvent is identified by the zoom-out string and carries a number as a payload.

class ZoomOutEvent extends BusEventWithPayload<number> {
  static type = 'zoom-out';
}

Here are a few other events you can subscribe to:

You can access the event bus available from the panel props, and subscribe to events of a certain type using the getStream() method. The callback passed to the subscribe method will be called for every new event.

import React, { useEffect } from 'react';
import { RefreshEvent } from '@grafana/runtime';

// ...

interface Props extends PanelProps<MyOptions> {}

export const MyPanel: React.FC<Props> = ({ eventBus }) => {
  useEffect(() => {
    const subscriber = eventBus.getStream(RefreshEvent).subscribe(event => {
      console.log(`Received event: ${event.type}`);
    })

    return () => {
      subscriber.unsubscribe();
    }
  }, [eventBus]);

  return <div>Event bus example</div>;
}

Important: Remember to call unsubscribe() your subscriber to avoid memory leaks.

Note that while many event types are available but not yet exported, such as the PanelEditEnteredEvent, you can still subscribe to them by re-implementing the event type yourself:

class MyPanelEditEnteredEvent extends BusEventWithPayload<number> {
  static type = 'panel-edit-started';
}

While there’s no official documentation of the supported events, you can figure out the available events by searching for static type = '.*'; in the Grafana repository.

We’ll be improving the event bus and adding more events in the future. Let me know how you’re using the event bus, and any events you think would be useful to your plugin!

5 Likes

Hi @marcusolsson ,

Thanks for writing an official documentation about events in Grafana.
I’ve followed step by step your sample and implemented on my grafana plugin panel. I’m trying to get all DataHoverEvents and LegacyGraphHoverEvent from others to my custom panel.

The thing is DataHoverEvents and LegacyGraphHoverEvent events are not firing.
I’ve fired a DataHoverEvent manually after X seconds for testing I am subscribing properly to these events and my subscription works, so the only thing I can assume is the events are not firing from panels.

I can check how the RefreshEvent is working properly but not the events I need.
I cannot see any kind of errors or any weird logs. The tooltip from panels is working but the event is not firing.

Why is happening this?

I am using docker-compose with:

  • Grafana: grafana:9.2.15-ubuntu
  • MySQL: mysql:latest

My code:

export const VideoPanel: React.FC<Props> = ({
	options,
	data,
	eventBus,
  }) => {

	const [timestampState, setTimestampState] = useState(-1);
	const [valueState, setValueState] = useState(-1);
 
  
	useEffect(() => {
  
	  const subscriber = eventBus.getStream(RefreshEvent).subscribe(event => {
		console.log(`Received event: ${event.type}`);
	  });
  
	  const subscriber2 = eventBus.getStream(DataHoverEvent).subscribe(event => {
		console.log(`Received event: ${event.type}`);
	  })
  
	  const subscriber3 = eventBus.getStream(LegacyGraphHoverEvent).subscribe(event => {
		console.log(`Received event: ${event.type}`);
	  })
  
	  return () => {
		subscriber.unsubscribe();
		subscriber2.unsubscribe();
		subscriber3.unsubscribe();

	  };
	}, [eventBus]);
  
	return (
	  <div className="video-panel" style={{ overflow: 'auto' }}>
  		  <div>
			<div>DATA event</div>  
			<div>Timestamp: {timestampState}</div>
			<div>Value: {valueState}</div>
		  </div>
	  </div>
	);
  };

Update:

The DataHoverEvent is only fired when I hover the legend, which is useless… I have attached a video for clarifying
grafana

1 Like

@csantosm You can check the Time Series panel code if there are any other events that are fired on hover.

Alternatively, look at the Apache ECharts panel, which supports event handling and can update environment variables using locationService or fire an event using the eventBus.

Hey, I’m facing the same problem as you @csantosm… Has anyone found a solution yet?

@vandrommemi You must enable shared crosshair in the dashboard properties to Grafana publish events on hover.

Yes of course, I did. However, I experience the following:

When I test my panel plugin with the latest version of Grafana running as a service on Windows, I only get output when hovering over the legend and not when hovering over the panels.

When I test my panel plugin with grafana development on wsl I do get output from my DataHoverEvent, but rowIndex and columnIndex are null or no attribute of the payload. Only the DataFrame is populated. Point does contain a relative Y but also no “time” displayed on the x-axis…

this is my code:

  useEffect(() => {
    const subscriber = eventBus.getStream(DataHoverEvent).subscribe(event => {
      console.log(`Received event:`, event);
    })
    return () => { subscriber.unsubscribe(); }
  }, [eventBus]);

@vandrommemi This is how we did it at 6:44

Thanks for your reply @mikhailvolkov!

Unfortunately i still have an error:

Argument of type '{ type: string; }' is not assignable to parameter of type 'BusEventType<BusEvent>'.
  Type '{ type: string; }' provides no match for the signature 'new (...args: any[]): BusEvent'.

This is my current code:

  useEffect(() => {
    const subscription = eventBus.subscribe({ type: "data-hover" } , () => {
      console.log("Data hovered.");
    });
    return () => { subscription.unsubscribe(); }
  }, [eventBus]);

Do you have any idea how i can solve this?

Hi @marcusolsson , I would like to know if is it possible to pass data from one plugin panel to another plugin panel. I am building two panels and want to have inter-panel communication between them. For example, I have two Plugin A and B developed and I want to send some data from A to B on button press events. How can I implement this situation using eventBus?

@rajatghosh52 Panel A should do eventBus.publish(), and Panel B should subscribe to this event.

Marcus is not working with Grafana anymore.