H5P Guides

How to create an editor widget

In this guide, we will show you how to create and use a new widget for the H5P editor

In this guide, we are gonna create a visual color selector that will look like this:

We will use a color selector created by Brian Grinstead. It is found on Brian Grinstead's GitHub page. The editor widget itself is available at H5P's GitHub page.

To create and use a widget, the following steps are needed:

  1. Create the editor widget library
  2. Create the code skeleton
  3. Create code for creating the visual part of the widget
  4. Create code for doing validation and saving
  5. Configuring a content type to use the widget

Create the editor widget library

An editor widget is like any another H5P library, i.e. it is defined in a file called library.json:

{
  "title": "H5PEditor.ColorSelector",
  "majorVersion": 1,
  "minorVersion": 0,
  "patchVersion": 0,
  "runnable": 0,
  "machineName": "H5PEditor.ColorSelector",
  "author": "Joubel AS",
  "preloadedJs": [
    {
      "path": "scripts/spectrum.js"
    },
    {
      "path": "scripts/color-selector.js"
    }
  ],
  "preloadedCss": [
    {
      "path": "styles/spectrum.css"
    },
    {
      "path": "styles/color-selector.css"
    }
  ]
}

As you can see from the library definition above, we use the H5PEditor namespace for editor widgets.

The file structure of the library is as follows:

├── library.json
├── README.md
├── scripts
│   ├── color-selector.js
│   └── spectrum.js
└── styles
    ├── color-selector.css
    └── spectrum.css

The files named spectrum.css and spectrum.js are part of the generic color picker.

Creating skeleton code

The coding starts off by creating the skeleton for our widget class:

/**
 * Color selector widget module
 *
 * @param {H5P.jQuery} $
 */
H5PEditor.widgets.colorSelector = H5PEditor.ColorSelector = (function ($) {

  /**
   * Creates color selector.
   *
   * @class H5PEditor.ColorSelector
   *
   * @param {Object} parent
   * @param {Object} field
   * @param {string} params
   * @param {H5PEditor.SetParameters} setValue
   */
  function C(parent, field, params, setValue) {
    this.parent = parent;
    this.field = field;
    this.params = params;
    this.setValue = setValue;
  }
  
  /**
   * Append the field to the wrapper.
   *
   * @param {H5P.jQuery} $wrapper
   */
  C.prototype.appendTo = function ($wrapper) {};

  /**
   * Validate the current values.
   *
   * @returns {boolean}
   */
  C.prototype.validate = function () {};

  /**
   * Remove the current field
   */
  C.prototype.remove = function () {};

  return C;
})(H5P.jQuery);

Important to notice is the following line:

H5PEditor.widgets.colorSelector = H5PEditor.ColorSelector = (function ($) {

Here two things happen:

  1. The widget name is defined, and can after this be referenced from a semantics.json file (colorSelector)
  2. The machineName (H5PEditor.ColorSelector) is declared

Other than that we define the constructor, which only store references to the input parameters.

Create code for creating the visual part of the widget

The visual part of the widget must be created and added to the DOM in the appendTo function:

/**
   * Append the field to the wrapper.
   *
   * @param {H5P.jQuery} $wrapper
   */
  C.prototype.appendTo = function ($wrapper) {
    var self = this;

    self.$container = $('<div>', {
      'class': 'field text h5p-color-selector'
    });

    // Add header:
    $('<span>', {
      'class': 'h5peditor-label',
      html: self.field.label
    }).appendTo(self.$container);

    // Create input field
    self.$colorPicker = $('<input>', {
      'type': 'text',
      'class': 'h5p-color-picker'
    }).appendTo(self.$container);

    // Create color picker widget
    self.$colorPicker.spectrum({
      preferredFormat: 'hex',
      color: self.getColor(),
      change: function (color) {
        self.setColor(color);
      }
    });

    // Add description:
    $('<span>', {
      'class': 'h5peditor-field-description',
      html: self.field.description
    }).appendTo(self.$container)

    self.$container.appendTo($wrapper);
  };

Create code for doing validation and saving

We need to implement the validate function which verifies the data is valid:

/**
 * Validate the current values.
 * @returns {boolean}
 */
C.prototype.validate = function () {
  return (this.params.length === 6);
};

We also need to inform the generic H5P editor that data has changed:

C.prototype.setColor = function (color) {
  this.params = color.toHex();
  this.setValue(this.field, this.params);
};

The setColor function is invoked from the appendTo function, whenever the color picker fires a change event.

Configuring a content type to use the widget

To test the new widget type in a content type, we have to do two things:

  1. Add an editor dependency in the content type's library.json 
  2. Update semantics.json to use the new widget

Here is what it will look like in library.json:

"editorDependencies": [
  {
    "machineName": "H5PEditor.ColorSelector",
    "majorVersion": 1,
    "minorVersion": 0
  }
] 

Here is what it will look like in semantics.json:

{
  "name": "color",
  "type": "text",
  "label": "Color",
  "description": "The color of the element",
  "optional": true,
  "default": "000000",
  "widget": "colorSelector"
}

The important thing here is that we set colorSelector as the widget, which makes our widget being used instead of a standard input field.

A note about third-party APIs using jQuery

H5P comes with its own jQuery. The same instance has to be used also by third party JS APIs, or potentially a lot of strange things could happen. In this example, we have therefore added the following to the spectrum.js file:

(function(jQuery) {
  // The original spectrum.js content goes here
})(H5P.jQuery);