🖼️
KASKO Frontend Documentation
  • README
  • Getting started
    • Setting up a new webapp
    • Webapp manifest
    • Building the flow
    • Defining layouts
    • Defining computed values
    • Defining dynamic paths
  • Core concepts
    • Forms
    • Validation
      • Using existing validators
      • Adding custom validators
    • Configuration
      • Price format
      • Date format
    • Performing API calls
    • Toggling visibility of components
    • Translations
    • Rules For Robust Application
  • Guides
    • Using logical statements (JsonLogic)
    • Adding discount codes
    • Developing Custom Plugins
    • Setting required requests
    • Adding save quote for later
    • Transforming request data
    • Manifest merging strategy
    • Knockout flow
    • Local pricing logic
    • Setting up dashboard policy profile
    • Handling offers
    • Repeater
  • Snippets
    • Reset input state
  • Useful resources
    • All component descriptions
    • JsonLogic core documentation
    • Kasko.js documentation
    • Example webapp
    • Example plugins (w/ various frameworks)
Powered by GitBook
On this page
  • Creating plugins
  • Example Plugin
  • Example Validation Plugin
  • Registering plugins to KASKO frontend framework
  • Interface
  • Examples
  • KASKO public service
  • Interface
  • Using plugin components in manifest
  • Example
  • Using plugin validators in manifest
  • Example
  • Component properties
  • Example
  • Component inheritance
  • Example
  1. Guides

Developing Custom Plugins

Last updated 1 year ago

Plugins are used to extend KASKO frontend with additional functionality. Most commonly they will be used to create custom components. For this are used. Any frontend framework can be used to create custom components as long as they are correctly transformed to web components. Most frameworks (including , , ) have wrapper modules that allow this functionality out-of-the-box. Note that this will have impact on performance. Hence it is recommended to use native WEB Components or .

Creating plugins

Demo plugins in the most popular frameworks can be viewed in the repository.

Each plugin consists of the following attributes:

  • plugin name

  • plugin WEB component/s

  • plugin

Example Plugin

class MyCustomComponent extends HTMLElement {
  constructTemplate() {
    return '<p>Hello World</p>';
  }

  connectedCallback() {
    this.innerHTML = this.constructTemplate();
  }
}

window.registerKaskoPlugin('my-plugin', {
  components: [
    {
      type: 'my-custom-component',
      component: MyCustomComponent,
    },
  ],
});

Example Validation Plugin

NOTE: Validators CAN be defined without components and vice versa.

function myCustomValidator({ value }) {
  if (value.indexOf('abc') > -1) {
    return {
      my_custom_validator: true,
    };
  }

  return null;
}

window.registerKaskoPlugin('my-plugin', {
  validators: [
    {
      name: 'my_custom_validator',
      validate: myCustomValidator,
    },
  ],
});

Registering plugins to KASKO frontend framework

In order to register plugins to the KASKO frontend framework, the registerKaskoPlugin function must be called.

Interface

registerPlugin(
  name: string,
  factory: ConfigFactory | ConfigObject,
): void
Argument
Description

name

Name of the plugin.

factory

Either a factory method or a plain object containing the plugin configuration.

Examples

// With plain object
window.registerKaskoPlugin('my-plugin', {
  components: [
    {
      type: 'my-custom-component',
      component: MyCustomComponent,
    }
  ],
});

// With factory method
window.registerKaskoPlugin('my-plugin', (kasko) => ({
  components: [
    {
      type: 'my-custom-component',
      component: myComponentFactory(kasko),
    }
  ],
}));

// Registering new validator with plain object
window.registerKaskoPlugin('my-plugin', {
  validators: [
    {
      name: 'my_custom_validator',
      validate: myCustomValidator,
    },
  ],
});

// Registering new validator with factory method
window.registerKaskoPlugin('my-plugin', (kasko) => ({
  validators: [
    {
      name: 'my_custom_validator',
      validate: myCustomValidator(kasko),
    },
  ],
}));

KASKO public service

When registering components or other attributes to KASKO frontend framework, a kasko public service is exposed via the factory method. This public service can be used to access translations, state slices, dispatch events and much more.

Interface

interface KaskoPublicService {
  /**
   * Retrieve a translation by the given translation key.
   */
  getTranslation(key: string, params: Object = {}): string;

  /**
   * Get the whole state or a slice of the state.
   */
  getState(slice?: string): any;

  /**
   * Get the current form state.
   */
  getFormState(): any;

  /**
   * Retrieve a referrer URL.
   */
  getReferrerUrl(): string;

  /**
   * Run a JsonLogic schema.
   *
   * @see: http://jsonlogic.com/
   */
  evaluateJsonLogic(schema: Object, args: Object = {}): any;

  /**
   * Creates Kasko compatible date out of string or other values.
   */
  createDate(value: Date | string | number): Date;

  /**
   * Dispatch an event.
   *
   * Available events:
   * set-form-input: set local form state; config: `{ first_name: 'John' }`
   * set-state-input: set global input state + trigger requests (if input valid); config: `{ first_name: 'John' }`
   * go-to-screen: go to a specific screen (using this should be a last-resort, preferably use `go-forward` event and manifest `next` definition); config: `{ screen: 'start' }`
   * go-to-field: go to a screen which contains a specified field; config: `{ field: 'first_name' }`
   * go-forward: validate + trigger requests + proceed to next screen
   * go-back: proceed to previous screen
   * set-flag: set a specific flag to a global state; config: { editing: true }
   * set-validation-errors: set error; config: `[{ field: 'first_name', path: '', code: 'required' }]`
   * remove-validation-errors: remove error;
   *   config: `[
   *     { field: 'first_name', path: '', code: 'required' },
   *     { field: 'first_name' },
   *   ]`
   * request-payment: triggers payment request
   * trigger-requests
   * validate-form: config: `(isValid) => {}`
   * submit-form: config: { input: { 'foo': 'bar' } }
   * save-quote: trigger lead request
   * request-item: config: load different item `{ item_id: 'item_id_123123', shouldOverwrite: false, shouldNavigate: false }`
   *   - pass `shouldOverwrite: false` to preserve current state upon item change, optional
   *   - pass `shouldNavigate: false` to skip initial_screen navigation upon item change, optional
   * add-upload: add a new media file to a global state; config: `{ file: File, field_name: 'upload', id: 8, callback: (error, payload) => {} }`
   * remove-upload: remove a media file with a specified id from a global state; config: `{ id: 8 }`
   * search-for-data: fetch data from Data Service;
   *   config: {
   *     field_name: 'test_field',
   *     data_field_name: 'data_field',
   *     source_field_name: 'source',
   *     filter: [{
   *       filter_param: 'filter_param',
   *     }],
   *     field_map: {
   *       map_param: 'map_param',
   *     },
   *     value: 'value',
   *     sort: 'desc',
   *     options_limit: 2000,
   *     callback: () => {},
   *   }
   */
  dispatchEvent(name: string, config?: any): void

  /**
   * Listen to an event.
   *
   * Available events:
   * field-validation: monitors field validity; example output: `false` (false = field is invalid)
   * validation-error: validation error occurred; example output: `{ required: true }`
   * validation-success: validation success occurred; output: `void`
   * state-changed: when state (or state slice) changed; example output: `123`
   * route-changed: when route has changed; example output: `{ previous: { path: '/start' }, current: { path: '/end' } }`
   * form-changed: when form has changed; example output: `{ previous: { duration: 'P1Y' }, current: { duration: 'P3Y' } }`
   * is-loading: when requests are made; example output: `true`;
   * is-loading-leads: when lead request is made; example output: `true`;
   * is-error-leads: when lead request fails; example output: `true`;
   * default-input-set: when default_values are set from manifest to input state;
   * item-loaded: when item data is loaded; output is item data
   */
  addEventListener(
    name: string,
    callback: Function,
    options?: Object,
  ): void;

  /**
   * Remove all event listeners with the same name & callback.
   *
   * The `callback` function passed here must have the same function reference
   * as with the one registered.
   *
   * @example
   * // OK:
   * kasko.addEventListener("state-changed", handleStateChange);
   * kasko.removeEventListener("state-changed", handleStateChange);
   *
   * // Wrong (exception):
   * kasko.addEventListener("state-changed", handleStateChange);
   * kasko.removeEventListener("state-changed", handleOtherStateChange);
   */
  removeEventListener(name: string, callback: Function): void;
}

Using plugin components in manifest

All plugin components registered to the KASKO frontend framework become available for use in the manifest. Reference them in the manifest the same way how any internal component would be used.

Example

{
  "components": [
    {
      "type": "title",
      "config": {
        "content_key": "hello.world"
      }
    },
    {
      "type": "my-custom-component"
    }
  ]
}

Using plugin validators in manifest

Example

{
  "type": "policy",
  "version": "v2",
  "field_definition": [
    {
      "path": "data",
      "name": "first_field",
      "validation": "string|only_emoji_chars_allowed"
    },
    {
      "path": "data",
      "name": "second_field",
      "validation": "required|string|at_least_n_chars_present_in:3,first_field"
    }
  ]
},

Component properties

Almost all components need to implement some dynamic functionality that can be configured via the manifest. This can be achieved with WEB Component attributes.

NOTE: Attributes CAN be stringified JSON.

Example

Component

class CustomTitleComponent extends HTMLElement {
  constructTemplate() {
    return `<h1>${this.getAttribute('content_key')}</h1>`;
  }

  connectedCallback() {
    this.innerHTML = this.constructTemplate();
  }
}

Usage in manifest:

{
  "components": [
    {
      "type": "custom-title",
      "config": {
        "content_key": "hello.world"
      }
    }
  ]
}

Component inheritance

Example

Component:

class CustomPanelComponent extends HTMLElement {
  constructTemplate() {
    return `
      <div class="panel">
        <div class="panel-header">
          <slot name="header"></slot>
        </div>

        <div class="panel-body">
          <slot></slot>
        </div>

        <div class="panel-footer">
          <slot name="footer"></slot>
        </div>
      </div>
    `;
  }

  connectedCallback() {
    this.innerHTML = this.constructTemplate();
  }
}

Usage in manifest:

{
  "components": [
    {
      "type": "custom-panel",
      "config": {
        "header_components": [
          { "type": "title" }
        ],
        "components": [
          { "type": "markdown" }
        ],
        "footer_components": [
          { "type": "footer" }
        ],
      }
    }
  ]
}

All plugin registered to the KASKO frontend framework become available for use in the manifest and field definition. Reference them in the manifest the same way how any internal validator would be used.

Sometimes custom components need to allow various content sections to be defined via manifest. For example, a panel component could have a header, body and footer section. This functionality can be achieved with .

WEB Components
Angular
React
Vue
LitElement
kasko-demo-plugins
validators
validators
slots