Custom Kibana Canvas Elements

In the intro to canvas webinar, Rashid khan mentions a plugin interface that allows users to add their own custom Canvas Elements. But I couldn't find any documentation for the same. Any pointers to how to develop new Canvas elements (say heatmaps)? Thank you

Good morning! I'm Clint, and I'm the Area Lead for Canvas.

We're currently working on all kinds of documentation... for plugins, it's something we want to get right the first time, and some of the inner workings of elements and functions have changed enough to make it difficult. That said, I can give you some information to get started.

Getting a custom element or function into Canvas starts with any other custom code in Kibana: a plugin. You can look to this documentation for details, if you're not already familiar.

So, from the Kibana directory:

  1. Run node scripts/generate_plugin.js my_canvas_plugin

This will create your plugin skeleton. You can pretty much answer no to all of the feature questions.

  1. Edit the plugin's index.js file.

There are a few dependencies your plugin will need. The most important is the interpreter which is what Kibana and Canvas use to register and evaluate expressions.

export default function (kibana) {
  return new kibana.Plugin({
    // Tell Kibana that this plugin needs canvas and the Kibana interpreter
    require: ['interpreter', 'canvas'],

    // The name of your plugin. Make this whatever you want.
    name: 'my_canvas_plugin',

    uiExports: {
      // Tell Kibana that the files in `/public` should be loaded into the
      // browser only when the user is in the Canvas app.
      canvas: ['plugins/my_canvas_plugin']
    },

    // Enable the plugin by default
    config(Joi) {
      return Joi.object({
        enabled: Joi.boolean().default(true),
      }).default();
    },
  });
}
  1. Create an entry point for your element and function. At the very least, you should have a function. But you can provide an element as well, which will allow your function to be added to the workpad from the Element selector. So, if we wanted to add a random number function and element, we would add and edit public/index.js:
/*global kbnInterpreter */

// Browser functions are Canvas functions which run in the browser, and can be used in
// expressions (such as `random | metric "Random Number"`)
const browserFunctions = [
  () => ({
    name: 'random',
    help: 'Make a random number between 1 and 100',
    args: {},
    fn() {
      return Math.floor(Math.random() * 100) + 1;
    }
  }),
];

// Elements show up in the Canvas elements menu and can be visually added to a canvas
const elements = [
  () => ({
    name: 'randomNumber',
    displayName: 'Random Number',
    help: 'A random number between 1 and 100',
    image: 'https://images.contentstack.io/v3/assets/bltefdd0b53724fa2ce/bltb59c89a07c05b937/5c583a6602ac90e80ba0ab8f/icon-white-circle-elastic-stack.svg',
    expression: 'random | metric "Random Number"',
  }),
];

// Register our elements and browserFunctions with the Canvas interpreter.
kbnInterpreter.register({
  elements,
  browserFunctions,
});

The magic happens in the kbnInterpreter call. This is what tells Kibana and Canvas the details of the function and element you've added, and how to evaluate it.

  1. (optional) Add any server functions to the plugin. The above is for a browser function. If you need access to the server for some reason, you'll register that in the plugin init:
export default function (kibana) {
  return new kibana.Plugin({
    // Tell Kibana that this plugin needs canvas and the Kibana interpreter
    require: ['interpreter', 'canvas'],

    // The name of your plugin. Make this whatever you want.
    name: 'my_canvas_plugin',

    uiExports: {
      // Tell Kibana that the files in `/public` should be loaded into the
      // browser only when the user is in the Canvas app.
      canvas: ['plugins/my_canvas_plugin']
    },

    // Enable the plugin by default
    config(Joi) {
      return Joi.object({
        enabled: Joi.boolean().default(true),
      }).default();
    },

    // Add this init function, which registers a new function with Canvas: `serverTime`
    init(server) {
      const { register } = server.plugins.interpreter;
      register({
        serverFunctions: [
          () => ({
            name: 'serverTime',
            help: 'Get the server time in milliseconds',
            args: {},
            fn() {
              return Date.now();
            },
          })
        ],
      });
    },
  });
}

For reference, we've added Typescript types to Canvas for elements and functions. This should help with any compatibility problems: https://github.com/elastic/kibana/tree/master/x-pack/legacy/plugins/canvas/types

You can write whatever code you want in your plugin: a Babel/Webpack build step, dependencies, libraries... you name it! The only thing you have to do is register the elements/functions as above, and they'll be available. Any errors for browser functions will be in the dev console, server functions in the Kibana logs. Good luck, and have fun!

2 Likes

Thank you @clintandrewhall for the prompt answer! Will check it out and get back in case of more questions.

@clintandrewhall Hello!
Can i add a new tool for the workpad? For example, on the workpad there is a color changer for the background, and i want to add new color changer for another element. It's a simple example.

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.